completed, queued, or error. Check the top-level status first, then read data (success or queued) or error (failure).
Status codes
| Code | Meaning | error.code | Action |
|---|---|---|---|
200 OK | Synchronous success | n/a | Read data |
202 Accepted | Async dispatch (SmartBrowse) | n/a | Read data.run_id and poll |
400 Bad Request | Malformed request | invalid_request | Fix the request |
401 Unauthorized | Missing or revoked API key | unauthorized | Check X-API-Key |
402 Payment Required | Out of credits | insufficient_credits | Top up at Billing |
402 Payment Required | Email not verified | email_verification_required | Verify your email |
403 Forbidden | Authenticated, but not allowed to perform this action | forbidden | Check the key’s scope or the resource owner |
404 Not Found | Resource doesn’t exist | not_found | Verify the ID |
409 Conflict | Resource in a state that disallows the action | conflict / account_deletion_pending | E.g. spending while account deletion is pending |
422 Unprocessable Entity | Schema validation failed | validation_failed | Adjust the JSON schema or prompt |
429 Too Many Requests | Rate limit hit | rate_limited | Back off. See Rate limits |
500 / 502 | Internal error | internal_error / service_unavailable | Retry with backoff; failed requests cost 0 credits |
Success envelope
data holds the payload — extracted JSON for /v1/smartscraper, fetched content for /v1/scrape. credits_used is what this call cost after deduction.
Queued envelope
Returned by the async-dispatch endpoint (POST /v1/smartbrowse/recipes/:id/run). No credit fields here because the work hasn’t finished and nothing’s been billed yet.
data.poll_url (i.e. GET /v1/smartbrowse/runs/:id) until the inner field hits a terminal state. We named it run_status so it doesn’t clash with the envelope’s outer status:
data.run_statusis one ofqueued | running | completed | failed | cancelled
Error envelope
error.code is stable — branch on it. error.message is human-readable and can change between releases, so log it for debugging but don’t pattern-match on the text. error.details is optional structured context (e.g. {balance, required} for insufficient_credits).
The request_id also comes back as the X-Request-ID response header on every response, success or failure. Include it when you ping support.
Retrying
Transient failures (timeouts,429, 5xx) are safe to retry. Since failed requests cost 0 credits, a capped backoff loop with jitter costs you nothing beyond the eventual successful call. Don’t retry on 400, 401, 402, 403, 404, or 422 — those won’t change until you fix the request.