Error codes reference
Jeszcze nie przetłumaczono — wyświetlono po angielsku.
Every failure is a 4xx or 5xx response with an application/problem+json body as defined in RFC 9457. The code field is the contract: it never changes meaning. The detail text is written for humans and may be reworded at any time, so do not match on it.
The problem document
{
"type": "https://langapi.xyz/docs/errors#text-too-long",
"title": "Text too long",
"status": 400,
"code": "text_too_long",
"detail": "Request contains 61204 characters; your plan allows 50000 per request.",
"request_id": "01JAX3M1P5R7T9V1X3Z5B7D9F1",
"max_characters": 50000,
"characters": 61204
}
type, title, status, code, detail and request_id are always present. Some codes append further fields; treat unknown fields as optional.
Status codes at a glance
| Status | code | Meaning |
|---|---|---|
| 400 | validation_error | A parameter is missing, malformed or inconsistent |
| 400 | invalid_target_lang | target_lang is not a supported target |
| 400 | invalid_source_lang | source_lang is not a supported source |
| 400 | too_many_texts | text has more than 50 entries |
| 400 | text_too_long | The request exceeds your plan's characters per request |
| 400 | bad_request | Dashboard only: an unknown checkout kind or plan was submitted |
| 401 | unauthorized | Missing, malformed or unknown API key |
| 401 | key_revoked | The key has been revoked |
| 402 | quota_exceeded | Included volume used and the overage cap reached |
| 402 | credit_exhausted | Prepaid balance too low for this request |
| 403 | forbidden | Dashboard only: not signed in, no team selected or insufficient role |
| 403 | glossary_limit_reached | Your plan's glossary count is used up |
| 404 | not_found | No such endpoint or resource |
| 404 | glossary_not_found | No glossary with that id in your team |
| 405 | method_not_allowed | Wrong HTTP method for this path |
| 419 | csrf_invalid | Dashboard only: a form was submitted with a missing or stale token |
| 429 | rate_limited | Requests per second exceeded |
| 500 | internal_error | An unexpected failure on our side |
| 503 | engine_unavailable | The translation engine did not deliver a result |
| 503 | service_unavailable | A supporting service is temporarily down |
Extra fields and headers by code
code | Additional fields | Header |
|---|---|---|
too_many_texts | max_texts | — |
text_too_long | max_characters, characters | — |
unauthorized | — | WWW-Authenticate: Bearer realm="langapi" |
quota_exceeded | character_limit, overage_cap_minor, period_end | — |
credit_exhausted | balance_minor, required_minor, currency | — |
glossary_limit_reached | max_glossaries | — |
method_not_allowed | — | Allow listing the accepted methods |
rate_limited | limit | Retry-After in seconds |
engine_unavailable | — | Retry-After: 5 |
service_unavailable | — | Retry-After: 5 |
Code by code
bad_request
Raised by the dashboard's checkout pages when the submitted plan or checkout kind is unknown. The API endpoints under /v1 do not produce it; a malformed API request is reported as validation_error.
credit_exhausted
Your prepaid balance does not cover the characters in this request. The document tells you the balance and what the request would have cost, both in minor currency units. Top up in the dashboard; the request itself was not charged.
csrf_invalid
Status 419. Raised by dashboard forms whose anti-forgery token is missing or stale, never by the API. Reload the page and submit again.
engine_unavailable
The engine was tried twice and did not return a usable translation. The reservation has been released. Wait for Retry-After seconds and resend with exponential backoff.
forbidden
A dashboard code: the visitor is not signed in, has no team selected, is not a member of the team, or lacks the role a page requires. API keys are never subject to role checks, so /v1 requests do not receive it.
glossary_limit_reached
The team already has as many glossaries as the plan allows. Delete one that is no longer used, or move to a plan with a higher limit.
glossary_not_found
The id is not a valid ULID, or no glossary with that id belongs to your team. Glossaries of other teams are indistinguishable from non-existent ones.
internal_error
Something failed that should not have. Retry once; if it persists, contact support and include the request_id.
invalid_source_lang
The source_lang value is not one of the 36 base codes. Note that regional variants are not valid sources.
invalid_target_lang
The target_lang value is not one of the 38 target codes. Fetch GET /v1/languages?type=target for the list.
key_revoked
The key was recognised but has been revoked. Deploy the current key; see API keys and authentication for a rotation procedure that avoids this.
method_not_allowed
The path exists, the method does not. The Allow header names the methods that do.
not_found
The path matches no endpoint. This is answered before authentication, so it does not tell you whether the key was valid.
quota_exceeded
The period's included characters are used up and the overage cap has been reached. Raise the cap in the dashboard or wait for period_end.
rate_limited
More requests in the last second than the plan allows. limit in the document and X-RateLimit-Limit in the header tell you the number. Wait for Retry-After and try again; see Request rate limits.
service_unavailable
A backing service other than the engine — for example the store behind the rate limiter and quota counters — could not be reached. Retry after a few seconds.
text_too_long
The sum of characters across all entries of text exceeds the per-request limit of your plan. Split the batch; the document reports the limit and the count you sent.
too_many_texts
More than 50 entries in text. Send several requests.
unauthorized
No key, a key that does not match the la_live_/la_test_ pattern, or a key that is not in our records. Check the header format first.
validation_error
The catch-all for parameter problems: an empty string in text, an unknown formality value, more/less on a target without formality support, context longer than 2,000 characters, a glossary without source_lang or with a mismatching pair, a malformed JSON body. The detail names the field.
What to retry
| Outcome | Codes |
|---|---|
| Retry with backoff | rate_limited, engine_unavailable, service_unavailable, internal_error |
| Do not retry unchanged | All other 4xx — the same request will fail the same way |
A request that fails is never charged: 429 and 503 are rejected before or after the reservation, and in the latter case the reservation is rolled back.
A minimal retry wrapper
const RETRY = new Set(['rate_limited', 'engine_unavailable', 'service_unavailable', 'internal_error']);
async function callTranslate(payload, key, maxAttempts = 5) {
for (let n = 0; n < maxAttempts; n++) {
const res = await fetch('https://api.langapi.xyz/v1/translate', {
method: 'POST',
headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (res.ok) return res.json();
const problem = await res.json();
if (!RETRY.has(problem.code)) {
throw new Error(`${problem.code} (${problem.request_id}): ${problem.detail}`);
}
const seconds = Number(res.headers.get('Retry-After') ?? 2 ** n);
await new Promise((r) => setTimeout(r, seconds * 1000 + Math.random() * 250));
}
throw new Error('translate: retries exhausted');
}
Keep the request id
Successful and failed responses alike carry X-Request-Id, and problem documents repeat it as request_id. Log it alongside your own correlation id. It is the one piece of information that lets us locate a specific request.
Engine errors stay internal
Messages, status codes and headers from the translation engine are never passed through to you. An engine failure is logged on our side without your text and surfaces to you only as engine_unavailable.
Zmieniono 7 wrz 2026, 00:00