Document Translation
Translate entire documents while preserving their original formatting. Upload a file, Langbly processes it in the background, and you download the translated result.
Supported Formats
| Format | Extensions | Notes |
|---|---|---|
| Microsoft Word | .docx, .doc | Legacy .doc outputs as .docx |
| Microsoft PowerPoint | .pptx, .ppt | Legacy .ppt outputs as .pptx |
| Microsoft Excel | .xlsx, .xls | Legacy .xls outputs as .xlsx |
| OpenDocument | .odt, .ods, .odp | Text, Spreadsheet, Presentation |
.pdf | Text-based PDFs only (not scanned images) | |
| Rich Text | .rtf | Outputs as .docx |
| HTML | .html, .htm | |
| Plain text | .txt | |
| Subtitles | .srt | Preserves timing codes |
| InDesign | .idml | Adobe InDesign Markup Language |
How It Works
- Upload your document with a target language
- Poll the job status until it completes
- Download the translated document
The entire process runs asynchronously. Most documents finish within 30 seconds to a few minutes depending on size.
Upload a Document
POST /v2/document/translate
Content-Type: multipart/form-data
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
file | file | Yes | The document to translate (max 20 MB) |
target | string | Yes | Target language code (e.g. nl, de, fr) |
source | string | No | Source language code. Auto-detected if omitted. |
formality | string | No | formal, informal, neutral, or auto |
glossary | JSON string | No | Inline glossary array: [{"source":"Hello","target":"Hallo"}] |
glossaryId | string | No | ID of a stored glossary to use. Inline glossary takes precedence. |
instructions | string | No | Custom instructions for the translator (max 500 chars) |
Example
curl -X POST https://api.langbly.com/v2/document/translate \
-H "X-API-Key: YOUR_API_KEY" \
-F "file=@report.docx" \
-F "target=nl" \
-F "formality=formal"
Response (202 Accepted)
{
"data": {
"jobId": "fb7451f7-b395-4e5c-8183-129d31745606",
"status": "queued"
}
}
Poll Job Status
GET /v2/document/:jobId
curl https://api.langbly.com/v2/document/fb7451f7-b395-4e5c-8183-129d31745606 \
-H "X-API-Key: YOUR_API_KEY"
Response
{
"data": {
"jobId": "fb7451f7-b395-4e5c-8183-129d31745606",
"status": "done",
"progress": 100,
"originalFilename": "report.docx",
"originalFormat": "docx",
"sourceLang": null,
"targetLang": "nl",
"charsExtracted": 12450,
"charsTranslated": 12450,
"segmentsTotal": 87,
"segmentsDone": 87,
"createdAt": "2026-02-26T10:00:00.000Z",
"completedAt": "2026-02-26T10:00:45.000Z",
"expiresAt": "2026-02-27T10:00:00.000Z"
}
}
Job Statuses
| Status | Description |
|---|---|
queued | Job accepted, waiting to start |
extracting | Extracting text segments from the document |
translating | Translating segments (progress updates during this phase) |
injecting | Inserting translations back into the document |
done | Complete. Download the result. |
error | Failed. Check errorMessage for details. |
Download Result
GET /v2/document/:jobId/result
Returns the translated document as a binary download. Only available when status is done.
curl https://api.langbly.com/v2/document/fb7451f7-b395-4e5c-8183-129d31745606/result \
-H "X-API-Key: YOUR_API_KEY" \
-o report_nl.docx
The response includes:
Content-Typematching the output formatContent-Dispositionwith a suggested filename (e.g.report_nl.docx)
Code Examples
Python
import requests
import time
API_KEY = "your-api-key"
BASE = "https://api.langbly.com"
# 1. Upload
with open("report.docx", "rb") as f:
resp = requests.post(
f"{BASE}/v2/document/translate",
headers={"X-API-Key": API_KEY},
files={"file": f},
data={"target": "nl", "formality": "formal"},
)
job_id = resp.json()["data"]["jobId"]
# 2. Poll
while True:
status = requests.get(
f"{BASE}/v2/document/{job_id}",
headers={"X-API-Key": API_KEY},
).json()["data"]
if status["status"] in ("done", "error"):
break
time.sleep(3)
# 3. Download
if status["status"] == "done":
result = requests.get(
f"{BASE}/v2/document/{job_id}/result",
headers={"X-API-Key": API_KEY},
)
with open("report_nl.docx", "wb") as f:
f.write(result.content)
Node.js
const fs = require('fs');
const API_KEY = 'your-api-key';
const BASE = 'https://api.langbly.com';
async function translateDocument() {
// 1. Upload
const form = new FormData();
form.append('file', fs.createReadStream('report.docx'));
form.append('target', 'nl');
const upload = await fetch(`${BASE}/v2/document/translate`, {
method: 'POST',
headers: { 'X-API-Key': API_KEY },
body: form,
});
const { data: { jobId } } = await upload.json();
// 2. Poll
let status;
do {
await new Promise(r => setTimeout(r, 3000));
const poll = await fetch(`${BASE}/v2/document/${jobId}`, {
headers: { 'X-API-Key': API_KEY },
});
status = (await poll.json()).data;
} while (!['done', 'error'].includes(status.status));
// 3. Download
if (status.status === 'done') {
const result = await fetch(`${BASE}/v2/document/${jobId}/result`, {
headers: { 'X-API-Key': API_KEY },
});
const buffer = Buffer.from(await result.arrayBuffer());
fs.writeFileSync('report_nl.docx', buffer);
}
}
translateDocument();
Limits
| Limit | Free tier | Paid plans |
|---|---|---|
| Max file size | 20 MB | 20 MB |
| Documents per month | 3 | Unlimited |
| Concurrent jobs | 3 | 3 |
| Result availability | 24 hours | 24 hours |
| Min billable chars | Actual chars | 50,000 chars |
Minimum charge
Paid plans are billed a minimum of 50,000 characters per document, even if the document contains fewer characters. This covers the overhead of document parsing and formatting preservation. Documents with more than 50,000 characters are billed at their actual character count.
Free tier accounts are not subject to the minimum charge.
Using Glossaries
You can use glossaries with document translation in two ways:
Inline glossary (passed with the request):
curl -X POST https://api.langbly.com/v2/document/translate \
-H "X-API-Key: YOUR_API_KEY" \
-F "file=@report.docx" \
-F "target=nl" \
-F 'glossary=[{"source":"Dashboard","target":"Overzichtspagina"}]'
Stored glossary (reference by ID):
curl -X POST https://api.langbly.com/v2/document/translate \
-H "X-API-Key: YOUR_API_KEY" \
-F "file=@report.docx" \
-F "target=nl" \
-F "glossaryId=your-glossary-id"
See the Glossary docs for more on creating and managing glossaries.
Custom Instructions
The instructions field lets you pass additional context to the translator. This can help with domain-specific content or style preferences.
curl -X POST https://api.langbly.com/v2/document/translate \
-H "X-API-Key: YOUR_API_KEY" \
-F "file=@legal-contract.docx" \
-F "target=nl" \
-F "instructions=This is a legal contract. Use formal legal Dutch terminology."
Instructions are limited to 500 characters.
Error Handling
If the job fails, the status response will include an errorMessage:
{
"data": {
"jobId": "...",
"status": "error",
"errorMessage": "Extract service returned 422: Unsupported file format"
}
}
Common errors:
| Error | Cause |
|---|---|
| 400 Unsupported file format | File type not in the supported formats list |
| 400 File too large | File exceeds 20 MB limit |
| 429 Too many active jobs | You have 3 concurrent jobs running |
| 429 Monthly document limit | Free tier: 3 documents per month |
| 429 Free tier limit reached | No characters remaining on free plan |
Legacy Format Conversion
When you upload a legacy Office format (.doc, .ppt, .xls, .rtf), the translated output is in the modern format (.docx, .pptx, .xlsx). The original formatting is preserved during conversion.