Skip to main content

Errors & Status Codes

When a request fails, success is false and an error field describes what happened. The HTTP status code tells you whether the problem is on your side, our side, or the target's side.

{
"success": false,
"error": "Target returned 403 after solver attempts",
"metadata": { "elapsed_time": 12.4, "solver_used": true }
}

Status codes

CodeMeaningWhat to do
200SuccessRead data. Still check data.status_code.
400Bad requestA parameter is missing or invalid. Fix the body.
401UnauthorizedAPI key missing or invalid. Check the X-API-Key header.
402Payment requiredBalance exhausted, monthly quota used up, or direct proxy locked on trial. Check the code field.
429Too many requestsYou hit a rate limit. Back off and retry.
500Server errorTransient on our side. Retry with backoff.
502Unlock failedWe could not unlock the target. Try js_rendering or a different proxy country.
503Service unavailableMaintenance or overload. Retry shortly.

Structured error codes

Some 4xx responses include a machine-readable code so you can branch in code without string-matching the message:

{
"success": false,
"error": "Concurrent request limit reached for your plan. Retry shortly.",
"code": "CONCURRENCY_LIMIT"
}
codeHTTPMeaning
CONCURRENCY_LIMIT429Too many in-flight requests for your plan. A Retry-After header is included. Lower concurrency or upgrade. See Rate limits.
MONTHLY_QUOTA_EXCEEDED402Your subscription's monthly included requests are used up. Upgrade or wait for the reset.
PROXY_LOCKED402Direct proxy access (/v1/proxy/fetch or proxy credential rotation) requires PAYG or an active plan. Trial users should use Web Unlocker or BaaS, or activate PAYG ($5 minimum top-up).

Two layers of status

There are two status codes to watch:

  1. The HTTP status of the OmniScrape API call (above). 200 means we processed your request.
  2. The target site's status, in data.status_code. You can get HTTP 200 from us with data.status_code: 404 because the page itself returned 404.
r = resp.json()
if not r["success"]:
handle_api_error(r["error"])
elif r["data"]["status_code"] >= 400:
handle_target_error(r["data"]["status_code"])
else:
use(r["data"]["content"])
  • Retry 429, 500, 502, 503 with exponential backoff (e.g. 1s, 2s, 4s, 8s).
  • Do not retry 400, 401, 402 — the request will keep failing until you fix it.
  • For stubborn 502s, escalate the mode to js_rendering and switch proxy country.
import time, requests

def scrape_with_retry(payload, headers, attempts=4):
for i in range(attempts):
r = requests.post(
"https://api.omniscrape.io/v1/scrape",
headers=headers, json=payload, timeout=120,
)
if r.status_code in (429, 500, 502, 503) and i < attempts - 1:
time.sleep(2 ** i)
continue
return r.json()

Billing on failure

Failed core unlock requests are not billed. If success is false, expect billing.charged to be 0.