API errors and rate limits
Every error code the API can return, what each one means, and the rate limits and plan limits that apply to a key.
The error shape
Every failure comes back in the same envelope, with the HTTP status telling you the broad category and code telling you exactly what happened:
{
"error": {
"code": "invalid_card",
"message": "cards[3].target must be at most 2000 characters.",
"param": "cards[3].target",
"request_id": "req_8fKd2mXp4LvNc7Bh"
}
}
codeis stable and safe to branch on in your script.messageis written for a human and may be reworded over time, so do not match on it.paramnames the exact field, including its index in a batch.request_idalso appears in our logs. Quote it if you contact support and we can find the exact request.
Error codes
Authentication - 401
| Code | Meaning |
|---|---|
invalid_api_key | The key is missing, malformed, or not one we issued |
revoked_api_key | The key was revoked. Create a new one in settings |
expired_api_key | The key passed its expiry date |
These are kept distinct on purpose. You already hold the key, so telling you why it failed turns a confusing dead end into something you can fix yourself.
Permissions - 403
| Code | Meaning |
|---|---|
insufficient_scope | The key lacks the scope this endpoint needs. The message names it |
deck_not_owned | The deck exists but belongs to another account |
account_pending_deletion | The account is scheduled for deletion |
Not found - 404
| Code | Meaning |
|---|---|
deck_not_found | No deck with that doc_id |
not_found | No such endpoint |
unknown_api_version | The version in the URL does not exist. The message lists the ones that do |
Bad request - 400
| Code | Meaning |
|---|---|
invalid_request | A required field is missing, or a value is the wrong type or too long |
invalid_card | One card in the batch is invalid. param names which |
invalid_custom_field | A custom field key is not defined on the deck, or its value is invalid |
invalid_language | A language code is not in a recognised form |
batch_too_large | More than 100 cards in one request |
field_not_patchable | You tried to change a field the API does not let you change |
deck_not_empty | A language change was attempted on a deck that already has cards |
invalid_cursor | The pagination cursor is not one we issued |
unknown_field / unknown_parameter | A field or query parameter we do not recognise |
invalid_json | The body is not valid JSON |
idempotency_key_reuse | The same Idempotency-Key was used for a different request |
Unknown fields are refused rather than ignored, which means a typo tells you immediately instead of quietly doing nothing.
Limits - 402, 413, 429
| Code | Status | Meaning |
|---|---|---|
quota_exceeded | 402 | You reached your plan's deck or card limit |
payload_too_large | 413 | The request body is over 256 KB |
rate_limited | 429 | Too many requests. See the rate limits below |
Everything else - 415, 503, 500
| Code | Status | Meaning |
|---|---|---|
unsupported_media_type | 415 | The body was not sent as application/json |
method_not_allowed | 405 | Wrong HTTP method for that path |
temporarily_unavailable | 503 | The API is paused for maintenance. Retry after the interval in Retry-After |
internal | 500 | Something broke on our side. Quote the request_id |
A 503 means paused, not lost - nothing was written, and the request is safe to retry.
Rate limits
Limits are counted per key, not per account, so one misbehaving script cannot starve the others.
- 60 requests per minute
- 5000 requests per day
Creating a deck counts for more than a read, because it is the most expensive thing a runaway loop can do.
Every response - not just a 429 - carries the current state:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed in the current window |
X-RateLimit-Remaining | How many you have left |
X-RateLimit-Reset | When the window resets, as a Unix timestamp in seconds |
Headers that only appear once you have already been throttled are no use for avoiding it, which is why they are always there. On a 429 you also get Retry-After, in seconds.
Backing off
Wait for the interval in Retry-After and try again. For everything else, exponential backoff with a few retries is the right pattern - and pair it with an Idempotency-Key on writes so a retry after a timeout cannot duplicate anything.
Do not retry a 4xx other than 429: the request will fail the same way until you change it.
Plan limits
Rate limits cap how fast you can go. Plan limits cap how much you can store, and they are the same limits the web app enforces:
| Free | Premium | |
|---|---|---|
| Decks | 100 | Unlimited |
| Cards | 10,000 | Unlimited |
Crossing one returns 402 quota_exceeded, with a message saying how many you have and how many you can still add. Nothing is written when a request is rejected for quota, so a 402 never leaves you in a half-finished state.
See free plan limits for the full picture, or Yalango Premium to lift them.