Every request goes to https://api.parse.bot. This page is the definitive reference for what each status code means — especially for executing endpoints (POST /scraper/{scraper_id}/{endpoint_name}), where Parse wraps the outcome in a structured envelope so you can tell whose fault a failure is: yours, the target site’s, or the scraper’s.
The execution envelope
A successful call returns the endpoint’s own JSON (the shape described by its return_schema) with HTTP 200:
{ "title": "A Light in the Attic", "price": 51.77, "rating": 3 }
A failed call returns a non-2xx status with an error envelope. The HTTP code tells you the category of failure, and the body carries a machine-readable status plus detail:
{
"error": {
"status": "upstream_error",
"upstream_status_code": 404,
"message": "The target site returned HTTP 404. See `snippet` for the upstream response body.",
"snippet": "<html>Product not found</html>",
"url": "https://example.com/products/invalid-sku"
},
"status_code": 502
}
The status field is the most useful signal — it maps 1:1 to the HTTP code:
| HTTP | status | What it means | What to do |
|---|
200 | success | The endpoint ran and returned data | Use the body |
422 | stale_input | Your input was invalid, or the resource is gone upstream | Fix the input and retry |
429 | — | Rate limit hit (burst or daily) | Honor Retry-After and back off |
502 | upstream_error | The target site returned a non-2xx response | Inspect upstream_status_code + snippet; do not blindly retry |
503 | blocked | Anti-bot blocked every proxy we tried | Retry later only if retry_after is present |
500 | error | The scraper crashed or hit a bug | Request a revision of the API |
400 | — | Malformed request (bad header, missing session creds) | Fix the request |
401 | — | Missing/invalid/expired API key | Check your key |
404 | — | Scraper (or pinned version) doesn’t exist or isn’t yours | Check the scraper_id |
502 is not a Parse outage and should not be auto-retried. It means the target site answered the scraper with a non-2xx (e.g. a 404 for an ID that doesn’t exist, a 401 for bad upstream credentials, a transient site 500). Read upstream_status_code and snippet to see what the site actually said before deciding whether to retry.
The scraper rejected your input before or during the upstream call — wrong type, missing required field, or a resource the site says is gone.
{
"error": {
"status": "stale_input",
"kind": "input_format_invalid",
"message": "trip_type must be 'one_way' or 'round_trip'",
"upstream_status_code": 400,
"url": "https://example.com/api/search"
},
"status_code": 422
}
kind: "input_format_invalid" — a parameter is malformed. Fix it against the endpoint’s input_params.
kind: "input_not_found" — the specific thing you named (a job ID, a product SKU) was confirmed gone by the site (often a 410). Not retryable with the same input.
502 — upstream_error (the target site failed)
The scraper reached the site, but the site returned a non-2xx. The body tells you exactly what came back:
{
"error": {
"status": "upstream_error",
"upstream_status_code": 503,
"message": "The target site returned HTTP 503. See `snippet` for the upstream response body.",
"snippet": "Service Unavailable"
},
"status_code": 502
}
Decide based on upstream_status_code: a 404/410 usually means your input pointed at nothing (don’t retry); a 5xx from the site may be transient (retry with backoff); a 401/403 may mean the API’s stored credentials need attention (revise the API).
503 — blocked (anti-bot stopped us)
Parse rotated through proxies and every attempt hit the site’s bot protection.
{
"error": {
"status": "blocked",
"block_type": "datadome",
"vendor": "datadome",
"kind": "antibot_solvable",
"attempts": 3,
"retry_after": 60,
"message": "Service temporarily unavailable - site protection blocking all proxies"
},
"status_code": 503
}
retry_after present (and a Retry-After header) → transient; retry after that many seconds.
retry_after: null (and no Retry-After header) → kind is antibot_unsolvable or ip_burned; retrying soon won’t help. If it persists, the API may need a revision.
500 — error (the scraper bugged out)
The scraper crashed or detected an internal fault. This is the API’s problem, not yours.
{
"error": {
"status": "error",
"kind": "scraper_bug",
"message": "The scraper crashed during execution. This could be due to bad input, broken scraper code, or a website update."
},
"status_code": 500
}
Fix: revise the API — POST /dispatch/tasks/{task_id}/revise with a description (e.g. “the get_paper endpoint 500s on valid IDs”). If it’s a marketplace/canonical API, the revision improves it for everyone.
429 — rate limited
{
"error": {
"error": "Rate limit exceeded",
"message": "Too many requests in a short burst. Retry in 5s.",
"limit_type": "burst",
"retry_after": 5
},
"status_code": 429
}
limit_type: "burst" — you exceeded the short-term token bucket. retry_after is seconds until the next token.
limit_type: "daily" — you hit your daily cap. retry_after is seconds until UTC midnight.
Every execution response carries rate-limit headers so you can pace yourself before getting a 429:
| Header | Meaning |
|---|
X-RateLimit-Remaining | Tokens left in the burst bucket |
X-RateLimit-Reset | Unix time when the next token is available |
X-RateLimit-Daily-Remaining | Requests left today (tiers with a daily cap) |
Retry-After | Seconds to wait (sent on 429, and on retryable 503) |
X-Credits-Charged / X-Credits-Remaining | Credits this call cost / your balance |
Authenticated login endpoints have a stricter cap (3 attempts/hour per API) — see Authenticated APIs.
400 / 401 / 404 — request problems
400 — a malformed request: an invalid API-Snapshot-Version header, or session_id/encryption_key sent only one of the pair. Body: {"error": "<reason>", "status_code": 400}.
401 — {"error": "Missing X-API-Key header" | "Invalid API key" | "API key expired", "status_code": 401}. Send a valid pmx_… key in X-API-Key (header name is case-insensitive; the value is not). See Authentication.
404 — {"error": "Scraper with ID '…' not found", "status_code": 404}. The scraper or pinned version doesn’t exist, or isn’t yours — Parse returns 404 rather than reveal another account’s resource. Use the scraper_id from your completed task’s generated_api.
A bare JSON {"detail":"Not Found"} (note: detail, not error) means the path doesn’t exist, not the scraper. Check you’re calling POST /scraper/{scraper_id}/{endpoint_name} exactly, on https://api.parse.bot.
Build failures (task status: "failed")
A dispatch/revision task can finish failed rather than completed; the task’s error field says why. Retry with a clearer task description, revise it, or — if may_require_auth was true — build it as an Authenticated API from the dashboard.
Still stuck?
- Confirm the base URL:
https://api.parse.bot.
- Verify your key with a cheap call:
GET /dispatch/tasks.
- Reach out to support from the dashboard — click the chat icon in the bottom-right corner to start a conversation with us. Grab the
X-Railway-Request-Id response header from the failing call and include it; it lets us trace your exact request.