# Batch mode Batch mode provides asynchronous translation for large or complex jobs that require processing multiple files or lengthy documents. ## Capabilities - Parallel processing of multiple files - Asynchronous job submission and result retrieval - Large-scale document and file collection support - Single target language per job Each input file generates its own translated output file that saves to a corresponding output location. ## How it works Batch mode processing runs asynchronously and enables you to translate multiple files in a single API call. This mode is ideal for workflows that involve large document archives. ## Limits These limits apply to file size, character count, and translation volume per job and per day. | Limit | Value | | --------------------------------------- | ---------------- | | Maximum characters per file | 4,000 characters | | Maximum file size | 16 KB | | Maximum translatable characters per job | 100,000,000 | | Maximum translatable characters per day | 1,000,000,000 | ## Input The API supports three input modes. | Mode | Description | | ---------- | -------------------------------------------------------------------------------------- | | `SINGLE` | A single text file. Inferred when the URI points to a single file. | | `PREFIX` | All files under a shared S3 prefix. Supports glob filters to include or exclude files. | | `MANIFEST` | An explicit list of file URIs. Maximum 1,000 files per manifest. | ## Output The API supports two output layouts. | Layout | Description | | ---------- | ----------------------------------------------------------------------------------------------------------- | | `ADJACENT` | Place the output file next to the input file in the same directory. Only valid when input mode is `PREFIX`. | | `PREFIX` | Use the output URI as the base directory for all translated files. | ## Output file format Each translated input file produces a JSON output file. The filename follows the pattern `{original_filename}_{file_id}.json`. The contents match the fast mode response structure: ```json { "request_id": "24cfac34-548a-4a55-a58c-b99a9ff66e4a", "result": { "translations": { "es-ES": "Translated text here." } } } ``` ## Translation options Use BCP-47 locale codes when you create a batch job. For the full list, see [Supported languages](/docs/ai-services/translator/#supported-languages). | Parameter | Type | Default | Description | | ------------------ | -------- | ------- | --------------------------------------------------------- | | `source_language` | string | — | Locale code of the input text (e.g., `"en-US"`). | | `target_languages` | string[] | — | A single target language locale code (e.g., `["fr-FR"]`). | ## Batch jobs ### Create a batch job To create a new batch translation job, use this endpoint: **`POST /aiservices/translator/jobs`** You can optionally include a `reference_id` for tracking. To receive webhook notifications when a job finishes, include a `notifications` object with a `webhook_url` and a `secret` for payload verification. #### Example request (cURL) This cURL example submits a batch translation job from S3 and defines the output location, target language, and settings. The response returns a job ID and status so you can track processing. ```shell curl -X POST https://api.zoom.us/v2/aiservices/translator/jobs \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "input": { "mode": "PREFIX", "source": "S3", "uri": "s3://acme-bucket/input/2026/03/", "filters": { "include_globs": ["*.txt"], "exclude_globs": [] }, "auth": { "aws": { "access_key_id": "AKIA...", "secret_access_key": "wJalrX...", "session_token": "FwoGZXIvYXdzE..." } } }, "output": { "destination": "S3", "uri": "s3://acme-bucket/output/2026/03/", "layout": "PREFIX", "overwrite": false, "auth": { "aws": { "access_key_id": "AKIA...", "secret_access_key": "wJalrX...", "session_token": "FwoGZXIvYXdzE..." } } }, "config": { "source_language": "en-US", "target_languages": ["fr-FR"] }, "notifications": { "webhook_url": "https://example.com/hooks/translator", "secret": "hmac-secret" }, "reference_id": "batch-translation-2026-03" }' ``` #### Response (201) ```json { "job_id": "job_abc123", "state": "QUEUED", "submitted_at": "2026-03-10T18:34:12Z" } ``` ### Batch job endpoints Three additional endpoints are available to manage batch jobs. | Action | Description | Endpoint | | -------------------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------- | | Get batch job status | Check the current status and progress summary of a specific job. | `GET /aiservices/translator/jobs/{jobId}` | | List batch jobs | View all batch jobs, optionally filtered by state. | `GET /aiservices/translator/jobs?state=QUEUED&page_size=50&next_page_token=...` | | Cancel batch job | Cancel a job that is in QUEUED or PROCESSING state. | `DELETE /aiservices/translator/jobs/{jobId}` | List responses include a `next_cursor` field. Pass its value as the `next_page_token` query parameter to retrieve the next page. ### Webhook verification When a `secret` is provided in the `notifications` object, the API signs each webhook request so you can verify it came from Zoom AI Services. Each webhook request includes two headers. | Header | Description | | ------------------------ | --------------------------------------------------------------- | | `x-zm-signature` | HMAC-SHA256 signature of the request, prefixed with `sha256=` | | `x-zm-request-timestamp` | Unix timestamp that indicates when the system sends the request | To verify the signature, compute `HMAC-SHA256` of the string `v0:{timestamp}:{raw_body}` using your secret, then compare it to the value in `x-zm-signature`. Use a timing-safe comparison to prevent timing attacks. Reject the request if the signatures do not match or if the timestamp is stale. ```javascript const message = `v0:${timestamp}:${rawBody}`; const expected = `sha256=${crypto.createHmac("sha256", secret).update(message).digest("hex")}`; const isValid = crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected), ); ```