Skip to content
Developers

Errors

Every error is a problem document with a stable code. Switch on the code, not on the text.

The problem document

An error answers with the content type application/problem+json (RFC 9457) and a body like this one. There is no success member and no other error shape.

An example problem document
{
  "type": "https://perfbee.com/developers/errors#not_found",
  "title": "Not found",
  "status": 404,
  "code": "not_found",
  "detail": "No crawl with this id in your workspace.",
  "request_id": "7c1f0a52-3d4b-4a39-8c0e-5e2a9d1b6f10"
}
FieldTypeDescription
typestringA URL naming the kind of error: https://perfbee.com/developers/errors#<code>.
titlestringA short, stable summary of the kind of error.
statusintegerThe HTTP status, repeated.
codestringThe stable machine code (see the error registry in the docs).
detailstringWhat went wrong this time, in a sentence.
request_idstringThe request's X-Request-ID: quote it when you contact support.
creditsobject or null402 only: the credit arithmetic.
credits.needednumberCredits the operation needs (worst case).
credits.availablenumberCredits the workspace has left this month.
credits.held_by_running_crawlsnumberCredits reserved by crawls that are still running.
errorsarray of object or null422 only: every field that was refused.
errors[].instringWhere the value was read: body, query, path or header.
errors[].fieldstringThe parameter or body field, dotted for nested ones.
errors[].messagestringWhy it was refused. The value that was sent is never echoed.
errors[].typestringA short machine word for the kind of mistake.
rate_limitobject or null429 from a rate limit: which one and how big.
rate_limit.scopestringWhich limit was hit: key, heavy, start, workspace or network.
rate_limit.limitintegerCalls allowed in the window.
rate_limit.window_secondsintegerLength of the window in seconds.
required_scopestring or null403 scope_required only: the scope the endpoint needs.

code is the word to switch on. detail is for people and may change. type is a link to the row of the table below. Quote request_id, which is also the X-Request-ID header, when you write to support.

Handling errors

  • A 429 carries Retry-After: wait that many seconds, then repeat the same call. The same goes for capacity_busy and crawl_not_ready.
  • A 402 is not a rate problem: the workspace has too few credits. credits says what is needed and what is free.
  • A 422, 404, 403 and 401 will not change if you repeat the call. Fix the call.
  • An internal_error is our fault. Try again after a short wait, and send the request_id if it keeps happening.
  • A crawl started twice with the same Idempotency-Key is safe: see idempotency.

New codes can be added within version v1. A client should treat a code it does not know by its status.

Every error code

CodeStatusWhat it means
bad_request400Bad request. The request could not be understood, for example a path or header the API cannot read.
invalid_cursor400Invalid cursor. The cursor was changed, belongs to another endpoint, another set of filters or another workspace. Start the list again without a cursor.
invalid_key401Invalid API key. The key is missing, malformed, revoked, expired, or its owner left the workspace. Send `Authorization: Bearer pbk_...`.
insufficient_credits402Not enough credits. The operation needs more credits than the workspace has left this month. `credits` says how many are needed and free.
forbidden403Forbidden. The key is valid but the API is not available to its workspace yet.
plan_required403Plan upgrade required. The workspace's plan does not include this feature.
scope_required403Scope required. The key lacks the scope this endpoint needs; `required_scope` names it.
ai_readiness_unavailable404No AI readiness report. The crawl has no AI readiness report: a fast crawl extracts no page data, and a detailed crawl that stopped early or is older than the report has none. `reason` says which.
not_found404Not found. The object does not exist, or belongs to another workspace (the two are never told apart).
method_not_allowed405Method not allowed. The path exists but not for this HTTP method; `Allow` lists the methods.
crawl_in_progress409Crawl in progress. A crawl of this site is already pending or running; `crawl_id` in the problem is that crawl.
crawl_not_active409Crawl is not active. The crawl is not pending or running, so there is nothing to cancel; `detail` says what it is.
crawl_not_ready409Crawl not ready. The crawl's data is not available yet or any more; retry after the time in `Retry-After`.
delivery_in_progress409Delivery in progress. The delivery is still pending or being retried. Wait until it is delivered or failed.
delivery_payload_unavailable409Payload is no longer stored. Only a failed delivery keeps its payload, for 3 days; a delivered event, or an older failure, cannot be sent again. Read the state through the API instead.
feed_limit_reached409Live link limit reached. The workspace already has as many live links as it may; revoke one first.
feed_site_has_staging_access409Site has staging access. The site has staging access set up, so its data cannot be shared through a live link.
idempotency_in_progress409Request in progress. A request with the same Idempotency-Key is still being handled; retry shortly.
webhook_endpoint_limit409Webhook endpoint limit reached. A workspace can have 5 webhook endpoints. Delete one first.
export_too_large413Export too large. The export holds more rows than a direct download allows; narrow it or use the dashboard's export job.
feed_too_large413Live link too large. The dataset holds more rows or cells than a live link can; narrow it with filters or download an export.
idempotency_key_reused422Idempotency key reused. The Idempotency-Key was already used with a different request body.
invalid_webhook_events422Invalid webhook events. `events` must name at least one event from GET /webhook-events, each once.
invalid_webhook_url422Invalid webhook URL. The URL must be an https:// address on a public host, without a user name, spaces or quote characters. The host is resolved when the endpoint is saved and again at every delivery; a private or internal address is refused.
validation_error422Validation failed. A parameter or body field is missing or has a wrong value; `errors` lists them.
crawl_limit_reached429Daily crawl limit reached. The key started 10 crawls today (UTC), or the workspace 20. `crawl_limit` says which; `Retry-After` is the time to the next UTC day.
crawl_too_soon429Crawl too soon. The site was crawled through the API less than 10 minutes ago (by REST or the MCP server). Crawls of one site through the API must be that far apart; `Retry-After` is the time left.
daily_credit_cap_reached429Daily credit cap reached. The crawls this key started today, counted at their worst case, plus this one would pass the key's daily credit cap. `daily_credit_cap` says the cap, what was used and what this crawl needs; `Retry-After` is the time to the next UTC day.
rate_limited429Rate limit exceeded. Too many requests this minute for the key, the workspace or the network; wait for `Retry-After` seconds.
too_many_heavy_requests429Too many heavy calls at once. Too many heavy calls of this workspace (lists of links, issues and pages, diffs, exports, webhook tests) are running at the same time. Send them one after another; retry after `Retry-After` seconds.
too_many_running_crawls429Too many running crawls. The workspace already has 2 API crawls running. Wait for one to end, or cancel one.
internal_error500Internal error. Something failed on our side; the error is logged under `request_id`.
capacity_busy503Service busy. The service has no free capacity for this request right now; retry after `Retry-After`.
query_too_slow503Query too slow. The data query ran past its time limit; narrow the filters or the page size and try again.
webhook_signing_unavailable503Webhook signing is not available. Signing secrets cannot be made on this server yet, so an endpoint cannot be created or its secret rotated. Try again later or write to support@perfbee.com.