Request and Retrieve Translations via the API
Use this API to send content for translation, track its progress, and retrieve the translations in all target languages.
This API accepts JSON-structured content, preserving the original structure and keys. It translates only text values, leaving numbers, booleans, nulls, and other non-text values unchanged.
API Quick Links
Create Content Translations
Creates a new translation job from JSON-structured data.
The endpoint preserves your content’s original hierarchy of keys and arrays, translating only text values while leaving numbers, booleans, nulls, and other non-text values unchanged.
It is especially useful for:
- Content management – Localizing dynamic content structured in JSON
- Configuration files – Translating user-facing strings in config data
- API responses – Translating structured response payloads
- Documentation – Localizing hierarchical help content or guides
For a complete Rails implementation of this workflow, see translating dynamic content in Rails using the PTC API.
HTTP Request
POST https://app.ptc.wpml.org/api/v1/content_translationParameters
| Parameter | Type | Required | Description |
|---|---|---|---|
data |
object | Yes | The JSON-structured data to translate. It can include nested objects, arrays, and string values. |
name |
string | No | A human-readable name for the translation job. If omitted, one is generated automatically. |
callback_url |
string | No | The URL that receives webhook notifications when the translation is complete. |
target_languages |
array[string] | No | The array of ISO codes for target languages. If omitted, translations are created for all project-configured languages. See the Available Target Languages API for more information. |
Request Body Example
{
"data": {
"app": {
"title": "My Application",
"navigation": {
"home": "Home",
"about": "About Us",
"contact": "Contact"
},
"buttons": {
"save": "Save",
"cancel": "Cancel",
"submit": "Submit"
},
"messages": {
"welcome": "Welcome to our platform",
"error": "An error occurred"
}
},
"version": "1.0.0",
"settings": {
"theme": "dark",
"notifications": true
}
},
"name": "App UI Translations",
"callback_url": "https://your-app.com/webhooks/translation-complete",
"target_languages": ["es", "fr", "de"]
}Responses
Success Response
{
"id": 123,
"name": "App UI Translations",
"status": "queued",
"created_at": "2024-01-15T10:30:00.000Z",
"updated_at": "2024-01-15T10:30:00.000Z"
}Response Schema
| Field | Type | Description |
|---|---|---|
id |
number | The unique identifier of the content translation job. |
name |
string | The job name (auto-generated if not provided). |
status |
string | The current job status (queued, processing, completed). |
created_at |
string | The ISO 8601 timestamp indicating when the job was created. |
updated_at |
string | The ISO 8601 timestamp indicating when the job was last updated. |
Error Responses
Invalid JSON Data
{
"errors": {
"data": ["Data must be a valid JSON object"]
}
}Invalid Target Languages
{
"errors": {
"target_languages": ["Language codes [zh, xx] are not configured for this project"]
}
}Unauthorized
{
"error": "Unauthorized access. Please provide a valid API token."
}Forbidden
{
"error": "Access denied. Insufficient permissions."
}JSON Data Processing
When working with JSON-structured data, PTC processes the data as follows:
- Structure is preserved – The original hierarchy of keys and nesting remains unchanged
- Only strings are translated – Numbers, booleans, arrays, and null values are kept as they are
- Path-based translation – Each translatable string is identified by its JSON path
- Supports nesting – Works with deeply nested objects and arrays
- Handles mixed data types – Non-string values are preserved without modification
Example Data Transformation
Input:
{
"user": {
"name": "Welcome User",
"settings": {
"theme": "Choose Theme",
"count": 5,
"enabled": true
}
}
}Processing outcome:
user.name: “Welcome User” → Gets translateduser.settings.theme: “Choose Theme” → Gets translateduser.settings.count:5 → Remains unchangeduser.settings.enabled:true → Remains unchanged
Translation Workflow
- Validation: JSON structure and target languages are checked
- Source file preparation: JSON is converted to an internal source format
- String reuse through translation memory: All translatable strings are extracted and stored in your project’s translation memory so previous translations can be reused
- Job queuing: A job is queued for each target language
- Processing: Automatic translation runs on the extracted strings
- Callback (optional): A webhook is sent when all translations are completed, if
callback_urlis provided
Webhook Callback
When a callback_url is provided, a POST request is sent when the job is completed.
Callback request body:
{
"id": 1,
"status": "completed",
"translations_url": "https://app.ptc.wpml.org/api/v1/content_translation/1"
}Supported Data Types
| JSON Type | Translation Behavior |
|---|---|
string |
Translated to target languages |
number |
Preserved as-is |
boolean |
Preserved as-is |
null |
Preserved as-is |
array |
Processed recursively |
object |
Processed recursively |
Example Requests
Basic JSON translation:
curl -X POST "https://app.ptc.wpml.org/api/v1/content_translation" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"welcome": "Welcome",
"buttons": {
"save": "Save",
"cancel": "Cancel"
}
},
"name": "UI Labels"
}'With specific target languages:
curl -X POST "https://app.ptc.wpml.org/api/v1/content_translation" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"data": {
"title": "Product Catalog",
"categories": {
"electronics": "Electronics",
"clothing": "Clothing"
}
},
"target_languages": ["es", "fr"],
"callback_url": "https://myapp.com/webhook"
}'Code Examples
curl -X POST "https://app.ptc.wpml.org/api/v1/content_translation" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"data":{"app":{"title":"My Application","navigation":{"home":"Home","about":"About Us"}}},"name":"App Translations","target_languages":["es","fr","de"],"callback_url":"https://your-app.com/webhooks/complete"}'
# The response includes the job "id". Poll its status (status stays
# "in_progress" until done — it is not set on the create response):
curl -X GET "https://app.ptc.wpml.org/api/v1/content_translation/CONTENT_TRANSLATION_ID/status" \
-H "Authorization: Bearer YOUR_API_TOKEN"require 'net/http'
require 'uri'
require 'json'
uri = URI('https://app.ptc.wpml.org/api/v1/content_translation')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
data: { app: { title: 'My Application',
navigation: { home: 'Home', about: 'About Us' } } },
name: 'App Translations',
target_languages: %w[es fr de],
callback_url: 'https://your-app.com/webhooks/complete'
}.to_json
response = http.request(request)
id = JSON.parse(response.body)['id']
puts "Translation job created with ID: #{id}"
# Poll the status endpoint for progress (status is set there, not on create)
status_uri = URI("https://app.ptc.wpml.org/api/v1/content_translation/#{id}/status")
status_request = Net::HTTP::Get.new(status_uri)
status_request['Authorization'] = 'Bearer YOUR_API_TOKEN'
status = JSON.parse(http.request(status_request).body)
puts "Status: #{status['status']} (#{status['completeness']}% complete)"import requests
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
payload = {
"data": {"app": {"title": "My Application",
"navigation": {"home": "Home", "about": "About Us"}}},
"name": "App Translations",
"target_languages": ["es", "fr", "de"],
"callback_url": "https://your-app.com/webhooks/complete"
}
response = requests.post('https://app.ptc.wpml.org/api/v1/content_translation', headers=headers, json=payload)
translation_id = response.json()['id']
print(f"Translation job created with ID: {translation_id}")
# Poll the status endpoint for progress (status is set there, not on create)
status_response = requests.get(f'https://app.ptc.wpml.org/api/v1/content_translation/{translation_id}/status', headers=headers)
status = status_response.json()
print(f"Status: {status['status']} ({status['completeness']}% complete)")<?php
$payload = [
'data' => ['app' => ['title' => 'My Application',
'navigation' => ['home' => 'Home', 'about' => 'About Us']]],
'name' => 'App Translations',
'target_languages' => ['es', 'fr', 'de'],
'callback_url' => 'https://your-app.com/webhooks/complete'
];
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://app.ptc.wpml.org/api/v1/content_translation',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_TOKEN',
'Content-Type: application/json'
],
]);
$response = curl_exec($curl);
curl_close($curl);
$id = json_decode($response, true)['id'];
echo "Translation job created with ID: " . $id . "\n";
// Poll the status endpoint for progress (status is set there, not on create)
$statusCurl = curl_init();
curl_setopt_array($statusCurl, [
CURLOPT_URL => "https://app.ptc.wpml.org/api/v1/content_translation/{$id}/status",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer YOUR_API_TOKEN'],
]);
$status = json_decode(curl_exec($statusCurl), true);
curl_close($statusCurl);
echo "Status: " . $status['status'] . " (" . $status['completeness'] . "% complete)";
?>import okhttp3.*;
OkHttpClient client = new OkHttpClient();
MediaType JSON = MediaType.parse("application/json");
String payload = "{"
+ "\"data\":{\"app\":{\"title\":\"My Application\",\"navigation\":{\"home\":\"Home\",\"about\":\"About Us\"}}},"
+ "\"name\":\"App Translations\",\"target_languages\":[\"es\",\"fr\",\"de\"],"
+ "\"callback_url\":\"https://your-app.com/webhooks/complete\"}";
Request request = new Request.Builder()
.url("https://app.ptc.wpml.org/api/v1/content_translation")
.addHeader("Authorization", "Bearer YOUR_API_TOKEN")
.post(RequestBody.create(payload, JSON))
.build();
Response response = client.newCall(request).execute();
System.out.println(response.body().string());package main
import (
"bytes"
"fmt"
"io"
"net/http"
)
func main() {
payload := []byte(`{
"data": {"app": {"title": "My Application",
"navigation": {"home": "Home", "about": "About Us"}}},
"name": "App Translations",
"target_languages": ["es", "fr", "de"],
"callback_url": "https://your-app.com/webhooks/complete"
}`)
req, _ := http.NewRequest("POST", "https://app.ptc.wpml.org/api/v1/content_translation", bytes.NewBuffer(payload))
req.Header.Add("Authorization", "Bearer YOUR_API_TOKEN")
req.Header.Add("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "YOUR_API_TOKEN");
var payload = @"{
""data"": {""app"": {""title"": ""My Application"",
""navigation"": {""home"": ""Home"", ""about"": ""About Us""}}},
""name"": ""App Translations"",
""target_languages"": [""es"", ""fr"", ""de""],
""callback_url"": ""https://your-app.com/webhooks/complete""
}";
var body = new StringContent(payload, Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://app.ptc.wpml.org/api/v1/content_translation", body);
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);const axios = require('axios');
const payload = {
data: {
app: {
title: "My Application",
navigation: { home: "Home", about: "About Us" }
}
},
name: "App Translations",
target_languages: ["es", "fr", "de"],
callback_url: "https://your-app.com/webhooks/complete"
};
const response = await axios.post('https://app.ptc.wpml.org/api/v1/content_translation', payload, {
headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
});
const translationId = response.data.id;
console.log('Translation job created with ID:', translationId);
// Poll the status endpoint for progress (status is set there, not on create)
const status = await axios.get(`https://app.ptc.wpml.org/api/v1/content_translation/${translationId}/status`, {
headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
});
console.log(`Status: ${status.data.status} (${status.data.completeness}% complete)`);Get Content Translations
Retrieves the original content and all translated versions for a specific content translation job.
The response preserves your input structure: it returns a source object plus one object per target language (keyed by language code such as es, fr, de).
HTTP Request
GET https://app.ptc.wpml.org/api/v1/content_translation/{id}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | The unique identifier of the content translation job to retrieve. |
Responses
Success Response
{
"source": {
"app": {
"title": "My Application",
"navigation": {
"home": "Home",
"about": "About",
"contact": "Contact"
},
"buttons": {
"save": "Save",
"cancel": "Cancel"
}
}
},
"es": {
"app": {
"title": "Mi Aplicación",
"navigation": {
"home": "Inicio",
"about": "Acerca de",
"contact": "Contacto"
},
"buttons": {
"save": "Guardar",
"cancel": "Cancelar"
}
}
},
"fr": {
"app": {
"title": "Mon Application",
"navigation": {
"home": "Accueil",
"about": "À propos",
"contact": "Contact"
},
"buttons": {
"save": "Enregistrer",
"cancel": "Annuler"
}
}
}
}Response Schema
| Field | Type | Description |
|---|---|---|
source |
object | The original source content in the same nested structure as submitted. |
{language_code} |
object | The translated content for each target language, keyed by its ISO code (for example es, fr, de), with the same structure as source. For more information, see the Available Target Languages API. |
Error Responses
Content Translation Not Found
{
"error": "Content translation not found"
}Unauthorized
{
"error": "Unauthorized access. Please provide a valid API token."
}Forbidden
{
"error": "Access denied. Insufficient permissions."
}Example Requests
Basic request:
curl -X GET "https://app.ptc.wpml.org/api/v1/content_translation/123" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json"Code Examples
curl -X GET "https://app.ptc.wpml.org/api/v1/content_translation/123" \
-H "Authorization: Bearer YOUR_API_TOKEN"require 'net/http'
require 'uri'
uri = URI('https://app.ptc.wpml.org/api/v1/content_translation/123')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
require 'json'
data = JSON.parse(response.body)
puts "Source: #{data['source']}"
data.each { |lang, translation| puts "#{lang}: #{translation}" unless lang == 'source' }import requests
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get('https://app.ptc.wpml.org/api/v1/content_translation/123', headers=headers)
data = response.json()
print('Source:', data['source'])
for lang_code, translation in data.items():
if lang_code != 'source':
print(f"{lang_code}:", translation)<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://app.ptc.wpml.org/api/v1/content_translation/123',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer YOUR_API_TOKEN'
],
]);
$response = curl_exec($curl);
curl_close($curl);
$data = json_decode($response, true);
foreach ($data as $langCode => $translation) {
echo strtoupper($langCode) . ": " . json_encode($translation) . "\n";
}
?>import okhttp3.*;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://app.ptc.wpml.org/api/v1/content_translation/123")
.addHeader("Authorization", "Bearer YOUR_API_TOKEN")
.build();
Response response = client.newCall(request).execute();
System.out.println(response.body().string());package main
import (
"fmt"
"io"
"net/http"
)
func main() {
req, _ := http.NewRequest("GET", "https://app.ptc.wpml.org/api/v1/content_translation/123", nil)
req.Header.Add("Authorization", "Bearer YOUR_API_TOKEN")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}
using System;
using System.Net.Http;
using System.Threading.Tasks;
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "YOUR_API_TOKEN");
var response = await client.GetAsync("https://app.ptc.wpml.org/api/v1/content_translation/123");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);const axios = require('axios');
const response = await axios.get('https://app.ptc.wpml.org/api/v1/content_translation/123', {
headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
});
const data = response.data;
console.log('Source:', data.source);
Object.keys(data).forEach(lang => {
if (lang !== 'source') console.log(`${lang}:`, data[lang]);
});Get the Content Translation Status
Retrieves the current status of a specific content translation job.
The response reflects the overall progress and includes whether the translation is queued, in progress, completed, or has failed.
HTTP Request
GET https://app.ptc.wpml.org/api/v1/content_translation/{id}/statusPath Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | The unique identifier of the content translation job to check. |
Responses
Success Response
{
"status": "completed",
"completeness": 100
}Response Schema
| Field | Type | Description |
|---|---|---|
status |
string | The current translation status. Possible status values include: queued, in_progress, completed, failed, status_unknown. |
completeness |
number | The percentage of translated strings (0–100). Calculated as (completed_translatable_strings / total_translatable_strings) × 100. |
Status Values
| Status | Description |
|---|---|
queued |
The translation has been queued and is waiting to be processed. |
in_progress |
The translation is currently being processed. |
completed |
The translation has been completed successfully. |
failed |
The translation has failed due to an error. |
status_unknown |
The translation status is unknown or cannot yet be determined. |
Error Responses
Possible causes:
- No content translation exists with the specified ID
- The translation job does not belong to the authenticated project
Example
curl -X GET "https://app.ptc.wpml.org/api/v1/content_translation/123/status" \
-H "Authorization: Bearer YOUR_API_TOKEN"Response:
{
"status": "completed",
"completeness": 100
}Response while the job is still running:
{
"status": "in_progress",
"completeness": 70
}{
"status": "completed",
"completeness": 100
}