Errors
Every HTTP status and error message the FluentC API returns, what causes it, and which ones are safe to retry.
Error shape
Errors are JSON with a single error string, and the HTTP status tells you the class of problem.
{ "error": "Target language 'es' is not enabled for this site." }
Branch on the status code first. Match on the message only for the specific cases below; the messages are quoted exactly as the API returns them.
The one exception: a batch job that couldn't be translated is not an HTTP error. Batch results returns 200 with "status": "failed" and an error string.
Status codes
| Status | Meaning | Retry? |
|---|---|---|
200 | Success. For batch results, check status. | n/a |
401 | Authentication failed. | No. Fix the key. |
403 | The key is valid but the account can't make this request. | No. Fix the account. |
422 | The request is invalid. | No. Fix the request. |
500 | Something went wrong on our side. | Yes, with backoff. |
401 Unauthorized
| Message | Cause |
|---|---|
API key (Site UUID) is missing | The Authorization header is absent or empty. |
Invalid API key: Site not found. | The key is wrong, has extra characters, or has a Bearer prefix. |
Invalid API key: Associated company not found. | The key's account can't be resolved. Contact us. |
403 Forbidden
| Message | Cause |
|---|---|
Access denied: Translations are disabled for this site. | Translation is switched off for the key's site. |
Subscription error: ... | The subscription isn't active, or has no languages. The text after the colon says which. |
Usage limit error: ... | A usage limit was reached. The text after the colon says which. |
422 Unprocessable Entity
| Message | Endpoint | Cause |
|---|---|---|
Input is missing | translate, checklanguage | input is empty or absent. |
Invalid or missing input format. Allowed formats: text, html, json | translate | input_format isn't one of the three. |
Invalid or missing input format. Allowed formats: text, html | checklanguage | input_format isn't text or html. |
Invalid or missing target language code. | translate | target_language isn't two or three lowercase letters. |
Target language 'xx' is not enabled for this site. | translate | Valid code, but not enabled for your key. |
Invalid source language code: 'xx'. | translate | source_language isn't two or three letters. |
Source language 'xx' is not enabled for this site. | translate | Valid code, but not enabled for your key. |
Invalid or missing mode. Allowed modes: batch, real-time | translate | mode is absent or unknown. |
Realtime translation is not supported for this agent. | translate | real-time on a Batch plan key. |
Agent translation is not supported for this site. | translate | The key isn't set up for the translation API. |
Invalid JSON format | translate | input_format is json but input isn't valid JSON. |
job_id (hash_key) is missing | results | The body has no job_id. |
500 Internal Server Error
{ "error": "An unexpected error occurred during authentication." }
Retry with exponential backoff. If it keeps happening, contact us with the time of the request and the job_id if there was one.
Retrying
- Do retry
500responses and network timeouts. Wait 1 second, then 2, 4, 8, up to a limit you choose, and add a little random jitter. - Back off if you hit a plan limit. Rate limits depend on your plan; the Batch plan allows 20 calls per hour. Treat
429the same way as500. - Don't retry
401,403or422unchanged. They will fail the same way. - Batch requests are safe to resubmit. The
job_idis deterministic for the same input and target language.
async function translateWithRetry(body, attempts = 4) {
for (let i = 0; i < attempts; i++) {
const res = await fetch("https://api.fluentc.ai/ai_agent/translate", {
method: "POST",
headers: {
Authorization: process.env.FLUENTC_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (res.ok) return res.json();
const retryable = res.status === 429 || res.status >= 500;
if (!retryable || i === attempts - 1) {
const { error } = await res.json().catch(() => ({}));
throw new Error(`${res.status}: ${error ?? res.statusText}`);
}
const delay = 1000 * 2 ** i + Math.random() * 250;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
Something unclear, or missing? Tell us.