Errors and limits
What each failure code means, which are worth retrying, and why a refusal from a source is reported as a refusal rather than as an empty result.
Shape
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Invalid request body",
"request_id": "req_…",
"retryable": false,
"details": { }
}
}retryable is the field to branch on. Nothing else in the error is a stable contract.
Codes
| Code | Meaning | Retry |
|---|---|---|
VALIDATION_FAILED | The request body does not match the schema | No — fix the request |
UNAUTHORIZED | Missing or unknown key | No |
PLAN_LIMIT_REACHED | A ceiling inside your plan (403). The monthly allowance lands here too | No — change the plan, or wait for the first of the month (Retry-After carries the seconds) |
NO_CAPACITY | No browser worker free | Yes, with backoff |
NO_IP_AVAILABLE | No healthy address for the country requested | Yes — often means a region is still warming up |
INVALID_URL | Blocked by the SSRF gates, or malformed | No |
BROWSER_FAILED | The page did not load | Yes, once |
PAGE_TOO_LARGE | The page's HTML or the amount transferred exceeded the limit (HTML 2 MiB by default, 16 MiB at most) (422) | No |
RATE_LIMITED | Requests per minute (429) | Yes — wait the Retry-After seconds |
ROBOTS_DISALLOWED | robots.txt disallows the path (403) | No |
DOMAIN_RATE_LIMITED | Pacing toward one domain (429), including crawl-delay | Yes — wait the Retry-After seconds |
Refusals are not empty results
When a source answers 403 or 429, that is reported as an HTTP status on the response — not as a successful extraction with nothing in it. The distinction matters more than it looks: an empty result reads as "there is nothing there", which sends you to check your selectors, when the actual message was "slow down".
We record refusals with their date. When the page itself returns 403, 429 or 503, a monitored source is read again on the next cycle after one or two such refusals; after three in a row it is stopped, and the morning digest says so once. After a 429 we do not request pages from that host, from anywhere, for the time in Retry-After (one hour if none is given, kept between 60 seconds and 24 hours).
Politeness is enforced, not advisory
Requests toward one domain are paced regardless of how fast you send them. robots.txt is fetched and honoured, including crawl-delay. A disallowed path returns ROBOTS_DISALLOWED rather than being fetched anyway.
There is no setting that turns this off, and support cannot turn it off for you. A source that refuses us once is a source nobody gets back, and the loss falls on every customer who needed it.
Last updated