Errors
Every error the Grabber API returns, with its HTTP status, its stable detail code and what to do next. Errors are never charged.
Last updated
Every error has the shape { status, message, detail }, plus a hint when
there is something the caller can do about it. detail is a stable code to
branch on; message is the HTTP reason phrase. Nothing is ever cached:
every answer has Cache-Control: no-store.
You pay only for a page that was loaded, so no error is ever charged. A page whose own server answered 404 or 500 is not an error: it was loaded, so the answer is 200 and it is charged. Its status is in data.fetch.httpStatusCode.
Error codes
| Status | detail | Meaning |
|---|---|---|
400 | invalid_query | The query is wrong, and hint says how. |
400 | invalid_url | The url is not http or https, has a username or password, or is too long. |
400 | blocked_url | The url points at a host that cannot be fetched. |
401 | invalid_api_key | The key is missing, malformed, unknown, or deleted. |
402 | insufficient_credits | The balance is below the price of this request. Nothing was charged and nothing was fetched. |
404 | not_found | The path is not /fetch. |
405 | method_not_allowed | The method is not GET. |
429 | rate_limited | More than 60 requests in this minute for this key. Wait Retry-After seconds. |
500 | internal_error | Something failed on Grabber's side. Nothing was charged. Try again; if it keeps happening, it is not caused by your request. |
502 | fetch_failed | The page could not be loaded. |
502 | blocked_url | The page redirected to a host that cannot be fetched. |
502 | body_too_large | The page is over 8 MiB. |
502 | format_unavailable | unwrap=true, and none of the requested formats can be produced for this content. |
502 | upstream_unavailable | Grabber could not get an answer from its fetching service. |
Examples
{
"status": 400,
"message": "Bad Request",
"detail": "invalid_query",
"hint": "Give exactly one address to fetch, for example ?url=https://example.com/"
}What to do
- 400: the request itself is wrong, so sending it again gives the same answer. Read
hint, fix the query or the URL, then retry. - 401: check that the key is sent as
Authorization: Bearer gsh_…and still exists in the dashboard. - 402: add credit at the address in
hint, or turn on auto top-up so the balance never runs out. - 429: wait the number of seconds in
Retry-After, then retry. The limit is 60 requests per minute per key. - 500 and 502
upstream_unavailable: the problem is on Grabber's side. Retry with a growing delay, such as 1, 2, then 4 seconds. - 502
fetch_failed: the page did not load. Check the URL in a browser; if it opens there, try again later. - 502
format_unavailable: withunwrap=true, add a format this content can produce, such asraw, after the one you want.
Response headers
| Header | Meaning |
|---|---|
X-Grabber-Cost | What this request cost, in US dollars: 0.001, 0.005, or 0 when nothing was charged. |
X-Grabber-Balance | What is left of your credit after this request, in US dollars. |
Cache-Control | Always no-store. Answers are never cached. |
WWW-Authenticate | Always Bearer. |
Allow | Always GET. |
Retry-After | Seconds until the next minute begins. |

