Pricing a browser API is an exercise in not trusting your gut, because the gut does not know what a capture costs. Neither did we, until we measured it.
The famous-50 run
In July we captured fifty real-world pages in production conditions and read the bill: a mean of 19.1 seconds per capture at 2048 MB, about $0.000636 of Lambda per invocation. The renderer split that shipped days later made it two invocations per capture — the orchestrator billed idle wall-clock while the renderer worked — so the real figure landed near $0.0013 per capture, plus the fixed monthly cost of the renderer's NAT gateway.
Against those numbers, the top of our old price ladder was underwater. Enterprise was 1,000,000 requests at $99 with unlimited retention — at full utilisation, roughly -$28.37 per subscriber, before NAT. A plan sold at a loss is a plan that dies at the worst moment: when it gets popular.
What changed
- Enterprise rebalanced to 100,000 requests at $99 with 365-day retention. A real ceiling, not forever.
- Metered overage on every paid plan: usage beyond quota bills at $0.002 per extract, up to 2x the included quota, and then the account is refused until the owner deliberately goes further.
- The free tier hard-stops at its 100 extracts. A free plan must never accrue a charge the account did not agree to pay.
- Enterprise is self-serve. It previously required a sales conversation that had no procedure behind it; it now checks out like any other plan.
Why a credit is not a request
A capture is not one thing with one cost. Rendering a page and returning a JPEG is cheap; rendering it, walking the accessibility tree, running a cleanup pass and capturing a frame per paint is several times the work. Charging both the same price means one of them is subsidising the other, and the subsidy runs the wrong way — the light request pays for the heavy one.
So requests are priced in credits, with a base cost and a surcharge per expensive option. That has two consequences worth stating.
The first is that you can see the price before you spend it. GET /credits returns the current surcharge table and consumes nothing, and the playground prints the estimate for the request on screen as you build it. The estimate comes from the same function that prices the invoice — not a second copy of the table that can drift from it, which is a mistake we made once and now have a test against.
The second is that cheap requests stay cheap. If all you need is Markdown, you are not paying for the diagnostics you did not ask for.
What we deliberately did not do
Per-seat pricing. This is an API. The number of people at your company who have the key is not a cost we incur, and charging for it would be inventing a meter to bill against.
A cheaper tier with a slower queue. Deliberately degrading a capture to protect a price point makes the product worse in a way the customer can feel, in exchange for a number on a pricing page. If a plan cannot be served properly, the honest move is not to sell it.
Unlimited anything. The last plan that said "unlimited" is the one that started this whole exercise. Every tier now has a stated ceiling, including the retention window, because a limit you can read is better than a limit you discover.
Why the 2x ceiling is the honest part
Overage exists so a customer who wants to spend more can, and so the heaviest accounts stop being the least profitable. It is not unbounded: a runaway script must not be able to bill an account without limit. The hard stop at 2x quota forces a deliberate decision — and keeps the product from turning a coding error into an invoice.
The caveat we are keeping public
Enterprise is still the thinnest-margin tier, and the per-capture math above excludes the renderer NAT gateway, which puts it back under water on its own at full utilisation. That is defensible today because metered overage captures the heavy users and typical utilisation is far below quota. It is not a promise — the next repricing re-measures, counts both invocations, includes NAT, and decides again from numbers, not intuition.
The current plan ladder is on the pricing page, and every number there comes from the same plan definition the dashboard and the docs read.