Popunder — Cost Dashboard Every figure is USD. Measured means your account's own meters; modelled means a published rate times a volume you chose. The two never share a node.
Measured spend first. Everything else is one tab away.
🔌 Connection — Worker URL & admin token Prices your account's own meters for the selected window. URL and token are shared with the other admin pages, and the token is sent only to your Worker.
💰 What the account actually spent
measured meters ·/admin/usage
No measured data yet — open 🔌 Connection above, or press ⬇️ Load real usage in the top bar.
Cost breakdown — measured meters Every row is always shown, including every zero. The buttons beside the tabs dim or fold — they never delete a line.
How this window is priced Five ways to price one loaded snapshot. This picks the arithmetic, not the period — the Data window select in the top bar is the only control that refetches.
How this basis is computed
| Meter | units / month | included | status | rate | $ Cost is the units above the included tier, times the rate — inside its tier a meter reads zero. Base and tiers are account-wide: this is an allocation, not a bill. |
|---|---|---|---|---|---|
| — | $0.00 | ||||
| Variable meters — 8 lines above | — | ||||
| Base — Workers Paid, flat | — | ||||
| 💰 REAL COST | — | ||||
| If pro-rated | — | ||||
| + AE preview meters, if Cloudflare ever bills them | — | ||||
🔮 End of billing period, at the current rate
| Meter | so far | per day | at period end | % of tier | $ |
|---|---|---|---|---|---|
| Variable meters — 0 lines above | — | ||||
| Base — Workers Paid, flat | — | ||||
| 💰 PROJECTED BILL AT PERIOD END | — | ||||
Previous billing period
📊 Where the requests go, and how close each meter is to charging
measured ·/admin/usage + /admin/stats
No measured data yet — press ⬇️ Load real usage in the top bar.
📊 Requests → cache → delivery Requests, then cache hit or miss, then delivered or refused. A segmented bar is composition; a single fill on an empty track is a rate. Only a MISS costs a KV read — the Worker request is billed on a hit and on a miss alike. Needs the stats route.
L2 · edge cache
The three buckets are exclusive and sum to the billed total — that is why they get a composition bar
Free-tier headroom Fill is used, empty track is headroom, against the monthly included tier. Past full the fill clamps and the number beside it carries the overshoot.
Spend by meter Each meter's $ divided by the largest $ in the list. No track, so it can never be read as a rate. Every meter is listed, including the zeroes — but the only place a TOTAL is printed is 💰 Spend, where the lines that make it are itemised.
| Meter | units | free tier | $ / month · share of spend |
|---|
Preview meters (not billed)
🛡️ What the targeting gate would cost
left column measured · right column modelledThe comparison below needs a load — press ⬇️ Load real usage in the top bar. The modelled ladder underneath it works without one.
🛡️ EdgeGate — now vs gated Left is your real meters. Right is the same meters with EdgeGate armed: CPU only, using the two per-request constants in Model, Per visitor.
| Meter | units / month | free tier | cost / month | |||
|---|---|---|---|---|---|---|
| now 📡 measured | with EdgeGate ⚠ modelled | now 📡 | with EdgeGate ⚠ | Δ With EdgeGate minus without, per meter. Zero means the gate changes nothing there. The 'with' column is modelled — the gate is not deployed. | ||
| 💰 TOTAL | — | — | — | |||
| Hypothetical (if the AE preview meters — writes + SQL reads — get billed; free today) | — | — | — | |||
This Δ is CPU + R2 only; it cannot see the /geo request the gate removes nor the 300 s dedupe its no-store 403 destroys
🛡️ Modelled delta by volume ⚠ modelled Ported from the reference model, recomputed from this page's rates. It prices both columns off one request count, so it misses the /geo call the gate removes.
| Worker req / month | CPU-ms now | CPU-ms with EdgeGate | $ now | $ with EdgeGate | Δ |
|---|
The bill is identical at every volume below — all of it CPU
Ladder assumptions: 90% cache hit · 0.20 ms/req today · +0.01 ms warm · refills 2× miss at 0.30 ms
📐 What a given volume would cost
derived — no $ here is a billEvery cost in this tab is DERIVED; two of its assumptions can be replaced with measurements
Every COST in this tab is DERIVED: a published Cloudflare rate multiplied by a volume
you choose below. No $ figure here is a bill. The rates are the frozen constants in
the first card — read from cost-model.js, the single source of truth for the whole
panel; the volumes are the editable assumptions, each shown with its documented default.
Two of those assumptions — edge cache hit-rate and CPU-ms / request — can
be replaced with values measured in the 💰 Spend / 📊 Meters tabs; when you do that, the
field says MEASURED and names the endpoint that answered. Everything else
stays an assumption and says so. Where a measured value appears here for context it carries
a 📡 or 📊 prefix and names the endpoint that
answered. Anything without one of those prefixes is model output.
💵 Rates — fixed inputs The prices and included tiers every figure here multiplies by. Read from cost-model.js, the panel's single source of rates, so this list cannot drift.
- wrapper → 1 Worker req (KV only on a cache miss, 1 AE write)
- engine → R2: $0 Worker, free egress
- ad config → R2: $0 Worker (rotation mode only,
cfg.analyytics.com, edge-cached; single-URL mode = no cfg req) - /geo → 1 Worker req (rotation mode only),
{cc,vpn}cached 12 h inlocalStorageKey__cc_<CFG_ID>, so the cache is per publisher origin × wrapper id: /geo bills once per 12 h cache window, not once per pageview.
⚙️ Assumptions
/geo once per 12 h (live)Cost breakdown — USD / month Modelled, not measured: rates times a volume you chose above. Never a bill.
| Meter | units / month | included | status | rate | $ Cost is the units above the included tier, times the rate — inside its tier a meter reads zero. Base and tiers are account-wide: an allocation, not a bill. |
|---|---|---|---|---|---|
| 💰 MONTHLY COST | — | ||||
| Hypothetical (if the AE preview meters — writes + SQL reads — get billed; free today) | — | ||||
What this wrapper mode implies
👤 What one visitor costs Marginal rates — what the NEXT visitor costs, never an allocation of the flat base. Config, /geo and proxycheck exist only on rotation wrappers, so the headline is weighted.
What you owe today, and what is owed regardless of traffic
⚙️ Inputs — MEASURED vs ASSUMPTION
🔗 One cold visitor, event by event Wire order for a cold visitor's first pageview with the gate off. The early rows are the wrapper request and what it drags with it; the later ones are the /geo request.
| # | Event | Host | Meter | rate | qty | $ / visitor | $ / 1,000 |
|---|---|---|---|---|---|---|---|
| 💰 BILLED TODAY — one cold visitor | — | — | |||||
| AE preview (what it would cost — $0 owed) · proxycheck.io (external) | — | — | |||||
What the chain adds up to, and why rows 5-8 are weighted
🧭 The cases THE BASIS COLUMN IS LOAD-BEARING: these rows are not the same unit and must never be subtracted. Some are per pageview, some are per visitor.
| Case | basis | $ / unit | $ / 1,000 | $ / 1M | + AE / 1M (preview) | + external / 1M |
|---|
A refused visitor re-bills on EVERY pageview; a delivered one bills once per 300 s — and case S prices a whole visitor, not a pageview
📊 Cost per 1,000 visitors More pageviews means more billed wrapper requests, but /geo is paid once per visitor — so cost per thousand pageviews falls as cost per visitor climbs.
| pageviews / visitor | billed /w/ / visitor | Worker req / 1,000 visitors | $ / 1,000 visitors (today) | $ / 1,000 (if AE bills) | $ / 1,000 pageviews |
|---|
How the billed request count is derived
🛡️ EdgeGate — per OUT-OF-TARGET visitor The two axes the volume ladder cannot see: the gate removes the /geo call and its lookup, a saving; and its refusal is no-store, which destroys the browser dedupe, a cost.
| Meter | today — gate off | armed — enforce | Δ / 1M refused |
|---|---|---|---|
| 💰 NET — Cloudflare meters | break-even: — pageviews inside one 300 s window · — pageviews if all are >300 s apart | — | |
| External quota (proxycheck.io) | — | ||
Both break-evens, and which one you are in
💥 Abuse cost — /w/ id flood Cost of a flood of wrapper requests with non-existent ids, with and without the free Rate Limiting rule, which blocks before the Worker. Marginal rates, worst case.
| Meter (per month) | Without rate-limit | With /w/ rate-limit |
|---|---|---|
| Total / month (today) | — | — |
The free rate-limit rule blocks the 11th request in 10 s from one IP — it never sees a distributed flood
⚖️ Does the model match the meters?
both sides on the same footing — a full calendar month, preview meters excludedNeeds both sides: a volume in 📐 Model, and a successful load.
Needs both sides: a volume in the 📐 Model tab, and a successful load.
🛠️ Kill switches & reference
destructive actions, and how the money is wired🛑 Kill switches — stop billed traffic (Worker domain) · stop the engine (R2)
🛑 Kill switch Detaches the custom domain from the Worker, so no request reaches it and costs stop. Worker and KV stay intact; reattach in one click. Runs through the Control URL below.
🛑 Engine kill switch (R2) ROUTING only — the bucket and its files are never deleted. NOT a cost lever, because R2 is free here. Toggle only the engine host; the same bucket also serves config.
🔀 Full flow — wrapper · /geo · engine · pop
/w/<id>.js, /geo, /admin/*/cfg/<id>.js (rotation, $0, edge-cached)config.html builder (operator)Cache: wrapper 300s (WRAPPER_TTL), 404 60s (NEGATIVE_TTL), per data-center; …
Cache: wrapper 300s (WRAPPER_TTL), 404 60s (NEGATIVE_TTL), per data-center; re-publish live ≤300s (instant with purge). Cost key: the Worker runs on every request (even a cache hit) → 1 Worker req each; the cache only saves the KV read. The engine (R2) and the click-time resolver never touch the Worker → $0.
Ad rotation = $0: the config is loaded R2-direct from …
Ad rotation = $0: the config is loaded R2-direct from cfg.analyytics.com/cfg/<id>.js (a <script>, $0 Worker, edge-cached) and the rotating url is picked client-side ($0) — the only per-pageview metered item stays the 1 wrapper Worker request. Config sits on a different domain than the engine (decoupled from the blockable engine domain). The cfg request only happens in rotation mode (AD TARGET = a cfg.analyytics.com/….js url); in single-URL mode (AD TARGET = a plain landing url) there is no cfg request at all — the pop opens that url directly. $0 either way.
Note: /geo and the click-time rotation resolver are live, not planned — the …
Note: /geo and the click-time rotation resolver are live, not planned — the Worker answers /geo with {cc,vpn} (country from request.cf.country, VPN flag from a proxycheck.io subrequest, fail-closed) and the wrapper calls it unconditionally once cfg loads. They exist only on a wrapper deployed in rotation mode; a single-URL wrapper has CFG_ID baked to '', so its resolver returns on the first line and it makes neither the cfg GET nor the /geo request. That is the Wrapper mode assumption in 📐 Model, and it is what the model's second billed Worker request is priced on.
🔧 Enabling Real usage & Kill switch — one-time: redeploy with CF_ACCOUNT_ID + MGMT_API_TOKEN
- Redeploy the Worker (it has the routes
/admin/usage,/admin/domain,/admin/r2domain) withCF_ACCOUNT_ID+ the secretMGMT_API_TOKEN. - MGMT_API_TOKEN = a User API Token with Account: Analytics Read + Workers Scripts Edit + Workers R2 Storage Edit + Zone: Workers Routes Edit + DNS Edit + Zone Read (+ Cache Purge). Put it in
deploy.env→ rundeploy.command. - The browser only sends
ADMIN_TOKEN(from the 🔌 Connection bar) to the Worker; the powerful token stays a Worker secret → no CORS, no secret in the browser.