Translations API
REST API endpoints for managing AI-powered documentation translations.
Overview
The Translations API provides programmatic control over AI-powered documentation translations. Configure target languages, trigger translation jobs, manage glossary terms for domain-specific terminology, and track translation status across your documentation.
Base path: https://api.holydocs.com/api/v1/projects/:projectId
All translation endpoints require authentication. Language management and triggering translations both require the projects:write scope.
HolyDocs translations are AI-powered, not machine-translated. Each page is translated by an LLM that understands context, preserves MDX component structure, and respects your glossary for consistent terminology. Code blocks, component props, and URLs are preserved untranslated.
Supported Languages
HolyDocs supports 24 languages for AI-powered translation:
The source language for your documentation is auto-detected from your content. You do not need to specify it explicitly. English is the default source language.
List Languages
Retrieve the configured target languages for a project.
bashGET /api/v1/projects/:projectId/languages
bashcurl "https://api.holydocs.com/api/v1/projects/$PROJECT_ID/languages" \ -H "Authorization: Bearer $HOLYDOCS_API_KEY"
bashholydocs api get "/projects/$PROJECT_ID/languages"
tsimport { HolyDocs } from '@holydocs/sdk';const client = new HolyDocs({ apiKey: process.env.HOLYDOCS_API_KEY });const { data } = await client.translations.listLanguages(projectId);
Response
json{ "data": { "sourceLanguage": "en", "targetLanguages": [ { "code": "ja", "name": "Japanese", "translatedPages": 38, "totalPages": 42, "completionRate": 0.90, "lastTranslatedAt": "2026-04-10T08:30:00Z" }, { "code": "de", "name": "German", "translatedPages": 42, "totalPages": 42, "completionRate": 1.0, "lastTranslatedAt": "2026-04-11T02:15:00Z" }, { "code": "zh-CN", "name": "Chinese Simplified", "translatedPages": 20, "totalPages": 42, "completionRate": 0.48, "lastTranslatedAt": "2026-04-08T14:00:00Z" } ] }}
bashcurl "https://api.holydocs.com/api/v1/projects/proj_abc123/languages" \ -H "Authorization: Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890"
javascriptconst response = await fetch( 'https://api.holydocs.com/api/v1/projects/proj_abc123/languages', { headers: { 'Authorization': 'Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890' } });const { data } = await response.json();for (const lang of data.targetLanguages) { const pct = (lang.completionRate * 100).toFixed(0); console.log(`${lang.name}: ${pct}% (${lang.translatedPages}/${lang.totalPages})`);}
Add Language
Add a new target language to the project. This does not trigger translation -- it configures the language as a translation target.
bashPOST /api/v1/projects/:projectId/languages
Request Body
json{ "code": "fr", "autoTranslate": true}
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | ISO 639-1 language code from the supported languages list |
autoTranslate | boolean | No | Automatically translate new and updated pages (default: false) |
Response
json{ "data": { "code": "fr", "name": "French", "autoTranslate": true, "translatedPages": 0, "totalPages": 42, "completionRate": 0.0, "createdAt": "2026-04-11T10:00:00Z" }}
Enable autoTranslate to keep translations up-to-date automatically. When a source page is updated and redeployed, the translation for that page is automatically re-queued. This is the recommended setting for actively maintained documentation.
Remove Language
Remove a language and all its translations from the project.
bashDELETE /api/v1/projects/:projectId/languages/:code
Path Parameters
| Parameter | Type | Description |
|---|---|---|
projectId | string | Project ID |
code | string | Language code to remove (e.g., fr) |
Response
json{ "data": { "deleted": true, "code": "fr", "pagesDeleted": 42 }}
This permanently deletes all translations for the removed language. The source content is not affected. This action cannot be undone.
Trigger Translation
Start a translation job for a specific page or all pages in a language.
bashPOST /api/v1/projects/:projectId/translate
Request Body
json{ "language": "ja", "scope": "all"}
json{ "language": "ja", "scope": "page", "pagePath": "/quickstart"}
json{ "language": "ja", "scope": "stale"}
| Field | Type | Required | Description |
|---|---|---|---|
language | string | Yes | Target language code |
scope | all | page | stale | No | What to translate (default: all) |
pagePath | string | Conditional | Required when scope is page |
force | boolean | No | Retranslate even if a current translation exists (default: false) |
Response
json{ "data": { "jobId": "tr_abc123", "language": "ja", "scope": "all", "pagesQueued": 42, "estimatedDuration": 180, "status": "queued", "createdAt": "2026-04-11T10:00:00Z" }}
bashcurl -X POST "https://api.holydocs.com/api/v1/projects/proj_abc123/translate" \ -H "Authorization: Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890" \ -H "Content-Type: application/json" \ -d '{ "language": "ja", "scope": "all" }'
javascriptconst response = await fetch( 'https://api.holydocs.com/api/v1/projects/proj_abc123/translate', { method: 'POST', headers: { 'Authorization': 'Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890', 'Content-Type': 'application/json' }, body: JSON.stringify({ language: 'ja', scope: 'stale' // Only retranslate pages updated since last translation }) });const { data } = await response.json();console.log(`Translation job ${data.jobId}: ${data.pagesQueued} pages queued`);
Translation Scopes
| Scope | Behavior |
|---|---|
all | Translate every page, skipping pages with up-to-date translations (unless force: true) |
page | Translate a single page specified by pagePath |
stale | Only translate pages where the source content has changed since the last translation |
List Translations
Retrieve translation status for all pages in a specific language.
bashGET /api/v1/projects/:projectId/translations
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
language | string | Yes | Target language code |
status | completed | pending | failed | stale | No | Filter by translation status |
page | number | No | Page number (default: 1) |
perPage | number | No | Results per page (default: 50, max: 200) |
Response
json{ "data": [ { "pagePath": "/quickstart", "pageTitle": "Quickstart Guide", "language": "ja", "status": "completed", "translatedTitle": "\u30af\u30a4\u30c3\u30af\u30b9\u30bf\u30fc\u30c8\u30ac\u30a4\u30c9", "sourceHash": "a1b2c3d4", "translatedHash": "e5f6g7h8", "wordCount": 1240, "translatedAt": "2026-04-10T08:30:00Z", "sourceUpdatedAt": "2026-04-09T12:00:00Z" }, { "pagePath": "/api/authentication", "pageTitle": "API Authentication", "language": "ja", "status": "stale", "translatedTitle": "API\u8a8d\u8a3c", "sourceHash": "i9j0k1l2", "translatedHash": "m3n4o5p6", "wordCount": 890, "translatedAt": "2026-04-05T06:00:00Z", "sourceUpdatedAt": "2026-04-08T14:00:00Z" }, { "pagePath": "/webhooks", "pageTitle": "Webhooks", "language": "ja", "status": "pending", "translatedTitle": null, "sourceHash": "q7r8s9t0", "translatedHash": null, "wordCount": 1560, "translatedAt": null, "sourceUpdatedAt": "2026-04-10T09:00:00Z" } ], "meta": { "total": 42, "page": 1, "perPage": 50, "totalPages": 1 }}
Translation Statuses
| Status | Description |
|---|---|
completed | Translation is current and matches the latest source content |
stale | Translation exists but the source page has been updated since. The translation is still served but may be outdated. |
pending | Page has not been translated yet for this language |
in_progress | Translation is currently being processed |
failed | Translation failed (typically due to content complexity or LLM error) |
A stale translation is still served to readers. HolyDocs does not hide translated content when the source changes. Instead, it flags the translation as stale so you can prioritize retranslation. Use scope: "stale" in the translate endpoint to batch-update stale pages.
Add Glossary Term
Add a term to the project glossary. Glossary terms ensure consistent translation of domain-specific terminology, product names, and technical jargon across all languages.
bashPOST /api/v1/projects/:projectId/glossary
Request Body
json{ "term": "HolyDocs", "description": "Product name, do not translate", "translations": { "ja": "HolyDocs", "de": "HolyDocs", "zh-CN": "HolyDocs", "fr": "HolyDocs" }}
| Field | Type | Required | Description |
|---|---|---|---|
term | string | Yes | The source term (1-100 characters) |
description | string | No | Context for the translator (e.g., "Product name, do not translate") |
translations | object | No | Map of language codes to translated terms. If omitted for a language, the AI uses the description as guidance. |
caseSensitive | boolean | No | Match the term case-sensitively (default: true) |
Response
json{ "data": { "id": "gl_abc123", "term": "HolyDocs", "description": "Product name, do not translate", "translations": { "ja": "HolyDocs", "de": "HolyDocs", "zh-CN": "HolyDocs", "fr": "HolyDocs" }, "caseSensitive": true, "createdAt": "2026-04-11T10:00:00Z" }}
bashcurl -X POST "https://api.holydocs.com/api/v1/projects/proj_abc123/glossary" \ -H "Authorization: Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890" \ -H "Content-Type: application/json" \ -d '{ "term": "deployment pipeline", "description": "The automated build and publish process", "translations": { "ja": "\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3", "de": "Deployment-Pipeline", "fr": "pipeline de d\u00e9ploiement" } }'
javascriptconst response = await fetch( 'https://api.holydocs.com/api/v1/projects/proj_abc123/glossary', { method: 'POST', headers: { 'Authorization': 'Bearer hd_a1b2c3d4e5f67890a1b2c3d4e5f67890', 'Content-Type': 'application/json' }, body: JSON.stringify({ term: 'deployment pipeline', description: 'The automated build and publish process', translations: { ja: '\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3', de: 'Deployment-Pipeline', fr: 'pipeline de d\u00e9ploiement' } }) });const { data } = await response.json();console.log(`Glossary term "${data.term}" added (${data.id})`);
Glossary Best Practices
Add entries for your product name, feature names, and brand terminology. Set the translation to the original term if it should not be translated (e.g., "HolyDocs" stays "HolyDocs" in all languages).
Terms like "webhook," "API key," and "SSO" may have established translations in some languages and should remain in English in others. Use the translations field to specify the correct form per language.
Add glossary entries for acronyms (e.g., "RBAC" with description "Role-Based Access Control") so the AI translator expands or preserves them appropriately in context.
If your documentation references specific UI labels (e.g., "Settings > API Keys"), add glossary entries so these labels are translated consistently with your product's actual localized UI.
Update Glossary Term
Update an existing glossary term's translations, context, or doNotTranslate flag. All fields are optional — unspecified fields are left untouched.
bashPUT /api/v1/projects/:projectId/glossary/:termId
Request Body
json{ "sourceTerm": "deployment pipeline", "translations": { "es": "pipeline de despliegue", "ja": "デプロイパイプライン" }, "context": "technical", "doNotTranslate": false}
| Field | Type | Required | Description |
|---|---|---|---|
sourceTerm | string | No | New source term |
translations | object | No | Map of language codes to translated terms (replaces the existing map) |
context | string | No | Context for the translator |
doNotTranslate | boolean | No | If true, preserve the source term verbatim in all translations |
Response
json{ "data": { "id": "gl_abc123", "sourceTerm": "deployment pipeline", "translations": { "es": "pipeline de despliegue", "ja": "デプロイパイプライン" }, "context": "technical", "doNotTranslate": false }}
Returns 404 NOT_FOUND if the glossary term does not exist or belongs to another project.
List Glossary Terms
Retrieve all glossary terms for a project.
bashGET /api/v1/projects/:projectId/glossary
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
page | number | No | Page number (default: 1) |
perPage | number | No | Results per page (default: 50, max: 200) |
q | string | No | Search glossary terms by name |
Response
json{ "data": [ { "id": "gl_abc123", "term": "HolyDocs", "description": "Product name, do not translate", "translations": { "ja": "HolyDocs", "de": "HolyDocs", "zh-CN": "HolyDocs", "fr": "HolyDocs" }, "caseSensitive": true, "createdAt": "2026-04-11T10:00:00Z" }, { "id": "gl_def456", "term": "deployment pipeline", "description": "The automated build and publish process", "translations": { "ja": "\u30c7\u30d7\u30ed\u30a4\u30e1\u30f3\u30c8\u30d1\u30a4\u30d7\u30e9\u30a4\u30f3", "de": "Deployment-Pipeline", "fr": "pipeline de d\u00e9ploiement" }, "caseSensitive": false, "createdAt": "2026-04-11T10:05:00Z" } ], "meta": { "total": 15, "page": 1, "perPage": 50, "totalPages": 1 }}
Export Translations to Repo
Commit all translations for a given language to a new branch in the project's connected repo. Lets you turn dashboard-only translations into repo-committed files that downstream tools or teammates can review in a pull request.
bashPOST /api/v1/projects/:projectId/translations/export-repo
Request Body
json{ "language": "ja", "branch": "translations/ja"}
| Field | Type | Required | Description |
|---|---|---|---|
language | string | Yes | Target language code |
branch | string | No | Destination branch name. Defaults to holydocs/translations-<lang>-<timestamp> |
Response
json{ "data": { "branch": "holydocs/translations-ja-1713384000000", "commitSha": "a1b2c3d4...", "filesWritten": 42 }}
Behavior
- Files are written as
<language>/<slug>.mdxunder the project'srepoPath(docs directory). - If the branch doesn't exist, it's created from the default branch. Existing branches are reused (idempotent).
- The response includes the commit SHA — open a PR manually on GitHub to merge.
- Requires a GitHub connection on the project. GitLab export is not yet supported.
- Returns
filesWritten: 0when there are no translations for the language (no-op).
Use this endpoint to graduate AI-generated or dashboard-edited translations into your repository so they are preserved across re-deploys and visible in code review. Once exported, HolyDocs treats them as human-owned translations and will never overwrite them with AI output.
Translation Workflow
A typical translation workflow using the API:
Add target languages
Configure the languages you want to support. Enable autoTranslate for languages that should stay automatically up-to-date.
bashPOST /api/v1/projects/proj_abc123/languages{"code": "ja", "autoTranslate": true}
Build your glossary
Add domain-specific terms, product names, and technical terminology to ensure consistent translations.
bashPOST /api/v1/projects/proj_abc123/glossary{"term": "HolyDocs", "description": "Product name, do not translate"}
Trigger initial translation
Translate all existing pages for each configured language.
bashPOST /api/v1/projects/proj_abc123/translate{"language": "ja", "scope": "all"}
Monitor progress
Poll the translations endpoint to track completion status.
bashGET /api/v1/projects/proj_abc123/translations?language=ja&status=pending
Maintain translations
With autoTranslate enabled, updated pages are retranslated on each deployment. For manual control, use scope: "stale" to batch-retranslate pages whose source has changed.
Translation Limits by Plan
| Plan | Languages | Pages per Month | Glossary Terms |
|---|---|---|---|
| Free | 1 | 50 | 10 |
| Starter | 3 | 500 | 50 |
| Pro | 10 | 5,000 | 200 |
| Business | 24 | 50,000 | 1,000 |
| Enterprise | 24 | Unlimited | Unlimited |
"Pages per month" counts individual page translation operations. Translating 42 pages into 3 languages counts as 126 page translations. Retranslating a stale page also counts as one operation.
Error Codes
| Code | Status | Description |
|---|---|---|
NOT_FOUND | 404 | Project not found or language not configured |
VALIDATION_ERROR | 400 | Invalid language code, missing required fields, or unsupported language |
AUTH_ERROR | 401 | Missing or invalid authentication |
FORBIDDEN | 403 | API key lacks projects:write scope |
LIMIT_EXCEEDED | 429 | Language limit, monthly page limit, or glossary term limit exceeded |
ALREADY_EXISTS | 409 | Language already configured or glossary term already exists |
TRANSLATION_FAILED | 502 | LLM translation failed (retry the job) |