Skip to content

Requests

Errors & limits

Every error has one shape: a code you can switch on, a message you can read, the field at fault, and what to do about it.

Find a code (A–Z)

Error format

Example error
HTTP/1.1 401 Unauthorized
X-Request-Id: req_dc3ff85b0bd44d62b2c4606f89ec3021

{
  "type": "authentication_error",
  "code": "UNAUTHENTICATED",
  "message": "API key is not recognised",
  "field": "authorization",
  "alternatives": ["check that the whole key was copied", "create a new key in your dashboard"],
  "details": { "request_id": "req_dc3ff85b0bd44d62b2c4606f89ec3021" }
}
FieldDescription
typeThe error class: authentication_error, permission_error, invalid_request_error, not_found_error, conflict_error, rate_limit_error, billing_error, service_unavailable_error or api_error.
codeA stable identifier to handle in code (tables below).
messageWhat went wrong, in words, with the numbers involved.
fieldThe part of the request the error is about.
alternativesHow to fix it, most useful first.
detailsStructured extras. Always includes request_id; quote it when you contact us.
Surface the whole error
Python
import os
import requests


def check(response: requests.Response) -> dict:
    """Return the JSON body, or raise with everything the API said about the error."""
    if response.ok:
        return response.json()
    error = response.json()
    raise RuntimeError(
        f"{response.status_code} {error['code']} ({error['field']}): {error['message']}. "
        f"Try: {'; '.join(map(str, error['alternatives']))}. Request id: {error['details'].get('request_id')}"
    )


# A task model id that is not on your account, to see what a refusal says:
response = requests.get(
    'https://dydema--eegapi-gateway.modal.run/v1/task-models/clf_00000000000000000000000000000000',
    headers={"Authorization": f"Bearer {os.environ['DYDEMA_API_KEY']}"},
    timeout=30,
)
try:
    check(response)
except RuntimeError as error:
    print(error)  # 404 CLASSIFIER_NOT_FOUND (classifier_id): This task model is unavailable or has been deleted. ...

Authentication and permissions

CodeStatusMeaning
UNAUTHENTICATED401No key, or a key the API does not recognize. Check the Authorization: Bearer header.
KEY_REVOKED401The key was revoked in the dashboard. Use another key or create a new one.
KEY_EXPIRED401The key passed its expiry date. Create a new one.
SCOPE_MISSING403The key is not allowed to use this endpoint (see Authentication).
TENANT_SUSPENDED403The account is paused, usually after a failed payment. Update your card under Billing.
IP_NOT_ALLOWED403The key is restricted to certain IP addresses and this is not one of them.

Request errors

CodeStatusMeaning
CLASSIFIER_NOT_FOUND404No task model with that id on your account, including one that expired or was deleted: details.status is expired or deleted then, and the message says when and why (see Wizard).
NOT_FOUND404No such endpoint (check the path), or an id that does not exist or is not yours.
CONFLICT409A request with this Idempotency-Key is still running. Retry after Retry-After.
PAYLOAD_TOO_LARGE413More than 4,000,000 numbers (trials × channels × samples) as sent or after resampling, or a body over the size cap. Send the dataset in batches with defer_fit and /trials (how).
INVALID_REQUEST422The body failed validation. field names the problem; details.errors lists every field.
TOO_FEW_TRIALS422A task model needs at least two classes and at least five trials of each.
CLASSIFIER_FIT_FAILED422The task model could not be fitted on these trials. Not billed: no create fee.
UNITS_REQUIRED422units is unitless, normalized or unknown. The encoders need physical units: send uV, mV or V.
IDEMPOTENCY_KEY_REUSED422This Idempotency-Key was already used with a different body. Use a new key.
TASK_UNSUPPORTED422A task whose description or labels show emotion or affect recognition, a clinical or diagnostic use, identifying or looking up a specific person (biometrics), or profiling age, sex or gender, at the dry run or at create. These are refused; see what is refused and why.
NO_REFERENCE_DATA422A zero-shot task model for classes with no matching reference data, or for a task where zero-shot does not measurably work on unseen datasets. The zero-shot tasks on offer are listed on the Wizard page; otherwise send labeled trials.

Rate limits and spending caps

Every response to an authenticated request says where you stand. Read these headers to slow down before you are refused:

HeaderDescription
x-ratelimit-limit-requestsRequests allowed per 10-second window.
x-ratelimit-remaining-requestsRequests left in the current window.
x-ratelimit-reset-requestsTime until the window resets, e.g. 3.2s.
CodeStatusMeaningRetry-After
RATE_LIMITED429Too many requests in a short period: one second (burst) or ten seconds (sustained).yes
CONCURRENCY_LIMITED429Too many requests in flight at once.yes
USAGE_CAP_EXCEEDED429A daily or monthly usage cap on your account was reached, or the daily allowance of estimates (dry runs).yes: seconds until the next day or month; do not retry in a loop
INSUFFICIENT_CREDIT402Insufficient credits. Add credit under Billing to resume. An estimate is refused this way too when the balance cannot cover the create fee and the first month of hosting.no
BUDGET_EXCEEDED402Your own daily or monthly spending cap was reached.no — raise it under Limits

Your limits are on the dashboard and in GET /v1/usage under limits. Need more? Email us.

Server errors

CodeStatusMeaning
INTERNAL500Our fault. Not charged. Retry, and send us the request_id if it keeps happening.
BACKEND_FAILURE502The model failed on our side. Not charged. Retry, and send us the request_id if it keeps happening.
POOL_UNAVAILABLE503The model is starting up or briefly unreachable. Retry after Retry-After.
POOL_SATURATED503The model is at capacity. Back off and retry.
SERVING_DISABLED503Serving is paused for maintenance. Retry after Retry-After.
BILLING_UNAVAILABLE503Billing is briefly unavailable (dashboard billing actions). Try again later.

Retrying

  • Retry 409, 5xx, and 429 with code RATE_LIMITED or CONCURRENCY_LIMITED, after the Retry-After seconds, backing off exponentially if it is absent.
  • Do not retry USAGE_CAP_EXCEEDED in a loop: its Retry-After is the time until the next day or month.
  • Do not retry other 4xx errors unchanged: fix what alternatives says first.
  • Send an Idempotency-Key header (any unique string, for example a UUID) on POST /v1/task-models, /trials, /fit and /predictions, and reuse it, with the same body, on every retry of that request: the API answers it once and bills it once. A retry while the first is still running is 409 CONFLICT; the same key with a different body is 422 IDEMPOTENCY_KEY_REUSED.
  • Use a generous timeout. A prediction normally answers in seconds; a create tests several methods on your trials and takes longer, and any request can take up to about 30 seconds more right after we deploy.
Refused requests are recorded on your usage page with their code, and they cost nothing.