From c335fc3e0dccae908e4fd078b0d5332417a07006 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 28 Jul 2026 20:04:33 +0000
Subject: [PATCH] docs: add generated pages from pipeline run 20260728-195522
Generated 3 pages for: Languages, Voice, unknown
- docs/voice/translate-a-pre-recorded-audio-file.mdx: No guide (tutorial or how-to) covers the 'Translate Audio Files' endpoints
- docs/languages/check-feature-availability-for-a-language-pair.mdx: No guide (tutorial or how-to) covers the 'Languages' endpoints
- docs/learning-how-tos/cookbook/google-sheets: docs/learning-how-tos/cookbook/google-sheets has under 100 words
---
docs.json | 6 +-
...ature-availability-for-a-language-pair.mdx | 216 ++++++++++++++++
.../translate-a-pre-recorded-audio-file.mdx | 239 ++++++++++++++++++
3 files changed, 460 insertions(+), 1 deletion(-)
create mode 100644 docs/languages/check-feature-availability-for-a-language-pair.mdx
create mode 100644 docs/voice/translate-a-pre-recorded-audio-file.mdx
diff --git a/docs.json b/docs.json
index 6ab5aa0b..5a58d1d8 100644
--- a/docs.json
+++ b/docs.json
@@ -102,6 +102,9 @@
}
]
}
+ ],
+ "pages": [
+ "docs/languages/check-feature-availability-for-a-language-pair"
]
},
{
@@ -149,7 +152,8 @@
"docs/voice/understanding-voice-sessions",
"docs/voice/message-encoding",
"docs/voice/supported-voice-languages",
- "docs/voice/voice-api-requirements"
+ "docs/voice/voice-api-requirements",
+ "docs/voice/translate-a-pre-recorded-audio-file"
]
},
{
diff --git a/docs/languages/check-feature-availability-for-a-language-pair.mdx b/docs/languages/check-feature-availability-for-a-language-pair.mdx
new file mode 100644
index 00000000..c7d8ac93
--- /dev/null
+++ b/docs/languages/check-feature-availability-for-a-language-pair.mdx
@@ -0,0 +1,216 @@
+---
+title: "Check feature availability for a language pair"
+description: "Use the v3/languages endpoints to look up which features are available for a specific source and target language combination."
+covers: [Languages]
+---
+
+The `GET /v3/languages` endpoint tells you which features — formality, glossaries, tag handling, and more — are available for a given language and DeepL resource. Use it to validate language pairs and conditionally enable features in your integration rather than hardcoding assumptions.
+
+This guide walks through the most common task: given a source language and a target language, determine which features you can use when translating between them.
+
+
+If you're currently using `GET /v2/languages`, see the [migration guide](/docs/languages/migrating-from-v2-languages) for differences and code examples. The v2 endpoint is deprecated.
+
+
+## Before you start
+
+You'll need a DeepL API key. Set it as an environment variable:
+
+```bash
+export DEEPL_API_KEY=your-api-key
+```
+
+## Step 1: Fetch languages for your resource
+
+Call `GET /v3/languages` with the `resource` parameter set to the DeepL API resource you're building for. For text translation, use `translate_text`.
+
+```bash
+curl -X GET 'https://api.deepl.com/v3/languages?resource=translate_text' \
+ --header "Authorization: DeepL-Auth-Key $DEEPL_API_KEY"
+```
+
+Each item in the response describes one language and whether it can be used as a source, a target, or both, along with the features it supports:
+
+```json
+[
+ {
+ "lang": "de",
+ "name": "German",
+ "status": "stable",
+ "usable_as_source": true,
+ "usable_as_target": true,
+ "features": {
+ "formality": { "status": "stable" },
+ "glossary": { "status": "stable" },
+ "tag_handling": { "status": "stable" }
+ }
+ },
+ {
+ "lang": "en",
+ "name": "English",
+ "status": "stable",
+ "usable_as_source": true,
+ "usable_as_target": false,
+ "features": {
+ "glossary": { "status": "stable" },
+ "tag_handling": { "status": "stable" }
+ }
+ },
+ {
+ "lang": "en-US",
+ "name": "English (American)",
+ "status": "stable",
+ "usable_as_source": false,
+ "usable_as_target": true,
+ "features": {
+ "glossary": { "status": "stable" },
+ "tag_handling": { "status": "stable" }
+ }
+ }
+]
+```
+
+Notice that `en` is source-only and `en-US` is target-only. Some features appear only on target languages (like `formality`) and some on both (like `glossary`). The next step explains how to determine which is which.
+
+## Step 2: Understand which side a feature applies to
+
+Some features require support on the source language, some on the target, and some on both. To check the rules for your resource, call `GET /v3/languages/resources`:
+
+```bash
+curl -X GET 'https://api.deepl.com/v3/languages/resources' \
+ --header "Authorization: DeepL-Auth-Key $DEEPL_API_KEY"
+```
+
+```json
+[
+ {
+ "name": "translate_text",
+ "features": [
+ { "name": "formality", "needs_target_support": true },
+ { "name": "style_rules", "needs_target_support": true },
+ { "name": "tag_handling", "needs_source_support": true, "needs_target_support": true },
+ { "name": "glossary", "needs_source_support": true, "needs_target_support": true },
+ { "name": "auto_detection", "needs_source_support": true }
+ ]
+ }
+]
+```
+
+For `translate_text`:
+- `formality` requires only target-language support
+- `glossary` and `tag_handling` require support on both the source and target language
+- `auto_detection` requires only source-language support (it applies when you omit the source language)
+
+## Step 3: Check availability for a specific language pair
+
+With the language data from Step 1 and the feature rules from Step 2, you can now determine which features are available for any pair. The logic is straightforward: a feature is available for a pair when every required side supports it.
+
+Here's a function that encapsulates the check:
+
+```python
+def check_pair_features(languages_by_code, resources_by_name, source_code, target_code, resource_name):
+ """
+ Returns a dict of feature -> status for a given source/target pair and resource.
+ Only includes features available for the pair (all required sides support it).
+ """
+ source = languages_by_code.get(source_code)
+ target = languages_by_code.get(target_code)
+
+ if not source or not source.get("usable_as_source"):
+ raise ValueError(f"{source_code!r} is not a valid source language")
+ if not target or not target.get("usable_as_target"):
+ raise ValueError(f"{target_code!r} is not a valid target language")
+
+ resource_features = {
+ f["name"]: f
+ for f in resources_by_name.get(resource_name, {}).get("features", [])
+ }
+
+ available = {}
+ for feature_name, rules in resource_features.items():
+ needs_source = rules.get("needs_source_support", False)
+ needs_target = rules.get("needs_target_support", False)
+
+ source_ok = not needs_source or feature_name in source.get("features", {})
+ target_ok = not needs_target or feature_name in target.get("features", {})
+
+ if source_ok and target_ok:
+ # Pick the most restrictive status across all required sides.
+ # Index order: stable=0, beta=1, early_access=2 — higher index means less stable.
+ statuses = []
+ if needs_source and feature_name in source.get("features", {}):
+ statuses.append(source["features"][feature_name]["status"])
+ if needs_target and feature_name in target.get("features", {}):
+ statuses.append(target["features"][feature_name]["status"])
+ status_order = ["stable", "beta", "early_access"]
+ available[feature_name] = max(statuses, key=lambda s: status_order.index(s)) if statuses else "stable"
+
+ return available
+```
+
+To use it, fetch both endpoints once at startup, index by code/name, then call the function for any pair:
+
+```python
+import os
+import requests
+
+API_KEY = os.environ["DEEPL_API_KEY"]
+HEADERS = {"Authorization": f"DeepL-Auth-Key {API_KEY}"}
+BASE_URL = "https://api.deepl.com"
+
+# Fetch once and cache
+languages = requests.get(
+ f"{BASE_URL}/v3/languages",
+ params={"resource": "translate_text"},
+ headers=HEADERS,
+).json()
+
+resources = requests.get(
+ f"{BASE_URL}/v3/languages/resources",
+ headers=HEADERS,
+).json()
+
+languages_by_code = {lang["lang"]: lang for lang in languages}
+resources_by_name = {res["name"]: res for res in resources}
+
+# Check English → German
+features = check_pair_features(
+ languages_by_code, resources_by_name,
+ source_code="en",
+ target_code="de",
+ resource_name="translate_text",
+)
+print(features)
+# {'formality': 'stable', 'tag_handling': 'stable', 'glossary': 'stable'}
+```
+
+The output tells you exactly which features you can pass to the [translate endpoint](/api-reference/translate) for this pair.
+
+## Step 4: Include beta languages (optional)
+
+By default, `GET /v3/languages` returns only stable languages. To include languages in beta, add `include=beta` to your request:
+
+```bash
+curl -X GET 'https://api.deepl.com/v3/languages?resource=translate_text&include=beta' \
+ --header "Authorization: DeepL-Auth-Key $DEEPL_API_KEY"
+```
+
+Beta languages have `"status": "beta"` in the response. Their features may also carry a `"status": "beta"` marker. Check the `status` field before surfacing beta languages or features to end users.
+
+
+Beta languages and features can change or be removed without notice. See [Alpha and beta features](/docs/resources/alpha-and-beta-features) before relying on them in production.
+
+
+## Caching recommendations
+
+The language list changes infrequently. Fetching it on every request adds latency and counts against your rate limits. Cache both `/v3/languages` and `/v3/languages/resources` responses and refresh them once daily, or on application startup. When DeepL adds a new language, the [language release process](/docs/resources/language-release-process) page describes what to expect in the API response.
+
+
+Do not hardcode language codes or feature lists in your application. Language codes follow BCP 47 and may use subtags beyond the common two-letter form (e.g. `zh-Hans`, `sr-Cyrl-RS`). Always treat codes as opaque identifiers. See [Language release process](/docs/resources/language-release-process) for details.
+
+
+## Next steps
+
+- [Using the Languages API](/docs/languages/using-the-languages-api) — full reference for both v3 endpoints, including all `resource` values and the complete response schema
+- [Supported languages](/docs/getting-started/supported-languages) — static table of all currently supported languages
+- [Migrating from v2/languages](/docs/languages/migrating-from-v2-languages) — if you're upgrading from the deprecated v2 endpoint
\ No newline at end of file
diff --git a/docs/voice/translate-a-pre-recorded-audio-file.mdx b/docs/voice/translate-a-pre-recorded-audio-file.mdx
new file mode 100644
index 00000000..b113267a
--- /dev/null
+++ b/docs/voice/translate-a-pre-recorded-audio-file.mdx
@@ -0,0 +1,239 @@
+---
+title: "Translate a Pre-Recorded Audio File"
+description: "Submit an audio file to the DeepL Voice Translate Job API and download plain text, SRT, or audio results using the async job workflow."
+covers: [Translate Audio Files]
+---
+
+The Voice Translate Job API translates pre-recorded audio files asynchronously. You submit a file, poll for completion, and download one or more outputs — plain text transcripts, SRT subtitles, or translated speech audio — in any combination of target languages. This guide walks you through the complete workflow with a real English MP3 podcast episode translated into German text and Spanish audio.
+
+For live audio, see the [Real-Time Voice Quickstart](/docs/voice/real-time-voice-quickstart) instead.
+
+
+ **Closed alpha.** This API is only available to select DeepL customers and may change without notice. Contact your customer success manager to request access.
+
+
+## Prerequisites
+
+- A DeepL API account with Voice Translate Job API access
+- Your DeepL API key
+- An audio file to translate (see [supported source formats](/api-reference/jobs-voice-translate/reference#supported-source-audio-formats))
+- `curl` and `jq` installed, or a language environment of your choice
+
+## The workflow at a glance
+
+Translating an audio file takes four steps:
+
+1. Create a job — get back an upload URL and a job ID
+2. Upload your audio file to the upload URL
+3. Poll the job status until all targets are `complete` (or `failed`)
+4. Download each result using its `download_url`
+
+The examples below use `https://api.deepl.com` (API Pro). If you're on API Free, replace that with `https://api-free.deepl.com`.
+
+## Step 1: Create a job
+
+Send a POST request with your source file metadata and the list of outputs you want. You must declare the file's `content_length` and `content_type` upfront — these are used to pre-authorize the upload.
+
+```bash
+curl -X POST https://api.deepl.com/v1/jobs/voice/translate \
+ -H "Authorization: DeepL-Auth-Key YOUR_AUTH_KEY" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "source_file": {
+ "name": "podcast-episode-42.mp3",
+ "content_type": "audio/mpeg",
+ "content_length": 15728640
+ },
+ "parameters": {
+ "source_language": "en"
+ },
+ "targets": [
+ { "language": "de", "type": "text/plain" },
+ { "language": "es", "type": "audio/pcm;encoding=s16le;rate=16000" }
+ ]
+ }'
+```
+
+A successful response returns a job ID, an upload URL, and a signature:
+
+```json
+{
+ "job_id": "a74d88fb-ed2a-4943-a664-a4512398b994",
+ "signature": "eyJhbGciOiJIUzI1NiIs...",
+ "upload_url": "https://assets.deepl.com/collections/a74d88fb-ed2a-4943-a664-a4512398b994/assets/b1c2d3e4-f5a6-7890-abcd-ef1234567890"
+}
+```
+
+Save the `job_id` — you'll need it to poll for status. The `upload_url` is a pre-signed URL valid for 5 minutes. If you miss the window, you'll need to create a new job.
+
+
+ Each entry in `targets` is independent. A single job can produce any combination of output types and languages — one job, multiple results.
+
+
+## Step 2: Upload your audio file
+
+PUT the file directly to the `upload_url` from step 1. Include the `Content-Type` header matching the `content_type` you declared when creating the job.
+
+```bash
+curl -X PUT "https://assets.deepl.com/collections/a74d88fb-ed2a-4943-a664-a4512398b994/assets/b1c2d3e4-f5a6-7890-abcd-ef1234567890" \
+ -H "Content-Type: audio/mpeg" \
+ --data-binary @podcast-episode-42.mp3
+```
+
+A successful upload returns an HTTP 200 with no body. Once the file is received, processing begins automatically — you don't need to trigger it separately.
+
+
+ The `Content-Type` on the upload request must exactly match `content_type` in your job creation request. A mismatch will cause the upload to fail.
+
+
+## Step 3: Poll for status
+
+Poll `GET /v1/jobs/voice/translate/{job_id}` until all targets reach a terminal status (`complete`, `failed`, or `downloaded`). Each target is processed independently, so some may finish before others.
+
+```bash
+curl https://api.deepl.com/v1/jobs/voice/translate/a74d88fb-ed2a-4943-a664-a4512398b994 \
+ -H "Authorization: DeepL-Auth-Key YOUR_AUTH_KEY"
+```
+
+While processing, targets show `status: processing`:
+
+```json
+{
+ "job_id": "a74d88fb-ed2a-4943-a664-a4512398b994",
+ "results": [
+ { "status": "processing" },
+ { "status": "processing" }
+ ]
+}
+```
+
+When a target completes, its result includes a `download_url` and `signature`:
+
+```json
+{
+ "job_id": "a74d88fb-ed2a-4943-a664-a4512398b994",
+ "results": [
+ {
+ "status": "complete",
+ "download_url": "https://assets.deepl.com/collections/a74d88fb/assets/c3d4e5f6",
+ "signature": "eyJhbGciOiJIUzI1NiIs..."
+ },
+ {
+ "status": "failed",
+ "error": { "message": "processing failed" }
+ }
+ ]
+}
+```
+
+Results appear in the same order as the `targets` array in your create request. A failed target does not affect other targets in the same job — if the German text completes successfully, its `download_url` is available even if the Spanish audio fails.
+
+A reasonable polling strategy is to start with a 5-second interval and back off to 30 seconds for larger files. Results expire 1 hour after the upload completes, so don't wait too long to download them.
+
+## Step 4: Download results
+
+Fetch each completed result using its `download_url`:
+
+```bash
+# Download German plain text
+curl -o translation-de.txt \
+ "https://assets.deepl.com/collections/a74d88fb/assets/c3d4e5f6"
+
+# Download Spanish PCM audio
+curl -o translation-es.pcm \
+ "https://assets.deepl.com/collections/a74d88fb/assets/d4e5f6a7"
+```
+
+The download URL does not require your API key — authentication is embedded in the pre-signed URL itself. After you download a result, its status transitions to `downloaded` and DeepL marks the asset for deletion. Download each result only once, or save it locally before processing.
+
+## Putting it all together
+
+Here's the complete flow as a shell script:
+
+```bash
+#!/usr/bin/env bash
+set -euo pipefail
+
+AUTH_KEY="YOUR_AUTH_KEY"
+FILE="podcast-episode-42.mp3"
+FILE_SIZE=$(wc -c < "$FILE")
+
+# Step 1: Create job
+echo "Creating job..."
+RESPONSE=$(curl -sS -X POST https://api.deepl.com/v1/jobs/voice/translate \
+ -H "Authorization: DeepL-Auth-Key $AUTH_KEY" \
+ -H "Content-Type: application/json" \
+ -d "{
+ \"source_file\": {
+ \"name\": \"$FILE\",
+ \"content_type\": \"audio/mpeg\",
+ \"content_length\": $FILE_SIZE
+ },
+ \"parameters\": { \"source_language\": \"en\" },
+ \"targets\": [
+ { \"language\": \"de\", \"type\": \"text/plain\" },
+ { \"language\": \"es\", \"type\": \"audio/pcm;encoding=s16le;rate=16000\" }
+ ]
+ }")
+
+JOB_ID=$(echo "$RESPONSE" | jq -r '.job_id')
+UPLOAD_URL=$(echo "$RESPONSE" | jq -r '.upload_url')
+echo "Job ID: $JOB_ID"
+
+# Step 2: Upload file
+echo "Uploading audio file..."
+curl -sS -X PUT "$UPLOAD_URL" \
+ -H "Content-Type: audio/mpeg" \
+ --data-binary @"$FILE"
+echo "Upload complete."
+
+# Step 3: Poll until all targets are terminal
+echo "Polling for results..."
+while true; do
+ STATUS=$(curl -sS \
+ -H "Authorization: DeepL-Auth-Key $AUTH_KEY" \
+ "https://api.deepl.com/v1/jobs/voice/translate/$JOB_ID")
+
+ STATUSES=$(echo "$STATUS" | jq -r '.results[].status')
+
+ if echo "$STATUSES" | grep -qE '^processing$|^pending$|^uploaded$'; then
+ echo "Still processing, waiting 10s..."
+ sleep 10
+ else
+ echo "All targets in terminal state."
+ break
+ fi
+done
+
+# Step 4: Download results
+echo "$STATUS" | jq -c '.results[]' | while IFS= read -r result; do
+ RESULT_STATUS=$(echo "$result" | jq -r '.status')
+ if [ "$RESULT_STATUS" = "complete" ]; then
+ DOWNLOAD_URL=$(echo "$result" | jq -r '.download_url')
+ FILENAME="result-$(echo "$DOWNLOAD_URL" | rev | cut -d/ -f1 | rev)"
+ echo "Downloading $FILENAME..."
+ curl -sS -o "$FILENAME" "$DOWNLOAD_URL"
+ else
+ echo "Target failed: $(echo "$result" | jq -r '.error.message // "unknown error"')"
+ fi
+done
+
+echo "Done."
+```
+
+## Common issues
+
+**Upload returns 403 or 400**: Check that the `Content-Type` header on the PUT request matches the `content_type` you declared in the create request. Also verify you're using the full `upload_url` from the response, not a reconstructed URL.
+
+**Job returns 404**: Either the job ID is wrong, or the job has expired and been deleted. Jobs are deleted after all results are downloaded or the result window closes.
+
+**A target fails with "processing failed"**: The source audio may be corrupt, silent, or in an unsupported format. Verify the file plays correctly locally, and check the [supported source formats](/api-reference/jobs-voice-translate/reference#supported-source-audio-formats).
+
+**File upload window expired**: You have 5 minutes from job creation to complete the upload. If you miss it, create a new job.
+
+## Next steps
+
+- [Reference: limits, status lifecycle, and output formats](/api-reference/jobs-voice-translate/reference) — check file size and duration limits before submitting large files
+- [Supported Voice Languages](/docs/voice/supported-voice-languages) — verify your target languages are available for translation
+- [Create Job endpoint reference](/api-reference/jobs-voice-translate/create-voice-translate-job) — full request and response schemas
+- [Get Job Status endpoint reference](/api-reference/jobs-voice-translate/get-voice-translate-job-status) — complete status response schema
\ No newline at end of file