Errors

ByteKit error response shape, common error codes, and retry guidance.

All errors from the ByteKit API follow a consistent envelope shape so you can handle them programmatically.

Error response shape

Every 4xx/5xx response across the API uses one unified envelope. status is always the literal string "failed", so you can branch on the response body alone.

{
  "status": "failed",
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Allowed 5 requests per second.",
    "http_status": 429,
    "failed_at": "2026-08-27T12:17:46.234Z"
  }
}

The number in a rate_limited message is your own plan's per-second limit — the same value the X-RateLimit-Limit response header carries.

FieldDescription
statusAlways "failed" on an error response — the top-level discriminator
error.codeMachine-readable string identifier
error.messageHuman-readable explanation
error.http_statusMirrors the HTTP response status (useful for logged or replayed bodies)
error.failed_atISO-8601 UTC timestamp of when the error was produced
error.detailsOptional object with additional context (e.g. which field failed validation). Absent — not {} — when the error carries no context

Common error codes

HTTP statusCodeMeaning
400invalid_urlA supplied URL (including webhook_url) is malformed or uses an unsupported scheme
400validation_errorField errors on /v1/search, POST /v1/billing/checkout and PUT/DELETE /v1/billing/payment-methods/{id}, plus rejections that run before schema validation on any endpoint: a removed field such as cookies or headers on a capture endpoint, or a /v1/fetch or /v1/fetch/bulk request asking for an option only /v1/scrape or /v1/bulk serves
401unauthorizedMissing or malformed Authorization header
401invalid_api_keyAPI key is present but not valid
402quota_exceededAccount has exceeded its plan quota
402spending_cap_reachedA credit-metered request would push overage spend past the account's cap. Only reachable on a custom plan with an overage agreement — standard plans have no automatic overage and stop at quota_exceeded
402billing_entitlement_impairedThe paid subscription is in a payment impairment (e.g. past_due); error.message names the reason
402billing_entitlement_expiredThe paid subscription's entitlement period has ended, or its dunning grace period has run out
403forbiddenKey is valid but lacks permission for this action
422validation_errorMalformed JSON, or a request field failed validation, everywhere the 400 row above does not name
429rate_limitedToo many requests per second; back off and retry
429concurrency_limitToo many simultaneous in-flight requests; back off and retry
500internal_errorThe upstream fetch or browser render failed

Webhook URLs Must Use HTTPS

All webhook URLs submitted to the ByteKit API must use HTTPS. Plain HTTP URLs (e.g. http://example.com/webhook) are rejected with a 400 invalid_url error.

Why HTTPS only? Webhook payloads may contain sensitive data (API keys, session tokens, scrape results). HTTPS encryption protects this data in transit.

Request with an invalid webhook URL:

{
  "webhook_url": "http://example.com/webhook"
}

Response (HTTP 400) — the same unified envelope as every other error; error.details names the offending field and reason:

{
  "status": "failed",
  "error": {
    "code": "invalid_url",
    "message": "url is not a valid HTTP(S) URL: scheme_unsupported",
    "http_status": 400,
    "failed_at": "2026-08-27T12:16:29.033Z",
    "details": {
      "url": "http://example.com/webhook",
      "field": "webhook_url",
      "reason": "scheme_unsupported"
    }
  }
}

Working example:

{
  "webhook_url": "https://example.com/webhook"
}

This applies to all endpoints that accept a webhook_url parameter: /v1/scrape (async), /v1/scrape/bulk, /v1/fetch/bulk, /v1/bulk, /v1/monitors, and /v1/sitemap.

Retry guidance

Not all errors are retryable. Use this table to decide:

CodeRetry?Guidance
invalid_urlNoFix the URL before retrying
validation_errorNoFix the request body before retrying
unauthorized / invalid_api_keyNoCheck your API key
quota_exceededNoUpgrade your plan or wait for quota reset
spending_cap_reachedNoThe overage cap is account-level and has no self-service control — contact support to raise it, or upgrade the plan
billing_entitlement_impairedNoSettle the outstanding invoice — retrying cannot succeed while the subscription is impaired
billing_entitlement_expiredNoRestore or renew the subscription; the entitlement period has ended
forbiddenNoYou do not have access to this resource
rate_limitedYesExponential backoff; honour the Retry-After header
concurrency_limitYesHonour the Retry-After header (a short fixed back-off — a slot frees on job completion, not a fixed window)
internal_errorYesExponential backoff; most resolve on retry

For retriable errors, start with a 1-second delay and double on each subsequent failure, up to a maximum of 60 seconds. Respect the Retry-After header value when it is present on 429 responses.

Example: handling errors in curl

response=$(curl -s -w "\n%{http_code}" -X POST https://api.bytekit.com/v1/scrape \
  -H "Authorization: Bearer sk_live_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}')

http_code=$(echo "$response" | tail -1)
# Body is everything except the trailing status-code line — drop only the last
# line so multi-line JSON responses survive intact.
body=$(echo "$response" | sed '$d')

if [ "$http_code" -ge 400 ]; then
  echo "Error $http_code: $body"
fi

Next steps

  • Rate Limits — quota model, concurrency slots, rate limit headers
  • Scraping — when 500 internal_error occurs and how fallback works
  • API Reference — per-endpoint error codes