This API translates text as queued jobs, one target language per job. Submit text, keep the returned job ID and fetch the job until processing has finished. Send every request over HTTPS with credentials for your account, either HTTP Basic credentials as shown in the examples or an API token, as described in Authentication. Keep credentials on your server.
Endpoints
Two API hosts, https://api.multi.tel and the compatibility host https://api.multitel.net, share the same account-scoped jobs, idempotency keys, limits and billing. They differ in response envelope, so use one host consistently with a matching parser.
| Operation | Method and path |
|---|---|
| Service information | GET /v3/translate |
| Submit a job | POST /v3/translate |
| Estimate without creating a job | POST /v3/translate/estimate |
| Read a job | GET /v3/translate/JOB_ID |
| Settle a job awaiting payment | POST /v3/translate/JOB_ID/settle |
| Usage | GET /v3/translate/usage |
On api.multi.tel, every path is also available with the prefix /v1/translate in place of /v3/translate. On api.multitel.net, use the /v3/translate paths.
The service information response reports enabled, paidEnabled, mode (batch), realtime (false), maxSourceCharacters (20000), cpuLanguages and ratesNanosPerCharacter. cpuLanguages lists the base language codes accepted for standard translation. ratesNanosPerCharacter gives the configured price per source code point for each engine, in billionths of a US dollar: a value of 5000 means $5 per million code points. The response does not report whether enhanced translation is currently available; an enhanced estimate returns 503 when it is not. Reading it is free.
Translate text
Send JSON with Content-Type: application/json to POST https://api.multi.tel/v3/translate. Each job has one target language, so submit a separate job for each target.
| Field | Meaning |
|---|---|
text | Required. Source text of 1 to 20,000 Unicode code points, including whitespace and line breaks, that is not only whitespace. Some emoji, and accented letters written with combining marks, contain more than one code point. Byte length is not the measure, and text is not normalized before counting. Longer text is rejected with 400 on this host. |
source_language | Source locale code. Required for cpu: without it, the request returns 422 translation_language_pair_unavailable. Optional for enhanced; if omitted, sourceLocale in the job and result is empty and the same-language check below does not apply. Providing it is recommended. To omit it, leave the field out; this host rejects an empty string. |
target_language | Required target locale code, such as es or fr-CA. |
engine | Required: cpu for standard translation or enhanced for enhanced translation. |
Locale codes are BCP 47 tags of 2 to 63 letters, digits and hyphens. They are canonicalized, so en-us is recorded as en-US. Invalid tags, and und, mul or zxx, return 400 invalid_translation_language. Standard availability is checked by base language, and nb and nn count as no. A regional target such as fr-CA is accepted and recorded, but standard output follows the base language and is not guaranteed to reflect the region.
Keep the request body within 128 KiB on this host. Send text as UTF-8 rather than escaping every non-ASCII character as a \u sequence, which can push a 20,000-character request over the limit.
The body accepts only these four fields. Any other property is rejected with 400 invalid_translation_request, including idempotency_key, owner IDs, free-processing flags and segment arrays.
Idempotency and reuse
Each submission requires an Idempotency-Key header of 1 to 190 characters. Use ASCII letters, digits, -, _, . and :, without leading or trailing spaces; a missing key or a key of 191 or more characters is rejected with 400. Keys are scoped to your account and to translation, across both hosts and all credentials of your account.
- The same key with the same text, canonical languages and engine returns the existing job with HTTP 200 and
duplicate: true, without another charge. - The same key with different text, languages or engine returns 409
idempotency_conflict. The same key also returns 409 if the processing version has changed since its job was created. In that case, read the original job by its ID instead of resubmitting. - A new key with the same text, canonical languages and engine returns the existing job with HTTP 200 and
duplicate: true, with no new processing charge, if a job with that content and the current processing version is queued, running, completed or awaiting payment. A reused job that is awaiting payment still needs settlement. Failed jobs are not reused: a new job is created and charged when it completes.
API_USER="your-api-username"
API_PASSWORD="your-api-password"
curl --fail-with-body --silent --show-error --user "$API_USER:$API_PASSWORD" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: translation-example-001" \
--data '{"text":"Please move my appointment to Tuesday at 3 PM, not 11 AM.","source_language":"en","target_language":"es","engine":"cpu"}' \
-o translation-job.json \
https://api.multi.tel/v3/translate
node -p 'JSON.parse(require("node:fs").readFileSync("translation-job.json","utf8")).data.id'A new job returns HTTP 202. The example text has 57 code points, so at $5 per million code points it costs $0.000285. An abridged response (the identifier is an example):
{
"status": "success",
"data": {
"id": "8b2e4c71-5d3a-4f09-a6e2-1c7f9d3b5a28",
"status": "queued",
"mode": "batch",
"realtime": false,
"engine": "cpu",
"sourceLocale": "en",
"targetLocale": "es",
"sourceCharacters": 57,
"costUsd": "0.000285000",
"costIsEstimate": true,
"pricePerMillionCharactersUsd": "5.000000000",
"progress": null
}
}A submission can also return 402 insufficient_balance when a prepaid balance cannot cover the job together with your other outstanding jobs, and 429 translation_queue_full when your account or the service has too many outstanding jobs. It returns 503 in these cases:
paid_translation_not_enabledwhile paid translation is disabled.translation_unavailablefor an enhanced request while enhanced translation is unavailable.translation_pricing_unavailablewhile the chosen engine has no configured rate.
Retry 429 and 503 responses later with the same key.
Choose the quality
Long text is processed in smaller pieces and joined again. Line breaks and spacing can change, and a sentence can lose context across a processing boundary. Keep the original text and review the assembled result. Standard translation uses the base language of a regional tag; accepting and recording a tag does not guarantee regional wording.
Standard translation runs on CPU workers. Enhanced translation sends text to the configured external AI service. Its output still needs review; the service does not guarantee that one engine is more accurate for every language pair or type of text.
Standard translation is a draft whose quality and processing time vary by language pair. Enhanced translation is intended for text where context matters, but it still needs review for important names, numbers, negations and commitments. Neither option repairs errors already present in an original transcript, so keep the original for comparison.
Use GET /v3/translate on the same API host to check current availability, supported standard languages and configured rates. A standard request whose source or target base language is not in cpuLanguages returns 422 translation_language_pair_unavailable. When source_language is given and has the same base language as the target, the request returns 409 translation_same_language instead of creating a job. Enhanced translation is enabled separately: while it is unavailable, enhanced submissions and estimates return 503.
Estimate first
On api.multi.tel, send the same JSON body to POST /v3/translate/estimate. No idempotency key is required. An estimate applies the same validation, including the 422 and 409 checks, but does not create a job, translate text, reserve funds, check your balance or charge your account. It returns HTTP 200 with estimate: true, charged: false, mode, realtime, engine, sourceCharacters, sourceLocale, targetLocale, pricePerMillionCharactersUsd and costUsd under data. For the example above, costUsd is 0.000285000 at the standard rate. While enhanced translation is unavailable, an enhanced estimate returns 503 service_unavailable. Estimates use the currently configured rate. A job's rate is fixed when the job is created, and its charge uses that rate.
Retrieve the translation
A new submission returns HTTP 202, and an existing or reused job returns HTTP 200. Keep data.id and use the shared job workflow. The job object contains these fields:
id,status,engine,sourceLocaleandtargetLocale.sourceCharacters,costUsd,costIsEstimateandpricePerMillionCharactersUsd.progressandcreatedAt.modelVersion, an opaque processing-version string.duplicateon reused responses.failureCodefor failed jobs:translation_failed,translation_timeout,input_failedorworker_lease_lost.
A failed job shows a costUsd of zero.
A completed job has data.result with sourceLocale (empty if no source language was given), targetLocale, the translated text (segment texts joined with line breaks), and segments. For text submitted through this API, segments contains one entry with the translated text and the target language. Results of jobs awaiting payment are withheld until settlement.
Using api.multitel.net
The compatibility endpoint POST https://api.multitel.net/v3/translate accepts the same JSON fields and the same Idempotency-Key header. If the header is absent, it also accepts an idempotency_key string in the JSON body. Any other extra field is rejected with 400 unsupported_translation_option. On this host, the raw body can be up to 256,000 bytes, and text over 20,000 code points returns 413 translation_too_large rather than 400. An empty source_language is treated as omitted. A 429 response includes Retry-After: 15. This host also returns these errors:
- 400
json_body_requiredwhen the body is missing, invalid JSON or a JSON scalar. Send a JSON object. - 400
invalid_translation_optionswhentext,target_languageorengineis missing, empty or not a string, orsource_languageis not a string. - 400
invalid_translation_textfor whitespace-only text. - 400
invalid_translation_enginefor an unknown engine. - 400
idempotency_key_requiredfor a missing, empty or overlong key, or a non-stringidempotency_key. - 400
invalid_jobfor a malformed job ID.
Responses from this host's translation service use the envelope {"status":{"code":HTTP_STATUS},"response":{...}}, where status is an object. Read the job ID from response.id and the completed result from response.result. Errors put the code in response.code, without a message or request ID. Authentication failures (401) are returned by the host's general sign-in check and can have a different body, so check the HTTP status before parsing. Jobs are shared with the primary host, but use the same host for polling, settlement and usage so that your parser matches the envelope. This host also accepts the same JSON fields at /v3/translate/estimate without an idempotency key, and its estimate is under response.