Credits & rate limits
Your plan includes a monthly allowance of credits. Every successful call spends some, according to what that call costs us to run — so a plan stretches a long way on cheap operations and less far on expensive ones.
What each call costs
One credit is one page fetch. Everything else is priced relative to that. This table is generated from the same source the API meters with, so it is never out of date:
| Endpoint | Credits | Calls a Free plan buys |
|---|---|---|
| /console | 1 | 1,000 |
| /html | 1 | 1,000 |
| /markdown | 1 | 1,000 |
| /screenshot | 1 | 1,000 |
| /lighthouse | 2 | 500 |
| /meta | 5 | 200 |
| /keywords | 15 | 66 |
| /summarize | 17 | 58 |
One option sits outside this table because it is priced per page fetch rather than per call: residential exits, covered below.
The split is not arbitrary. /html, /screenshot and /console all come from one visit to the page in a real headless Chrome and cost us the same; /lighthouse runs a full audit that takes minutes of CPU in a limited lane, so it is worth two. /markdown converts the page we have already rendered, in the browser and without a model, so it costs a page fetch and nothing more. The three AI endpoints send the page to a language model, and what they pay depends on how much of it the model has to read and write back: /meta reads a page's head and answers in a few hundred tokens, while /summarize reads the whole article and writes several paragraphs back.
Fetching from a residential address
One option is priced on its own, because what it buys is not work on the page but the connection the page arrives over. Residential exits fetch a page from a home broadband address instead of a datacentre one, for the sites that answer the two differently. They add 25 credits per page fetch — once per request, not once per operation, so a /scrape pays for one exit however many results it takes off the visit.
A worked example
Say a month's traffic is 500 /html calls, 100 /meta lookups and 25 /summarize calls, and a third of the requests repeat a URL you've already fetched:
Raw calls 500 /html + 100 /meta + 25 /summarize
Cache hits (33%) −165 /html −33 /meta −8 /summarize → free
Billable 335 /html 67 /meta 17 /summarize
335 × 1 = 335 credits
67 × 5 = 335 credits
17 × 17 = 289 credits
---
959 credits → fits the Free plan (1,000)Allowance per plan
These are the monthly allowances each plan includes. They are read from the same source the API enforces, so this table is never out of date.
| Plan | Credits / month | In parallel | Page fetches | /summarize calls |
|---|---|---|---|---|
| Free | 1,000 | 1 | 1,000 | 58 |
| Unlimited | Unlimited | Unlimited | Unlimited | Unlimited |
The Unlimited plan removes the allowance entirely and is arranged by email — write to us and we will size it to your workload.
Cache hits are free
Failed requests are free too
You are only charged for work that produced a result. However an analysis fails — a DNS failure, a refused connection, a timeout, a bad certificate, a page that 404s, or a site that puts a bot check in front of it we cannot clear — the call spends nothing. That includes the residential surcharge on a fetch that came back with nothing to sell you, and it includes a request we could not find a residential exit for at all.
Reading your balance from a response
You don't have to wait for a 429 to find out where you stand. Every response says what it cost and what is left:
- Name
X-Quota-Cost- Type
- integer
- Description
- What this request spent. Zero on a cache hit or a failed analysis, and on a
/scrapethe sum over the operations that actually did work — plus any residential exit it used. On a429it is what the request would have cost — the figure that didn't fit.
- Name
X-Quota-Limit- Type
- integer
- Description
- Your monthly credit allowance, or
unlimited.
- Name
X-Quota-Remaining- Type
- integer
- Description
- What is left after this request. The count is taken after your request, so it's what you have now.
- Name
X-Quota-Reset- Type
- timestamp
- Description
- When the allowance rolls over, as an ISO 8601 timestamp.
When you run out
If a request would cost more than you have left, URLpipe returns 429 Too Many Requests with a JSON body describing exactly where you stand. Nothing is executed and nothing is spent — a /scrape that can't afford all of its operations runs none of them, rather than silently dropping some.
{
"error": "quota_exceeded",
"message": "Your Free plan includes 1000 credits per month. You've used 990 and this request needs 17. Quota resets on 2026-10-01 (UTC).",
"limit": 1000,
"used": 990,
"needed": 17,
"resets_at": "2026-10-01T00:00:00Z"
}- Name
error- Type
- string
- Description
- Always
quota_exceededfor this response.
- Name
limit- Type
- integer
- Description
- Your monthly credit allowance.
- Name
used- Type
- integer
- Description
- Credits you've spent this period.
- Name
needed- Type
- integer
- Description
- What the refused request would have cost. Worth reading: a request for a cheaper operation may still fit.
- Name
resets_at- Type
- timestamp
- Description
- When the allowance resets — the 1st of next month, UTC — as an ISO 8601 timestamp.
Handling it gracefully
- Read
resets_atand back off until then rather than retrying immediately. - Widen
max_ageso more requests are served from cache for free. - Reach for a cheaper endpoint where it will do. If you only need a page's title and description,
/metacosts less than a third of/summarize. - Drop
residentialfrom the calls that do not need it. Most pages come back complete on the standard network, and the exit is the dearest thing on the rate card. - Your organization screen in the dashboard lists the operations spending the most this month — residential fetches included, on a line of their own — so you can see where the allowance is going.
Dormant free organizations
A free organization that nobody signs in to and that makes no API request for six months is treated as dormant. We email its admins and show a notice in the app; 30 days later the organization and its request history are deleted.
Anything counts as activity — one sign-in, one request. So a project whose only job is a nightly cron against /markdown never goes dormant, and neither does an account somebody logs into now and then. If a notice does arrive, using the organization cancels it, as does upgrading it or the button in your organization settings. Paid plans are never affected. The full policy is in the terms.
Parallel requests
Separately from how many credits a month buys, each plan sets how many requests you can run in parallel. Credits are how much work you can do in a month; this is how fast you get through it.
| Plan | Parallel requests | What that suits |
|---|---|---|
| Free | 1 | Cron jobs, a page per visitor, and steady background work. |
| Unlimited | Unlimited | Large crawls and pipelines — you set the width. |
A crawl of 5,000 pages costs the same credits however fast you run it — running several in parallel instead of one at a time is the difference between minutes and hours. Every response carries X-Concurrency-Limit, so a worker pool can size itself from the plan rather than by trial and error.
A request counts from the moment we accept it until it finishes, which for an async request includes the time it spends queued waiting for us to start it. A /scrape is one request however many operations it runs, because they come from a single page visit. And a cache hit occupies no slot at all — it does no work — so widening max_age buys you concurrency as well as lowering your bill.
Go over and the request is refused with 429 Too Many Requests and error: "concurrency_limit". Nothing is spent and nothing is queued — retry as soon as one of your own requests finishes.
{
"error": "concurrency_limit",
"message": "Your Free plan runs 1 request at a time, and 1 is already running. Retry when one finishes, or move to a plan that runs more in parallel. Nothing was spent on this request.",
"limit": 1,
"running": 1
}Retry-After on this response, deliberately: the answer is "when one of yours finishes", not a fixed number of seconds. A client that tracks its own in-flight requests always knows sooner than a header could tell it.Your limit is about your own work. We separately cap how many requests run against any one target site at a time, across all customers together, so URLpipe never hammers somebody else's server — and when a site starts struggling, we slow down further until it recovers. Those requests queue rather than being refused, and time a request spends waiting on a site we are pacing does not count against your parallel limit. You didn't cause that wait, so you don't pay for it.
Rate limits
Separately from your monthly allowance, every request is rate limited per project (keyed by your API key) so a runaway script can't flood the service. Two ceilings apply at once:
- Up to
60requests per minute. - Up to
15requests per 10 seconds (a short-burst ceiling).
Exceed either and URLpipe returns 429 Too Many Requests with a Retry-After header giving the seconds to wait. This response is distinct from the credit one above — its error is rate_limited, so you can tell "slow down" apart from "you've used your allowance".
{
"error": "rate_limited",
"message": "Too many requests. Slow down and retry after 12 seconds.",
"retry_after": 12
}So three different things can answer 429, and the error field is what tells them apart: rate_limited means you are sending too fast, concurrency_limit means too many of yours are already running, and quota_exceeded means the month's credits are gone. Each one tells you what to do next: slow down, wait for one of your own requests to finish, or wait for the 1st.