credits & pricing
the crawlbrulee api uses credit-based billing. every successful request is classified by the engine that delivered the result: http, browser, screenshot, or cache. the engine sets the base cost, the delivered proxy tier applies a multiplier, and generating requested screenshot slices adds one flat credit. failed requests and fully cached results cost 0.
credit rules
credit rules version 2.0.0 · effective August 29, 2026
version 2.0.0 supersedes version 1.0.0, effective July 15, 2026.
the sections in this group are the credit rules referenced by section 4 of the terms and conditions . they govern how requests consume credits. the plan and included credits presented for your purchase, together with the price and billing interval in the checkout or transaction confirmation from our merchant of record, govern your subscription.
credit cost for your requests
the formula is:
credits = engine base × proxy multiplier + screenshot slicing
| delivered engine | base credits | when it applies |
|---|---|---|
http | 1 | plain http fetches and /api/map |
browser | 3 | a browser delivered the result without a screenshot |
screenshot | 5 | a requested viewport or full-page screenshot was delivered |
cache | 0 | the base result was served from cache |
| delivered proxy | multiplier |
|---|---|
basic | ×1 |
advanced | ×5 |
auto is a request preference, not a billed tier. the response reports and bills the proxy that
actually delivered the result. generating a screenshot-slice variant through
actions_after adds a flat 1 credit after
the proxy multiplier, regardless of how many image tiles it contains. failures cost 0.
extraction options (markdown, raw_html, links, images), location emulation, and JavaScript
rendering do not add a separate fee. JavaScript rendering can change the delivered engine from
http to browser, and a delivered screenshot uses the screenshot base instead of the browser
base.
in rare cases, we return the requested page result without an optional screenshot. you’re charged
for the engine that actually delivered it — normally browser, not screenshot.
slice credits apply only when a requested slice variant is generated.
examples
| delivered result | cost |
|---|---|
http, basic proxy | 1 credit |
http, advanced proxy | 5 credits |
browser, basic proxy | 3 credits |
browser, advanced proxy | 15 credits |
screenshot, basic proxy | 5 credits |
screenshot, advanced proxy | 25 credits |
screenshot that generates a slice variant, basic proxy | 6 credits |
screenshot that generates a slice variant, advanced | 26 credits |
| cache hit that generates a new slice variant | 1 credit |
| cache hit with no newly produced slices | 0 credits |
| failed request | 0 credits |
how billing works
every request holds the most it could cost on its endpoint, so the ceiling is per endpoint.
on /api/scrape, an auto request without a screenshot needs 15 credits available to be accepted:
the browser engine on the advanced proxy is its worst case. an auto scrape that asks for a
screenshot needs 25. requesting screenshot slicing adds one flat credit to the reservation.
/api/map only ever uses the http base, so it reserves 1 on basic and 5 on auto or advanced.
if the balance cannot cover that ceiling, the request is rejected up front with a credit_limit
error even though the final cost could be lower.
after delivery we charge the actual cost and release the rest of the hold. an http result that
reserved 15 can cost as little as 1; an advanced browser result costs 15; a requested screenshot
that was not delivered is billed at the browser rate, and at the http rate when the http fallback
rescued the request. check
response_meta.usage on the response (see checking your usage) for the
charged engine, proxy, slices, and total.
you’re never charged for failed requests. cache hits have a 0-credit base; only newly produced screenshot slices can add credits to a cached response.
screenshot slices
slicing a full-page screenshot (via actions_after)
adds a flat 1 credit when the requested slice variant is generated, regardless of how
many image tiles it contains. the primary screenshot is covered by the screenshot engine base.
slices are computed at response time, so they can also be produced from a cached base result:
| scenario | credits |
|---|---|
| fresh screenshot, no slicing | screenshot base × proxy |
| fresh screenshot that generates a slice variant | screenshot base × proxy + 1 |
| cache hit that generates a new slice variant | 1 |
| cache hit where the requested slice variant is cached | 0 |
| cache hit, no slicing requested | 0 |
the billed slice variant is keyed on the slice request. asking for a slice height you have not used before charges for the new slices the first time; later cache hits are free when those slices are already cached.
plan tiers
the purchase flow shows the plan and included credits you are selecting; our payment processor shows the price and billing interval. your dashboard shows your current plan, available credits, and concurrency limit. credits reset each billing cycle — the same day of the month you were billed, at 00:00 utc — and unused base credits don’t roll over. every plan has the same api surface — no extraction features are gated behind higher tiers.
concurrency limits
your org has a maximum number of simultaneous active scrapes. the pool is shared between sync and async requests.
when you exceed it, you’ll get a 429:
{
"name": "usage_allocation_error",
"message": "Concurrency limit exceeded",
"details": {
"error_name": "usage_allocation_error",
"reason": "concurrency_limit",
"details": {
"current_concurrent": 5,
"max_concurrent": 5
}
}
}async jobs handle this gracefully — a submitted job waits in pending until a concurrency slot opens, then executes automatically.
checking your usage
per-request: response_meta.usage
this is the customer-facing indicator of what a single successful request cost — don’t try to
derive it yourself from the proxy tier and add-ons. the service usage ledger determines your
balance, subject to correction of manifest errors. successful /api/scrape, /api/map, and async
job results carry a response_meta.usage object. all of them include credits, engine, and
proxy; scrape and terminal async-scrape results also include screenshot_slices:
{
"response_meta": {
"usage": {
"credits": 1,
"engine": "http",
"proxy": "basic",
"screenshot_slices": 0
}
}
}| field | type | description |
|---|---|---|
credits | integer | credits actually charged for this request |
engine | string | delivered billing engine — http, browser, screenshot, or cache |
proxy | string | proxy tier that delivered the result — basic or advanced, never auto |
screenshot_slices | integer | billed slice-variant increment: 1 when newly produced, otherwise 0 |
/api/map omits screenshot_slices because map cannot generate screenshots. its engine is
http for fresh discovery or cache for a cached result.
aggregate: GET /api/usage
GET /api/usage returns a snapshot of the current billing cycle — credits, quota, and your concurrency limit. useful for pre-flight balance checks, dashboards, and alerting.
curl https://api.crawlbrulee.com/api/usage \
-H "Authorization: Bearer $CRAWLBRULEE_API_KEY"Response:
{
"total_credits": 20000,
"used_credits": 3425,
"available_credits": 16575,
"used_quota_percent": 17.1,
"max_concurrency": 5,
"usage_reset": "2026-07-17T00:00:00.000Z"
}| field | type | description |
|---|---|---|
total_credits | integer | credits available this billing cycle (plan base + purchased + gifted) |
used_credits | integer | credits spent so far this cycle; can land slightly above total_credits |
available_credits | integer | max(0, total_credits - used_credits); reads 0 once the allowance is spent |
used_quota_percent | number | percent of total_credits used, to 1 decimal; can read above 100 |
max_concurrency | integer | maximum simultaneous active scrapes for your org |
usage_reset | string | iso 8601 utc time when the cycle ends and used_credits resets |
the crawlbrulee sdk exposes this as client.usage().
credit exhaustion
when you run out of credits:
- requests fail with a
credit_limiterror (http 429), and keep failing until the cycle resets. used_creditscan read slightly higher thantotal_credits. work already in flight is allowed to finish rather than being cut off part-way, so the final tally can land just past your allowance. you are never billed for the difference.- to keep going before the reset, upgrade your plan or buy a credit pack.
{
"name": "usage_allocation_error",
"message": "Credit limit exceeded",
"details": {
"error_name": "usage_allocation_error",
"reason": "credit_limit",
"details": {
"current_usage": 750,
"current_reserved": 0,
"max_credits": 750
}
}
}optimizing credit usage
a few ways to keep costs down:
- use caching — the default 2-day freshness window makes fully cached repeat requests free. a newly produced screenshot-slice variant costs +1. don’t set
max_age: 0unless you need fresh data. - use the lightest engine that fits — http is 1 credit, browser is 3, and screenshot is 5 before the proxy multiplier. request JavaScript or screenshots only when the output needs them.
- start with
basic—advancedmultiplies the engine base by 5. use it when the higher success rate is worth the cost, or useautofor general-purpose scrapers. - normalize your urls — every non-tracking query param is part of the cache key, so dropping the ones that don’t change the page widens your cache hits.
- avoid
cleanup.exclude_selectorsandactions_beforewhen possible — they make requests ineligible for cache hits, so every call costs full price.cleanup.ads_and_popupsis free of that: it is part of the cache key, so it stays cacheable either way. - batch with async — async jobs wait for concurrency slots automatically instead of failing.
the fastest way to cut costs: check if you’re re-scraping urls within 2 days. if so, the cache is already saving you credits automatically.