> For clean Markdown of any page, append `.md` to the page URL.
> For a complete documentation index, see https://docs.sarvam.ai/llms.txt.
> For full documentation content in one file, see https://docs.sarvam.ai/llms-full.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sarvam.ai/_mcp/server.

# Document Translation API Overview

> Translate whole PDF, Word, spreadsheet, presentation, and web page files into 23 languages (English + 22 Indic) while preserving the original layout, with Sarvam's Document Translation API.

The Document Translation API takes a source document and returns it translated into one or more Indian languages, in its original file format. The layout, formatting, and structure of the source file are preserved.

Authentication: send your API key in the `api-subscription-key` header. Generate a key from [Key Management](https://dashboard.sarvam.ai/key-management) in the dashboard.

## What you get from one job

A single job translates into up to 12 target languages (out of 23 total supported) from one uploaded document. You upload once, and each target language is translated independently and exported separately.

## How it works

Document translation is asynchronous by design: you create a job, upload the file, start the pipeline, poll it, and then export each language's result, rather than getting a translated file back from a single request. Every integration follows the same five steps:

#### Create the job

`POST /translate/document/jobs` with your source/target languages and the original filename. Returns a `job_id` and a short-lived signed `upload_url`, with status `201 Created`.

#### Upload the document

`PUT` the file bytes to `upload_url`. This step is required; the create call only accepts metadata, not the file itself.

#### Start the pipeline

`POST /translate/document/jobs/{job_id}/start`, no request body. Runs file validation, then parsing (OCR for PDFs, structural extraction for Office and web formats), then translation, one independent branch per target language. Idempotent: calling `start` again on the same job returns the current job state instead of erroring.

#### Poll progress

`GET /translate/document/jobs/{job_id}/live-status` for an overall progress percentage and per-language translation state. Poll every 10-15 seconds.

#### Export each language

`POST /translate/document/jobs/{job_id}/export?lang={code}&format={fmt}` to enqueue an async export for one target language, then poll `GET /translate/document/jobs/{job_id}/export/status` until `export_state` is `Completed` and download from `download_url`.

Export is per language, not one call for the whole job. Loop over your `target_language_codes` and call `export` once for each, only after that language's own `state` in `live-status` is `Completed`.   Calling `export` before that returns `409 Conflict`.

## Quickstart

Install the SDK (`pip install sarvamai` or `npm install sarvamai`), set `SARVAM_API_KEY`, and point `document` at a local PDF or Office file. The client uses `https://api.sarvam.ai` by default, so your API key is the only configuration it needs.

Document translation does not include an SDK upload helper yet. After `create`, `PUT` the file bytes to `upload_url` with `Content-Type` and `x-ms-blob-type: BlockBlob`, then call `start`. See [Poll and Export](/api/api-guides-tutorials/doc-translation/how-to/poll-and-export) for the polling and download loop.

#### Python

```python
import os
from pathlib import Path

import httpx
from sarvamai import SarvamAI

client = SarvamAI(api_subscription_key=os.environ["SARVAM_API_KEY"])
document = Path("chapter1.pdf")

# 1. Create job (auto_process=True is the default)
job = client.document_translation.create(
    job_name="chapter1",
    source_language_code="en-IN",
    target_language_codes=["hi-IN", "ta-IN"],
    original_filename=document.name,
    auto_process=True,
    model_type="plus",
)

# 2. Upload to the signed URL (required — not handled by the SDK)
with document.open("rb") as f:
    httpx.put(
        job.upload_url,
        content=f.read(),
        headers={
            "Content-Type": "application/pdf",
            "x-ms-blob-type": "BlockBlob",
        },
        timeout=120.0,
    )

# 3. Start the pipeline (required even when auto_process=True)
client.document_translation.start(job_id=job.job_id)

# 4–5. Poll translation, then export — see Poll and Export guide
print(job.job_id)
```

#### JavaScript

```javascript
import fs from "fs";
import { SarvamAIClient } from "sarvamai";

const client = new SarvamAIClient({
    apiSubscriptionKey: process.env.SARVAM_API_KEY,
});

const documentPath = "chapter1.pdf";

// 1. Create job (auto_process defaults to true)
const job = await client.documentTranslation.create({
    job_name: "chapter1",
    source_language_code: "en-IN",
    target_language_codes: ["hi-IN", "ta-IN"],
    original_filename: documentPath,
    auto_process: true,
    model_type: "plus",
});

// 2. Upload to the signed URL (required — not handled by the SDK)
await fetch(job.upload_url, {
    method: "PUT",
    headers: {
        "Content-Type": "application/pdf",
        "x-ms-blob-type": "BlockBlob",
    },
    body: fs.readFileSync(documentPath),
});

// 3. Start the pipeline (required even when auto_process is true)
await client.documentTranslation.start(job.job_id);

// 4–5. Poll translation, then export — see Poll and Export guide
console.log(job.job_id);
```

That job is now running. See [Poll and Export](/api/api-guides-tutorials/doc-translation/how-to/poll-and-export) for the polling loop and per-language download flow.

## Page-granular vs document-granular responses

The response format depends on the uploaded file type:

* PDFs are tracked per page.
* Word, spreadsheets, and presentations are tracked per text segment (roughly a paragraph).

Both response formats share the same base fields. Only the progress-related fields differ.

### Response shapes at a glance

|                       | PDF uploads (per page)                                              | Word / Spreadsheet / Presentation uploads (per segment)      |
| --------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------ |
| Tracking unit         | Page                                                                | Segment (paragraph-sized chunk of text)                      |
| Start response        | `num_pages`                                                         | -                                                            |
| Live status           | `page_metrics`                                                      | `translation_metrics`                                        |
| Per-language progress | `pages_completed`, `pages_failed`                                   | `progress`, `segments_completed`, `segments_total`           |
| Other metrics         | `total_pages`, `pages_processed`, `pages_succeeded`, `pages_failed` | `languages_total`, `languages_completed`, `languages_failed` |

### Shared fields (both response types)

`job_id` `job_state` `job_name` `source_language_code` `progress` `translations[]` `created_at` `updated_at`

| Optional   | On failure      |
| ---------- | --------------- |
| `warnings` | `error_message` |

## Job states

| State                | Description                                        |
| -------------------- | -------------------------------------------------- |
| `Accepted`           | Job created, queued for processing                 |
| `Running`            | Job is being processed                             |
| `Completed`          | All languages translated successfully              |
| `PartiallyCompleted` | Some languages succeeded, some failed              |
| `Failed`             | All languages failed or a job-level error occurred |

Terminal states are `Completed`, `PartiallyCompleted`, and `Failed`.

Each language in `translations[]` has its own `state` (`Pending`, `Running`, `Completed`, `Failed`), independent of the others. A language is exportable as soon as its own entry reads `Completed`, even while other languages in the same job are still `Running`. Treat `PartiallyCompleted` as a terminal state alongside `Completed`, and check each language's own `state` before deciding what to export.

## Request parameters

Required:

| Field                   | Description                                                                                                                           |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `job_name`              | Human-readable job name, 1-255 characters                                                                                             |
| `source_language_code`  | BCP-47 code identifying the original document's language                                                                              |
| `target_language_codes` | Array of destination languages, 1 to 12 entries per job (out of 23 total supported); one translated document is produced per language |
| `original_filename`     | File name with extension; validated against the supported format list                                                                 |

Optional:

| Field                          | Description                                                                                                                                    |
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `genre`                        | Content category: `NON_FICTION`, `ADULT_FICTION`, `CHILDREN_FICTION`, `RELIGIOUS`, `LEGAL`, or `ACADEMIC`, used to tune the translation prompt |
| `metadata`                     | Additional metadata object                                                                                                                     |
| `auto_process`                 | Boolean. When `true` (the default), all target languages are bulk-translated automatically once parsing/OCR completes                          |
| `model_type`                   | `lite` (faster, suited to short-form content) or `plus` (higher quality, suited to long-form content). Defaults to `plus` when omitted         |
| `use_native_numerals`          | Boolean; when `true`, numerals render in the target script instead of Arabic numerals. `null`/`false` uses international numerals              |
| `style_guidelines`             | Up to \~4000 characters of global tone, terminology, and formatting instructions                                                               |
| `language_specific_guidelines` | Per-language overrides (\~4000 characters each); keys must match entries in `target_language_codes`                                            |

## Supported file formats

| Category      | Extensions                  |
| ------------- | --------------------------- |
| PDF           | `.pdf`                      |
| Word          | `.doc`, `.docx`, `.odt`     |
| Spreadsheets  | `.xls`, `.xlsx`, `.ods`     |
| Presentations | `.ppt`, `.pptx`, `.odp`     |
| Web pages     | `.html`, `.xhtml`, `.mhtml` |

## Supported languages

23 languages: English plus 22 Indic languages.

| Language  | Code     | Language | Code     |
| --------- | -------- | -------- | -------- |
| Assamese  | `as-IN`  | Manipuri | `mni-IN` |
| Bengali   | `bn-IN`  | Marathi  | `mr-IN`  |
| Bodo      | `brx-IN` | Nepali   | `ne-IN`  |
| Dogri     | `doi-IN` | Odia     | `or-IN`  |
| English   | `en-IN`  | Punjabi  | `pa-IN`  |
| Gujarati  | `gu-IN`  | Sanskrit | `sa-IN`  |
| Hindi     | `hi-IN`  | Santali  | `sat-IN` |
| Kannada   | `kn-IN`  | Sindhi   | `sd-IN`  |
| Kashmiri  | `ks-IN`  | Tamil    | `ta-IN`  |
| Konkani   | `kok-IN` | Telugu   | `te-IN`  |
| Maithili  | `mai-IN` | Urdu     | `ur-IN`  |
| Malayalam | `ml-IN`  |          |          |

## Response examples

`POST /translate/document/jobs` returns `201 Created`:

```json
{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "upload_url": "https://api.sarvam.ai/translate/document/uploads/550e8400-e29b-41d4-a716-446655440000?sig=...",
  "expires_in_hours": 24,
  "source_language_code": "en-IN",
  "target_language_codes": ["hi-IN", "ta-IN"]
}
```

`GET /translate/document/jobs/{job_id}/live-status` returns:

```json
{
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "job_name": "chapter1",
  "job_state": "Running",
  "source_language_code": "en-IN",
  "progress": 55,
  "page_metrics": {
    "total_pages": 42,
    "pages_processed": 23,
    "pages_succeeded": 23,
    "pages_failed": 0
  },
  "translations": [
    {
      "target_language_code": "hi-IN",
      "state": "Running",
      "pages_completed": 12,
      "pages_failed": 0
    },
    {
      "target_language_code": "ta-IN",
      "state": "Pending",
      "pages_completed": 0,
      "pages_failed": 0
    }
  ],
  "created_at": "2026-08-04T10:00:00Z",
  "updated_at": "2026-08-04T10:05:00Z"
}
```

`GET /translate/document/jobs/{job_id}/export/status` returns, once the export is `Completed`:

```json
{
  "export_id": "exp_1a2b3c4d",
  "job_id": "550e8400-e29b-41d4-a716-446655440000",
  "target_language_code": "hi-IN",
  "format": "pdf",
  "export_state": "Completed",
  "download_url": "https://api.sarvam.ai/translate/document/exports/550e8400-e29b-41d4-a716-446655440000/hi-IN?sig=...",
  "filename": "chapter1_hi-IN.pdf",
  "expires_in_hours": 24,
  "file_size": 1048576,
  "created_at": "2026-08-04T10:10:00Z",
  "completed_at": "2026-08-04T10:12:30Z"
}
```

## Errors

| HTTP Status | Description                                                                                            |
| ----------- | ------------------------------------------------------------------------------------------------------ |
| `400`       | Invalid request — invalid language code, genre, filename, or export format                             |
| `401`       | Unauthorized — send a valid `api-subscription-key`                                                     |
| `402`       | Payment required — insufficient credits to complete this operation                                     |
| `404`       | Not found — no job exists for this `job_id`, or no export for the given `export_id` or `lang`          |
| `409`       | Conflict — export already in progress for this language and format, or translation not yet `Completed` |
| `500`       | Internal server error                                                                                  |
| `503`       | Service unavailable — export enqueue failed. Please retry                                              |

## Best Practices

#### Poll, Don't Block

Poll `live-status` every 10-15 seconds from a background worker, not an HTTP request handler. Cap total wait time and handle timeouts gracefully.

#### Export Per Language

Export is per language, not one call for the whole job. Loop over your `target_language_codes` and call `export` once for each, only after that language's own `state` is `Completed`.

#### Check Per-Language State

Don't gate the export loop on `job_state` alone. A job at `PartiallyCompleted` can still have individual languages ready to export. Check each language's own `state` in `translations[]`.

#### Pick the Right Genre

Use `genre` to tune the translation prompt for your content type. `LEGAL` for contracts, `CHILDREN_FICTION` for children's books, `ACADEMIC` for research papers.

## Limits

| Limit                                                      | Value                                                                                             |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
| Target languages per job                                   | 1 to 12 (out of 23 total supported languages)                                                     |
| `job_name` length                                          | 1-255 characters                                                                                  |
| `style_guidelines` / `language_specific_guidelines` length | \~4000 characters each                                                                            |
| Signed upload URL validity                                 | `expires_in_hours` on the create response (24 hours)                                              |
| Signed export URL validity                                 | `expires_in_hours` on the export response (24 hours); call `export/status` again for a fresh link |
| Poll interval                                              | Every 10-15 seconds against `live-status`                                                         |

## Next Steps

#### [Poll and Export](/api/api-guides-tutorials/doc-translation/how-to/poll-and-export)

Complete code examples for polling and exporting in Python, JavaScript, and curl.

#### [Pick a Genre and Model Tier](/api/api-guides-tutorials/doc-translation/how-to/genre-and-model-tier)

How to use genre and model\_type for different content types.

#### [API Reference](/api-reference/translate-document/create-doc-translation)

Endpoint-level schemas for every request and response field.

Need help scoping a document translation integration? Reach out on [Discord](https://discord.com/invite/5rAsykttcs).