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

# Create Document Translation

POST https://api.sofer.ai/v1/documents/{document_id}/translations
Content-Type: application/json

Start a document translation. Text and transcript documents use paragraph-level
translation.

**API price:** USD 0.25 per 1,000 billable words, deducted from your API balance.
Pricing is prorated to the word: 1,000 billable words costs USD 0.25 and 10,000
billable words costs USD 2.50. Cached current paragraphs are free; stale, missing,
failed, forced, or newly added paragraphs are billable.

Repeated requests for the same document and translation configuration reuse
cached work and the same billing identity, preventing duplicate charges.

A document can have multiple translations. Each combination of target language and
translation style is stored separately, so the same document can be translated
into both English and Hebrew without duplicating the source document.

Reference: https://docs.sofer.ai/api-reference/translate/create-document-translation

## Authentication

- `Authorization` header (bearer token, required) — Bearer authentication of the form `Bearer <token>`, where token is your auth token.

## Request

### Path parameters

- `document_id` (UUID, required) — Stable document ID.

### Body (application/json)

This endpoint expects a DocumentTranslationRequest.

- `target_language` (enum, optional) — Translation target language. Use `en`, `he`, or `yi`; omit it to use automatic English/Hebrew direction selection. Custom styles and language-specific built-ins require their explicit compatible target language.
  - Allowed values: `auto`, `en`, `he`, `yi`
- `translation_style` (string, optional) — Translation style ID. Built-ins are `default` for a clear natural translation, `learning_english` for polished Torah-learning English, `torah_yiddish` for natural beis-medrash Yiddish, and `sefer_hebrew` for traditional sefer-style Hebrew. Custom style IDs come from `/v1/translation-styles`. Omit this to use `default`. `learning_english` requires target language `en`, `torah_yiddish` requires `yi`, `sefer_hebrew` requires `he`, and each custom style declares its compatible language.
- `force` (boolean, optional) — Regenerate the translation even if cached content already exists.

## Response

### 200

- `document_id` (UUID, required) — Stable source document ID.
- `target_language` (enum, required) — Requested translation language. With `auto`, each segment reports its concrete language in its own `target_language` field.
  - Allowed values: `auto`, `en`, `he`, `yi`
- `translation_style` (string, required) — Translation style ID used for this independently stored target-language translation.
- `status` (string, required) — Overall translation status.
- `completed_segments` (integer, required) — Number of successfully translated paragraphs or pages.
- `failed_segments` (integer, required) — Number of failed paragraphs or pages.
- `source_word_count` (integer, required) — Total visible words across the document's current paragraphs or pages, including content that already has a current translation.
- `billable_word_count` (integer, required) — Words in missing, failed, forced, or stale segments that still need translation. Cached current segments are excluded, so this can be zero even when `source_word_count` is not.
- `estimated_api_cost_cents` (double, required) — Estimated API balance reservation for the current `billable_word_count`, in cents, at USD 0.25 per 1,000 words. For example, `25` means USD 0.25 and `250` means USD 2.50. This is an estimate of remaining work, not a historical invoice, and normally becomes zero after all current segments are translated.
- `segments` (list of DocumentTranslationSegment, required) — Paragraph- or page-level results for this target language and translation style.
- `total_segments` (integer, optional) — Total paragraphs or pages in the translation job.
- `created_at` (datetime, optional) — When the translation job was created.
- `updated_at` (datetime, optional) — When the translation job was last updated.
- `error` (string, optional) — Overall job error, if any.

## Errors

### 401 Authentication Error

Authentication error

### 404 Document Not Found

Document not found or not owned by the authenticated API user.

### 400 Invalid Document

Invalid document content, format, state, or update.

### 402 Insufficient API Balance

API balance is too low to reserve this translation at $0.25 per 1,000 source words.

### 429 Rate Limit Error

Rate limit exceeded, or you have used up your quota

## Types

### DocumentTranslationSegment

- `segment_id` (string, required) — Stable paragraph or page ID used to pair source and translated content.
- `segment_index` (integer, required) — One-based paragraph or page order in the document.
- `source_hash` (string, required) — Hash of the exact source content used for this translation. It is compared with the current source hash to detect edits.
- `status` (string, required) — Segment translation status.
- `is_stale` (boolean, required) — True when the current source hash differs from `source_hash`; requesting translation again updates only stale or missing segments unless `force` is true.
- `source_text` (string, optional) — Exact visible source paragraph. OCR page responses may omit this because the page structure is represented in HTML.
- `source_language` (string, optional) — Detected source language.
- `target_language` (string, optional) — Translation target language.
- `start_time` (double, optional) — Source start time in seconds when the document is a transcription.
- `end_time` (double, optional) — Source end time in seconds when the document is a transcription.
- `speaker` (string, optional) — Speaker label when the document is a transcription.
- `content` (string, optional) — Sanitized translated HTML. HTML preserves paragraph direction, emphasis, source quotations, and OCR page layout that plain text would lose.
- `error` (string, optional) — Error message if this segment failed.

## Examples

**Request**

```json
{}
```

**Response**

```json
{
  "document_id": "d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32",
  "target_language": "auto",
  "translation_style": "translation_style",
  "status": "status",
  "completed_segments": 1,
  "failed_segments": 1,
  "source_word_count": 1,
  "billable_word_count": 1,
  "estimated_api_cost_cents": 1.1,
  "segments": [
    {
      "segment_id": "segment_id",
      "segment_index": 1,
      "source_hash": "source_hash",
      "status": "status",
      "is_stale": true,
      "source_text": "source_text",
      "source_language": "source_language",
      "target_language": "target_language",
      "start_time": 1.1,
      "end_time": 1.1,
      "speaker": "speaker",
      "content": "content",
      "error": "error"
    },
    {
      "segment_id": "segment_id",
      "segment_index": 1,
      "source_hash": "source_hash",
      "status": "status",
      "is_stale": true,
      "source_text": "source_text",
      "source_language": "source_language",
      "target_language": "target_language",
      "start_time": 1.1,
      "end_time": 1.1,
      "speaker": "speaker",
      "content": "content",
      "error": "error"
    }
  ],
  "total_segments": 1,
  "created_at": "2024-01-15T09:30:00Z",
  "updated_at": "2024-01-15T09:30:00Z",
  "error": "error"
}
```

**SDK Code**

```python
import requests

url = "https://api.sofer.ai/v1/documents/d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32/translations"

payload = {}
headers = {
    "Authorization": "Bearer <apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.sofer.ai/v1/documents/d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32/translations';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <apiKey>', 'Content-Type': 'application/json'},
  body: '{}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.sofer.ai/v1/documents/d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32/translations"

	payload := strings.NewReader("{}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.sofer.ai/v1/documents/d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32/translations")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.sofer.ai/v1/documents/d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32/translations")
  .header("Authorization", "Bearer <apiKey>")
  .header("Content-Type", "application/json")
  .body("{}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.sofer.ai/v1/documents/d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32/translations', [
  'body' => '{}',
  'headers' => [
    'Authorization' => 'Bearer <apiKey>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.sofer.ai/v1/documents/d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32/translations");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <apiKey>",
  "Content-Type": "application/json"
]
let parameters = [] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.sofer.ai/v1/documents/d5e9c84f-c2b2-4bf4-b4b0-7ffd7a9ffc32/translations")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```