Errors
Every failure uses the same response format with success: false, data: null and an error object. This page lists every error.type the API can return, generated from the same registry the API serves at GET https://api.onekey.dev/v1/errors (public, no key), so it cannot drift from the code.
{
"success": false,
"data": null,
"metadata": {
"request_id": "7dd14539f2f24ca1bc1eb63809293092"
},
"error": {
"code": "invalid_cursor",
"type": "INVALID_CURSOR",
"message": "The cursor is invalid, expired, or from another query",
"request_id": "7dd14539f2f24ca1bc1eb63809293092",
"doc_url": "https://onekey.dev/errors/INVALID_CURSOR"
}
}
The error object#
| Field | Meaning |
|---|---|
type | Stable machine string in upper snake case. Branch on this. |
code | The same word in lower case (invalid_cursor). Kept for older clients; type and code never disagree. |
message | Human text. It may change and never contains text from the source platform, so do not parse it. |
request_id | Correlation id, equal to the X-Request-Id header and metadata.request_id. Quote it if you contact support. |
doc_url | https://onekey.dev/errors/{type}, which resolves to the matching section of this page. |
On a 429, and on the two idempotency answers that ask you to wait, a Retry-After header says how many seconds to wait. See Rate limits & headers.
All types at a glance#
63 types. Jump to any of them for what to do.
| Status | error.type | Meaning |
|---|---|---|
| 400 | BAD_REQUEST | The request was malformed (for example an oversized body). |
| 400 | COHORT_BUDGET_EXCEEDED | The worst-case price of a cohort query is above the max_credits you set; nothing was held or run. |
| 400 | COHORT_IDENTITY_PLATFORM_UNSUPPORTED | A member names a platform that has no recent-posts feed to query; nothing was stored. |
| 400 | IDEMPOTENCY_KEY_INVALID | The Idempotency-Key header is not a UUID. |
| 400 | INVALID_REQUEST | A batch or streaming request was malformed (bad body, too many items, or an Idempotency-Key on a GET stream); nothing was run or billed. |
| 400 | INVALID_TOKEN | The password-reset or email-verification token is invalid or expired. |
| 401 | INVALID_CREDENTIALS | Email or password did not match (dashboard sign-in). |
| 401 | NOT_AUTHENTICATED | A dashboard session cookie is required for this route. |
| 401 | UNAUTHORIZED | The x-api-key header is missing, malformed, or revoked. |
| 402 | INSUFFICIENT_CREDITS | The account balance cannot cover this call; buy a pack to continue. |
| 402 | KEY_BUDGET_EXCEEDED | This API key reached its credit_limit; raise or clear the budget, or use another key. |
| 403 | CSRF_REJECTED | A state-changing dashboard request lacked the CSRF header or came from an unknown origin. |
| 403 | PAID_ACCESS_RESTRICTED | This account may not use paid data routes yet (verify your email or buy a pack). |
| 403 | PASSWORD_INCORRECT | Changing the password: the current password did not match. |
| 404 | BILLING_WEBHOOK_NOT_FOUND | No billing webhook with that id exists on this account. |
| 404 | COHORT_NOT_FOUND | No cohort with that id exists on this account, or it passed its retention and was deleted. |
| 404 | COHORT_QUERY_NOT_FOUND | No cohort query with that id exists on this account, or it expired and was deleted. |
| 404 | JOB_NOT_FOUND | No job with that id exists on this account, or its results expired and were deleted. |
| 404 | MONITOR_NOT_FOUND | No monitor with that id exists on this account. |
| 404 | NOT_FOUND | The endpoint does not exist, or the upstream platform has no such item. |
| 405 | METHOD_NOT_ALLOWED | This route does not accept that HTTP method. |
| 409 | ALREADY_VERIFIED | The email address is already verified; no verification email was sent. |
| 409 | BILLING_WEBHOOK_LIMIT | The account already has the maximum number of billing webhooks. |
| 409 | COHORT_IDENTITY_CONFLICT | Two different external_ids claim the same platform and handle inside one cohort. |
| 409 | COHORT_LIMIT_REACHED | The account already has the maximum number of cohorts; delete one. |
| 409 | COHORT_MEMBER_LIMIT | A cohort holds at most 10,000 members. |
| 409 | COHORT_QUERY_NOT_READY | The cohort query has not succeeded yet, so there are no results to read; poll its status. |
| 409 | DELETE_ALREADY_REQUESTED | Account deletion was already requested; an operator completes it. |
| 409 | IDEMPOTENCY_IN_PROGRESS | A request with the same Idempotency-Key is still running; retry after 1 second. |
| 409 | IDEMPOTENCY_REPLAY_UNAVAILABLE | The original response exceeded 64 KB and was not stored for replay. |
| 409 | JOB_FINISHED | The job already finished, so it cannot be cancelled. |
| 409 | JOB_LIMIT_REACHED | The account (or this key) already has the maximum number of unfinished jobs. |
| 409 | KEY_LIMIT | The account already has the maximum number of active API keys. |
| 409 | MONITOR_LIMIT_REACHED | The account already has the maximum number of active monitors; pause or delete one. |
| 409 | MONITOR_RUN_IN_PROGRESS | The monitor is being run by the scheduler right now; retry the manual run shortly. |
| 409 | WEBHOOK_DISABLED | The monitor's webhook was switched off after repeated delivery failures; PATCH a new webhook.url to re-enable it. |
| 422 | IDEMPOTENCY_KEY_REUSED | The Idempotency-Key was already used for a different endpoint or parameters. |
| 422 | INVALID_BILLING_WEBHOOK | A billing webhook setting is malformed (events, low_threshold_credits, url, description). |
| 422 | INVALID_COHORT | A cohort request is malformed (unknown field, bad handle, missing caps, too many members in one call). |
| 422 | INVALID_CURSOR | The pagination cursor is invalid, was tampered with, or belongs to a different query. |
| 422 | INVALID_INTERVAL | A monitor interval must be one of 15m, 1h, 6h, 12h, 24h. |
| 422 | INVALID_JOB | A job request is malformed (bad body, too many items, unknown fields); nothing was held or charged. |
| 422 | INVALID_LABEL | The label= parameter named a preset that does not exist; nothing was charged. |
| 422 | INVALID_PARAMS | A query parameter is missing, too long, or has the wrong format. |
| 422 | INVALID_RULE | A monitor alert rule is malformed (see the rule grammar in the monitors docs). |
| 422 | INVALID_TARGET | A monitor target names an unknown endpoint or recipe, or a malformed body. |
| 422 | NO_SEARCHABLE_PLATFORMS | Universal search or a report: the platform hints, category or explicit list left no platform with an endpoint in the needed role; nothing was charged. |
| 422 | PASSWORD_POLICY | The new password does not meet the policy (length, common passwords, not the email). |
| 422 | SIGNUP_FAILED | The account could not be created with those details. |
| 422 | UNSUPPORTED_PLATFORM | A multi-platform recipe named a platform that has no endpoint in that role; nothing was charged. |
| 422 | UNSUPPORTED_URL | GET /v1/lookup: no endpoint recognises that URL shape; nothing was charged. |
| 422 | WEBHOOK_URL_REJECTED | A webhook URL must be https on a public host; private, loopback, link-local and metadata addresses are refused. |
| 429 | AI_BUDGET_EXCEEDED | Today's AI judgment allowance for this account (or the service) is used up; see Retry-After. |
| 429 | CONCURRENCY_LIMITED | Too many requests from this key are in flight at once (default cap 50); retry as others finish, see Retry-After and X-Concurrency-*. |
| 429 | RATE_LIMITED | Too many requests for this key, too many failed sign-ins, or the dashboard playground / resend limit; see Retry-After. |
| 500 | HTTP_ERROR | A generic HTTP-level failure that has no more specific type. |
| 500 | INTERNAL_ERROR | Something failed on our side; the request_id identifies it for support. |
| 502 | UPSTREAM_BLOCKED | The upstream platform refused our request; nothing was charged. |
| 502 | UPSTREAM_ERROR | The upstream platform returned an error; nothing was charged. |
| 502 | UPSTREAM_RATE_LIMITED | The upstream platform is rate limiting us; retry later, nothing was charged. |
| 502 | UPSTREAM_TIMEOUT | The upstream platform did not answer in time; nothing was charged. |
| 503 | AI_UNAVAILABLE | AI judgments are temporarily unavailable; retry, or call without strict=true to get unjudged rows. |
| 503 | IDEMPOTENCY_STORE_UNAVAILABLE | The idempotency store is unreachable, so the request was refused before billing. |
Your request#
The request itself needs changing. The same request will fail the same way, so do not retry it.
BAD_REQUEST#
400 bad_request. The request was malformed (for example an oversized body).
- What to do: Fix the request. The body or headers could not be read at all.
- Retry: No.
- Charged: No.
INVALID_REQUEST#
400 invalid_request. A batch or streaming request was malformed (bad body, too many items, or an Idempotency-Key on a GET stream); nothing was run or billed.
- What to do: Fix the request body or headers as the message describes. Nothing was run, so there is nothing to undo.
- Retry: No, not unchanged.
- Charged: No.
INVALID_PARAMS#
422 invalid_params. A query parameter is missing, too long, or has the wrong format.
- What to do: Compare with the parameters table on the endpoint page: a required parameter is missing, or a value is too long or has the wrong format. The message names the parameter.
- Retry: No.
- Charged: No, rejected before any charge.
INVALID_CURSOR#
422 invalid_cursor. The pagination cursor is invalid, was tampered with, or belongs to a different query.
- What to do: Send
pagination.next_cursorback exactly as you received it, as thecursorparameter, with the same endpoint and the same other parameters. Start again from the first page if you no longer have a valid cursor. See Pagination. - Retry: No.
- Charged: No.
INVALID_LABEL#
422 invalid_label. The label= parameter named a preset that does not exist; nothing was charged.
- What to do: The
label=parameter named a preset that does not exist. Use onlysentiment,question,purchase_intent,complaintandissue, comma-separated, at most five. The message lists the names it did not recognise. See Labels. - Retry: No, not unchanged.
- Charged: No, rejected before anything runs.
UNSUPPORTED_URL#
422 unsupported_url. GET /v1/lookup: no endpoint recognises that URL shape; nothing was charged.
- What to do: The URL you passed does not match any endpoint. Call the platform endpoint directly with its own parameters instead; the platform catalog lists them.
- Retry: No.
- Charged: No.
UNSUPPORTED_PLATFORM#
422 unsupported_platform. A multi-platform recipe named a platform that has no endpoint in that role; nothing was charged.
- What to do: Remove that platform from the request, or use one that has an endpoint in that role.
GET /v1/platforms(free, no key) lists what each platform offers. - Retry: No, not unchanged.
- Charged: No.
NO_SEARCHABLE_PLATFORMS#
422 no_searchable_platforms. Universal search or a report: the platform hints, category or explicit list left no platform with an endpoint in the needed role; nothing was charged.
- What to do: Universal search or a report found no platform to search. Either the platform hints in your query name only platforms with no search endpoint (for example
x on instagram), yourcategoryholds none, or no form of the query fits the search rules of the platforms available. Name platforms that have a search endpoint inplatforms=, drop the hint, or remove thecategory;dry_run=trueshows the plan for free. See Universal search and Reports. - Retry: No, not unchanged.
- Charged: No, refused before any leg runs.
NOT_FOUND#
404 not_found. The endpoint does not exist, or the upstream platform has no such item.
- What to do: Check the endpoint path (
/v1/{platform}/{endpoint}) and the spelling of the parameter. For a lookup, the record does not exist on the platform. - Retry: No.
- Charged: No. A lookup that reaches the platform is reserved and refunded.
METHOD_NOT_ALLOWED#
405 method_not_allowed. This route does not accept that HTTP method.
- What to do: Data endpoints are
GET. Dashboard routes use the method shown on their docs page. - Retry: No.
- Charged: No.
Keys, access and rate limits#
Who is calling, and how often.
UNAUTHORIZED#
401 unauthorized. The x-api-key header is missing, malformed, or revoked.
- What to do: Send the key in the
x-api-keyheader and check it is not revoked in your dashboard. Repeated bad attempts from one address are rate limited. - Retry: No, not with the same key.
- Charged: No.
PAID_ACCESS_RESTRICTED#
403 paid_access_restricted. This account may not use paid data routes yet (verify your email or buy a pack).
- What to do: This endpoint needs a data route that is only open to accounts with a verified email and a purchased pack. Verify your email or buy a pack, then retry.
- Retry: After the account qualifies.
- Charged: No.
RATE_LIMITED#
429 rate_limited. Too many requests for this key, too many failed sign-ins, or the dashboard playground / resend limit; see Retry-After.
- What to do: Wait the number of seconds in the
Retry-Afterheader, then retry.X-RateLimit-Remainingtells you how close you are before it happens. See Rate limits & headers. - Retry: Yes, after
Retry-After. - Charged: No.
CONCURRENCY_LIMITED#
429 concurrency_limited. Too many requests from this key are in flight at once (default cap 50); retry as others finish, see Retry-After and X-Concurrency-*.
- What to do: This key already has the maximum number of requests in flight (default 50). Lower your parallelism, or wait for some to finish and retry after
Retry-After(1 second).X-Concurrency-Remainingshows how much room is left. See Rate limits & headers. - Retry: Yes, after
Retry-After. - Charged: No.
Credits and keys#
Nothing is charged for any of these.
INSUFFICIENT_CREDITS#
402 insufficient_credits. The account balance cannot cover this call; buy a pack to continue.
- What to do: The account balance cannot cover this call. Buy credits.
GET /v1/accountshows the balance for free. - Retry: After topping up.
- Charged: No.
KEY_BUDGET_EXCEEDED#
402 key_budget_exceeded. This API key reached its credit_limit; raise or clear the budget, or use another key.
- What to do: This key has reached its own credit limit; the account balance is untouched and other keys still work. Raise or clear the limit in the dashboard, or use another key. See Per-key credit limits.
- Retry: After the limit is raised or cleared.
- Charged: No.
KEY_LIMIT#
409 key_limit. The account already has the maximum number of active API keys.
- What to do: Revoke a key you no longer use before creating another. The maximum number of active keys is shown in the dashboard.
- Retry: After revoking a key.
- Charged: Not applicable.
AI enrichment#
Only when a call asks for labels, relevance or themes. Preview: available only when enabled for your account.
AI_BUDGET_EXCEEDED#
429 ai_budget_exceeded. Today's AI judgment allowance for this account (or the service) is used up; see Retry-After.
- What to do: Nothing could be judged because the daily enrichment allowance for your account, or for the service as a whole, is used up. Wait
Retry-Afterseconds (until the next UTC day), or addstrict=falseto get the rows back unjudged. A call that judged only some rows is a partial result, not this error. See AI pricing. - Retry: Yes, after
Retry-After; or now withstrict=false. - Charged: No. The reservation is refunded in full; the endpoint data is cached, so repeating the call is a free hit for the data.
AI_UNAVAILABLE#
503 ai_unavailable. AI judgments are temporarily unavailable; retry, or call without strict=true to get unjudged rows.
- What to do: Nothing could be judged: enrichment is not enabled for this account yet, or the judging service is temporarily unavailable. Retry after a few seconds (
Retry-After), or addstrict=falseto get the rows back withlabels.statusset tounavailableand no judgments. See Labels. - Retry: Yes, after a short wait, if the service was only unavailable. Not if enrichment is not enabled for your account.
- Charged: No. Refused before the data is fetched when enrichment is off; otherwise the enrichment reservation is refunded in full.
Idempotency#
Only when you send an Idempotency-Key header. See Idempotency.
IDEMPOTENCY_KEY_INVALID#
400 idempotency_key_invalid. The Idempotency-Key header is not a UUID.
- What to do: The
Idempotency-Keyheader must be a UUID (any version, for example6f1c2d3e-4b5a-4c6d-8e7f-90a1b2c3d4e5). See Idempotency. - Retry: With a valid key.
- Charged: No, refused before billing.
IDEMPOTENCY_IN_PROGRESS#
409 idempotency_in_progress. A request with the same Idempotency-Key is still running; retry after 1 second.
- What to do: The first request with this key is still running. Wait one second (
Retry-After: 1) and send the same request again; you will get the replayed result once it completes. - Retry: Yes, after one second.
- Charged: No, this attempt is not billed. The first request is billed once when it completes.
IDEMPOTENCY_REPLAY_UNAVAILABLE#
409 idempotency_replay_unavailable. The original response exceeded 64 KB and was not stored for replay.
- What to do: The original call succeeded and was billed, but its response was over 64 KB and was not kept for replay. Send the request again with a new
Idempotency-Key; that is a new, billed call. - Retry: Only with a new key.
- Charged: No, nothing is charged for this answer.
IDEMPOTENCY_KEY_REUSED#
422 idempotency_key_reused. The Idempotency-Key was already used for a different endpoint or parameters.
- What to do: The same key was used before for a different endpoint or different parameters. Use a fresh UUID for each distinct request.
- Retry: With a new key.
- Charged: No.
IDEMPOTENCY_STORE_UNAVAILABLE#
503 idempotency_store_unavailable. The idempotency store is unreachable, so the request was refused before billing.
- What to do: The store that guarantees a single charge is unreachable, so the request was refused before billing rather than risk billing twice. Wait one second (
Retry-After: 1) and retry with the same key. - Retry: Yes, after one second.
- Charged: No.
Monitors and webhooks#
Only on the /v1/monitors routes. Nothing is charged for any of these. See Monitors.
MONITOR_NOT_FOUND#
404 monitor_not_found. No monitor with that id exists on this account.
- What to do: No monitor with that id exists on this account. List yours with
GET /v1/monitorsand check the id (mon_...). A deleted monitor is gone for good. See Monitors. - Retry: No.
- Charged: No.
MONITOR_LIMIT_REACHED#
409 monitor_limit_reached. The account already has the maximum number of active monitors; pause or delete one.
- What to do: The account already has 25 active monitors. Pause or delete one, then create or reactivate this one. Paused monitors do not count. See Monitors.
- Retry: After pausing or deleting a monitor.
- Charged: No.
MONITOR_RUN_IN_PROGRESS#
409 monitor_run_in_progress. The monitor is being run by the scheduler right now; retry the manual run shortly.
- What to do: The scheduler is running this monitor right now, so a manual run was refused. Wait a few seconds and run it again, or just wait for the scheduled run.
- Retry: Yes, after a few seconds.
- Charged: No, this attempt is not billed. The run already in progress is billed once, as usual.
WEBHOOK_DISABLED#
409 webhook_disabled. The monitor's webhook was switched off after repeated delivery failures; PATCH a new webhook.url to re-enable it.
- What to do: Ten delivery attempts in a row failed, so the webhook of this monitor was switched off. Runs continue and you can still read their deltas from
GET /v1/monitors/{id}/runs. Fix your endpoint, thenPATCHthe monitor with{"webhook": {"url": "..."}}(the same URL is fine) to switch it back on. See Monitors. - Retry: After the webhook is re-enabled with a
PATCH. - Charged: No.
INVALID_INTERVAL#
422 invalid_interval. A monitor interval must be one of 15m, 1h, 6h, 12h, 24h.
- What to do: Use one of
15m,1h,6h,12hor24h. Nothing shorter is accepted. - Retry: No, not unchanged.
- Charged: No.
INVALID_RULE#
422 invalid_rule. A monitor alert rule is malformed (see the rule grammar in the monitors docs).
- What to do: The message names the rule that is wrong. At most 10 rules;
onisany_change,new_items,field_changeorthreshold;field_changeandthresholdneed a dottedfield;thresholdneeds anopand avalue; unknown keys are refused. See Alert rules. - Retry: No, not unchanged.
- Charged: No.
INVALID_TARGET#
422 invalid_target. A monitor target names an unknown endpoint or recipe, or a malformed body.
- What to do: The target must name an existing
platformandendpoint, or arecipeofsearch/multi,creator/cardorlookup, and the webhook and name must be well formed. Check the endpoint on the platform catalog. - Retry: No, not unchanged.
- Charged: No.
WEBHOOK_URL_REJECTED#
422 webhook_url_rejected. A webhook URL must be https on a public host; private, loopback, link-local and metadata addresses are refused.
- What to do: The webhook must be an
httpsURL on port 443 to a public host. Private, loopback, link-local and cloud-metadata addresses, internal names, IP literals and URLs with credentials are refused, and so is a host that resolves to any of them. To test locally, use a public HTTPS tunnel. See Monitors. - Retry: No, not unchanged.
- Charged: No.
Cohorts#
Only on the /v1/cohorts and /v1/cohort-queries routes. Nothing is charged for any of these. See Cohorts.
COHORT_NOT_FOUND#
404 cohort_not_found. No cohort with that id exists on this account, or it passed its retention and was deleted.
- What to do: No cohort with that id exists on this account, or it passed its retention and was deleted. List yours with
GET /v1/cohorts. See Cohorts. - Retry: No.
- Charged: No.
COHORT_QUERY_NOT_FOUND#
404 cohort_query_not_found. No cohort query with that id exists on this account, or it expired and was deleted.
- What to do: No cohort query with that id exists on this account, or it expired with its cohort. Submit it again with
POST /v1/cohorts/{id}/queries. - Retry: No.
- Charged: No.
COHORT_LIMIT_REACHED#
409 cohort_limit_reached. The account already has the maximum number of cohorts; delete one.
- What to do: The account has 50 cohorts. Delete one with
DELETE /v1/cohorts/{id}. - Retry: After deleting one.
- Charged: No.
COHORT_MEMBER_LIMIT#
409 cohort_member_limit. A cohort holds at most 10,000 members.
- What to do: A cohort holds at most 10,000 members. Remove some with
DELETE /v1/cohorts/{id}/membersor use a second cohort. - Retry: No, not unchanged.
- Charged: No.
COHORT_IDENTITY_CONFLICT#
409 cohort_identity_conflict. Two different external_ids claim the same platform and handle inside one cohort.
- What to do: Two different
external_ids claim the same platform and handle in this cohort. Send oneexternal_idper identity, or remove the old one first. See Cohorts. - Retry: No, not unchanged.
- Charged: No.
COHORT_QUERY_NOT_READY#
409 cohort_query_not_ready. The cohort query has not succeeded yet, so there are no results to read; poll its status.
- What to do: The query has not succeeded. Poll
GET /v1/cohort-queries/{id}untilstatusissucceeded, then read the results. Afailedor cancelled query has none. - Retry: Yes, after it succeeds.
- Charged: No.
COHORT_IDENTITY_PLATFORM_UNSUPPORTED#
400 cohort_identity_platform_unsupported. A member names a platform that has no recent-posts feed to query; nothing was stored.
- What to do: A member names a platform with no recent-posts feed.
GET /v1/cohortslists the supported platforms. Nothing in the request was stored. - Retry: No.
- Charged: No.
COHORT_BUDGET_EXCEEDED#
400 cohort_budget_exceeded. The worst-case price of a cohort query is above the max_credits you set; nothing was held or run.
- What to do: The worst-case price of the query (in the message) is above your
max_credits. Raise it, narrowplatforms, or use a smaller cohort. Nothing was held or run. - Retry: No, not unchanged.
- Charged: No.
INVALID_COHORT#
422 invalid_cohort. A cohort request is malformed (unknown field, bad handle, missing caps, too many members in one call).
- What to do: Fix the body as the message says: unknown fields (a member is
platform,handleand an optionalexternal_idonly), a bad handle, missing caps (max_pages_per_identity,max_items_per_identity,max_creditshave no defaults) or adate_fromthat is not a full RFC 3339 timestamp. See Cohorts. - Retry: No, not unchanged.
- Charged: No.
Jobs and billing webhooks#
Only on /v1/jobs and /v1/billing/webhooks. Reading and managing these is free; a refused job holds nothing. See Background jobs and Billing webhooks.
JOB_NOT_FOUND#
404 job_not_found. No job with that id exists on this account, or its results expired and were deleted.
- What to do: No job with that id exists on this account, or its results passed the 7 days they are kept after the job finished. List yours with
GET /v1/jobsand check the id (job_...). See Background jobs. - Retry: No.
- Charged: No.
JOB_LIMIT_REACHED#
409 job_limit_reached. The account (or this key) already has the maximum number of unfinished jobs.
- What to do: The account has 10 unfinished jobs, or this key has 3. Wait for one to finish (poll
GET /v1/jobs) or cancel one withDELETE /v1/jobs/{id}, then submit again. See Limits. - Retry: After a job finishes or is cancelled.
- Charged: No, nothing was held.
JOB_FINISHED#
409 job_finished. The job already finished, so it cannot be cancelled.
- What to do: The job already completed, was cancelled or failed, so there is nothing left to cancel. Read its results with
GET /v1/jobs/{id}. - Retry: No.
- Charged: No.
INVALID_JOB#
422 invalid_job. A job request is malformed (bad body, too many items, unknown fields); nothing was held or charged.
- What to do: The message names the problem. Send exactly one of
requests(up to 5,000 items ofplatform,endpoint,params) orcrawl(endpointandurlsfor awebendpoint); AI parameters are not supported; unknown fields are refused. See Submit a job. - Retry: No, not unchanged.
- Charged: No, nothing was held.
BILLING_WEBHOOK_NOT_FOUND#
404 billing_webhook_not_found. No billing webhook with that id exists on this account.
- What to do: No billing webhook with that id exists on this account. List yours with
GET /v1/billing/webhooks(whk_...). A deleted endpoint is gone for good. See Billing webhooks. - Retry: No.
- Charged: No.
BILLING_WEBHOOK_LIMIT#
409 billing_webhook_limit. The account already has the maximum number of billing webhooks.
- What to do: The account has 5 billing webhooks. Delete one you no longer use, or change its
urlandeventswithPATCHinstead of creating another. - Retry: After deleting an endpoint.
- Charged: No.
INVALID_BILLING_WEBHOOK#
422 invalid_billing_webhook. A billing webhook setting is malformed (events, low_threshold_credits, url, description).
- What to do: The message names the field.
eventsis a non-empty list ofcredits.low,credits.depleted,purchase.completed;low_threshold_creditsis a number of credits, 0 or more;descriptionis at most 120 characters; unknown fields are refused. See Billing webhooks. - Retry: No, not unchanged.
- Charged: No.
The source platform#
The platform Onekey fetches from did not answer usefully. These are refunded and worth retrying with backoff. If Onekey holds a recent copy it serves that instead, as a 200 with metadata.stale: true (see Caching).
UPSTREAM_ERROR#
502 upstream_error. The upstream platform returned an error; nothing was charged.
- What to do: The source platform answered with an error. Retry with exponential backoff (for example 1 s, 2 s, 4 s). If it keeps failing, check /v1/status.
- Retry: Yes, with backoff.
- Charged: No, refunded.
UPSTREAM_TIMEOUT#
502 upstream_timeout. The upstream platform did not answer in time; nothing was charged.
- What to do: The source platform was too slow. Retry with backoff.
- Retry: Yes, with backoff.
- Charged: No, refunded.
UPSTREAM_BLOCKED#
502 upstream_blocked. The upstream platform refused our request; nothing was charged.
- What to do: The source platform refused Onekey's request. Retry later; if it persists, check /v1/status.
- Retry: Later.
- Charged: No, refunded.
UPSTREAM_RATE_LIMITED#
502 upstream_rate_limited. The upstream platform is rate limiting us; retry later, nothing was charged.
- What to do: The source platform is throttling Onekey. Retry after a short wait.
- Retry: Yes, after a short wait.
- Charged: No, refunded.
Our side#
Something broke in the API. Quote the request_id if you write to us.
INTERNAL_ERROR#
500 internal_error. Something failed on our side; the request_id identifies it for support.
- What to do: Something failed on our side. Retry once; if it repeats, send us the
request_id. - Retry: Once.
- Charged: No. Any reservation is refunded.
HTTP_ERROR#
500 http_error. A generic HTTP-level failure that has no more specific type.
- What to do: A framework-level failure with no more specific type. Retry once; if it repeats, send us the
request_id. - Retry: Once.
- Charged: No.
Dashboard only#
Returned by the sign-in and account routes the dashboard uses, never by data endpoints.
INVALID_CREDENTIALS#
401 invalid_credentials. Email or password did not match (dashboard sign-in).
- What to do: Dashboard sign-in only: the email or password did not match. Use Forgot password if needed.
- Retry: With the right credentials.
- Charged: Not applicable.
NOT_AUTHENTICATED#
401 not_authenticated. A dashboard session cookie is required for this route.
- What to do: Dashboard routes need a signed-in browser session, not an API key. Sign in at /login.
- Retry: After signing in.
- Charged: Not applicable.
CSRF_REJECTED#
403 csrf_rejected. A state-changing dashboard request lacked the CSRF header or came from an unknown origin.
- What to do: Dashboard routes that change state need the CSRF header and a request from the dashboard origin. Use the dashboard rather than calling these routes from elsewhere.
- Retry: No.
- Charged: Not applicable.
INVALID_TOKEN#
400 invalid_token. The password-reset or email-verification token is invalid or expired.
- What to do: The password-reset or email-verification link is invalid or has expired. Request a new one.
- Retry: With a new link.
- Charged: Not applicable.
SIGNUP_FAILED#
422 signup_failed. The account could not be created with those details.
- What to do: The account could not be created with those details. Check the email address and password requirements.
- Retry: With different details.
- Charged: Not applicable.
Other#
Recently added types.
PASSWORD_INCORRECT#
403 password_incorrect. Changing the password: the current password did not match.
- What to do: Read the
messageand quote therequest_idif you contact support. - Retry: Check the status code: 4xx will fail the same way; 5xx may succeed on retry.
- Charged: Failed calls are not charged.
PASSWORD_POLICY#
422 password_policy. The new password does not meet the policy (length, common passwords, not the email).
- What to do: Read the
messageand quote therequest_idif you contact support. - Retry: Check the status code: 4xx will fail the same way; 5xx may succeed on retry.
- Charged: Failed calls are not charged.
ALREADY_VERIFIED#
409 already_verified. The email address is already verified; no verification email was sent.
- What to do: Read the
messageand quote therequest_idif you contact support. - Retry: Check the status code: 4xx will fail the same way; 5xx may succeed on retry.
- Charged: Failed calls are not charged.
DELETE_ALREADY_REQUESTED#
409 delete_already_requested. Account deletion was already requested; an operator completes it.
- What to do: Read the
messageand quote therequest_idif you contact support. - Retry: Check the status code: 4xx will fail the same way; 5xx may succeed on retry.
- Charged: Failed calls are not charged.
Retry guidance#
- Retry:
429afterRetry-After;502and500with exponential backoff and jitter (for example 1 s, 2 s, 4 s, up to 3 or 4 tries); the two idempotency409/503answers after one second. - Do not retry:
400,401,402,403,404,405,422. The same request fails the same way. - Failed calls are not charged, so a retry that does not work costs nothing. To make a retry that *does* work safe against double billing, send an
Idempotency-Key. - One exception to "not charged": a request that was refused by the source because of your own input (for example a URL you supplied that the target rejected) after a paid data route already ran keeps its charge. This is rare and the response is still the
502shown above.
Example: safe retry loop#
import time, requests
def onekey(path, params, key, tries=4):
for attempt in range(tries):
r = requests.get(f"https://api.onekey.dev{path}", params=params, headers={"x-api-key": key}, timeout=30)
if r.status_code in (429, 503) or (r.status_code == 409 and r.json()["error"]["type"] == "IDEMPOTENCY_IN_PROGRESS"):
time.sleep(int(r.headers.get("Retry-After", "1")))
elif r.status_code >= 500:
time.sleep(2 ** attempt)
else:
return r.json() # success, or a 4xx you should not retry
return r.json()