Full API reference: developer.universally.com carries the live specification for both the Translator API and the Platform API, with every field, status code and response example. This page covers what is worth knowing around it.
Send a page's HTML, get the same HTML back with its text translated. This is the endpoint the WordPress plugin uses, and the one to use for any platform that can render a page server-side.
Endpoint
POST https://translator.universally.com/v1/translate/html
X-API-Key: your_api_key_here
Content-Type: application/json
Request
{
"html": "<html>...</html>",
"targetLanguage": "es",
"sourceUrl": "https://example.com/pricing/",
"parserMode": "auto"
}
| Field | Required | Notes |
|---|---|---|
html |
yes | The full document, or a fragment. Must not be empty. |
targetLanguage |
yes | One of the project's configured variant codes, such as es, es-419 or pt-br. A region code such as es-mx is rejected |
sourceUrl |
yes | Must be a valid URL, and its domain must match the project's domain |
parserMode |
no | auto or classic. Defaults to auto. |
sourceUrl is not decoration. It identifies which page is being translated so the result can be stored and reused, and it is checked against your project's domain. A mismatch returns TRANSLATION_SOURCE_URL_DOMAIN_MISMATCH.
Response
{
"success": true,
"data": {
"translatedHtml": "<html>...</html>",
"metadata": {
"siteId": "site_123",
"siteDomain": "example.com",
"sourceLanguage": "en",
"targetLanguage": "es",
"sourceUrl": "https://example.com/pricing/",
"stringsExtracted": 142,
"stringsTranslated": 142,
"limitReached": false,
"skippedStrings": 0
}
},
"code": "DATA_FETCHED"
}
Serve translatedHtml to the visitor. The metadata is for your own logging and is worth recording:
limitReached is true when the project was already at or over its word total when the request arrived. You still get valid HTML and a 200, with source-language text wherever a new translation would have been needed. See Usage limits.
skippedStrings counts what that left untranslated. It is the only skip signal: it is set for the word limit and nothing else. Exclusion rules do not appear here, because excluded content is removed before anything is extracted. A page excluded entirely comes back with every counter at zero.
stringsExtracted against stringsTranslated routinely differ, and a gap on its own means nothing was wrong. Text appearing in more than one place (a <title> that is also an og:title and a JSON-LD headline), strings that are skipped as untranslatable (a bare URL, an email address, a number, a single character), and anything the word limit skipped all account for it.
targetLanguage echoes the variant you sent, normalised to lower case. It is matched exactly against the project's configured variants rather than resolved from a region, which is why es-mx returns TRANSLATION_TARGET_LANGUAGE_NOT_ALLOWED while es-419 works. See Language variants and regional targeting.
Compressed requests
Set Content-Encoding: gzip and send the compressed body. The size limit counts the bytes you send, so compressing is what lets a large document through.
Limits
The request body cannot exceed 5 MB as sent, which is the compressed size when you use gzip. Over that returns REQUEST_TOO_LARGE with status 413. See API errors and limits.
Caching your side
The response is stored on our side, so a repeat request for the same page and language is fast and does not spend words again. It is still a network round trip.
Cache the translated HTML in your own layer, as the WordPress plugin does. That is the difference between a fast page and a fast-ish one. See Performance and page speed.
When a page is excluded
If the path matches an Exclude Pages rule, the endpoint returns successfully with the original HTML rather than an error. Check the metadata if you need to distinguish the two. See Exclude pages.