Universally exposes a REST API for translating web content into 110+ languages. The API is platform-agnostic: any system that can make an HTTPS request can send content and receive translations back. The official WordPress plugin is built on top of this same API, but nothing here is WordPress-specific.
Use the API when you want to integrate translation directly into your own stack: a custom CMS, a headless frontend, an edge worker, a mobile or web app, or any place where the plugin cannot reach.
But as we currently support only WordPress integration officially, this is more a general overview of the api which should allow to extend the plugin.
Two services
The API is split across two separate hosts. Each owns a distinct set of endpoints, so the host tells you which capability you are calling.
Translator service
https://translator.universally.com
This is where all translation happens. Most integrations only ever call this service.
| Method | Endpoint | Description |
|---|---|---|
POST | /v1/translate/html | Translate a full HTML document |
POST | /v1/translate/strings | Translate an array of strings (no URL required) |
POST | /v1/translate | Auto-detects HTML or strings from the request body |
GET | /health | Health check (no auth) |
API service
https://api.universally.com
This service provides account-level reference data. You do not need it to translate; it supports setup and language selection.
This currently is very limited for public use.
Authentication in brief
Every request is authenticated with a single API key sent in the X-API-Key header. Your key is a 64-character string issued per site from your Universally dashboard, and the same key works on both services. Because the key can spend your translation allowance, keep it server-side.
X-API-Key: your_api_key_here
Content-Type: application/json
Response envelope
Every endpoint returns the same JSON envelope, whether the request succeeds or fails:
{
"success": true,
"data": { },
"message": "Human-readable message or null",
"code": "MACHINE_READABLE_CODE"
}
successistruefor 2xx responses andfalsefor errors.dataholds the payload on success and isnullon error.codeis a stable, machine-readable identifier you can branch on. Prefer it over parsingmessage.
HTML or strings: which endpoint do I use?
The Translator offers two ways to translate, depending on the shape of your content.
Use /v1/translate/html when you have a rendered page. You send a complete HTML document along with its canonical URL. Universally extracts every translatable string, including body text, meta tags, Open Graph data, structured data, image alt text, and form attributes, translates them, localizes links, and returns the rewritten HTML. This is the right choice for server-rendered pages and edge integrations.
Use /v1/translate/strings when you have discrete pieces of text. You send an array of strings and receive a map of original to translated text. No URL is required, so this is the right choice for content that is not tied to a page: dynamic content loaded over AJAX, user-generated text such as comments and reviews, app content, push notifications, or any fragment your rendering pipeline produces on the fly.
If you are unsure, send the request to /v1/translate and Universally will dispatch to the correct handler based on the body you provide.
Examples
Both translation methods live on the Translator service and authenticate with your API key in the X-API-Key header.
Translate HTML
Send a rendered page and its canonical URL; receive the translated document back.
curl -X POST https://translator.universally.com/v1/translate/html \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"html": "<!DOCTYPE html><html><head><title>Welcome</title></head><body><h1>Hello world</h1></body></html>",
"targetLanguage": "fr",
"sourceUrl": "https://example.com/welcome"
}'
Response:
{
"success": true,
"data": {
"translatedHtml": "<!DOCTYPE html><html lang=\"fr\"><head><title>Bienvenue</title></head><body><h1>Bonjour le monde</h1></body></html>",
"metadata": {
"siteId": "site_123",
"siteDomain": "example.com",
"sourceLanguage": "en-us",
"targetLanguage": "fr",
"sourceUrl": "https://example.com/welcome/",
"stringsExtracted": 2,
"stringsTranslated": 2,
"limitReached": false,
"skippedStrings": 0
}
},
"message": "Data retrieved successfully.",
"code": "DATA_FETCHED"
}
Translate strings
Send an array of strings; receive a map of original to translated text. No URL required.
curl -X POST https://translator.universally.com/v1/translate/strings \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"strings": ["Welcome back", "Add to cart"],
"targetLanguage": "fr"
}'
Response:
{
"success": true,
"data": {
"translations": {
"Welcome back": "Bon retour",
"Add to cart": "Ajouter au panier"
},
"metadata": {
"sourceLanguage": "en-us",
"targetLanguage": "fr",
"stringsReceived": 2,
"stringsTranslated": 2,
"limitReached": false,
"skippedStrings": 0
}
},
"message": "Data retrieved successfully.",
"code": "DATA_FETCHED"
}
How caching works
Universally caches every translation it produces, so you are never billed twice for the same content.
- HTML translations are cached per page and language. Repeat requests for the same URL and target language return the cached result instantly, and only newly changed strings are sent for translation.
- String translations are cached per individual string. Once a string is translated for a given language, it is served from cache on every future request, no matter which page or context it appears in.
You do not need to manage this. Send your content on every request and Universally returns cached translations where available and translates only what is new.