--- title: "Dubbing API Reference" section: "Dubbing" order: 1 sidebarLabel: "API Reference" --- ## Overview The Dubbing API is an asynchronous dubbing service. Submit an audio or video URL, and receive a fully dubbed file in your target language. - **Pricing:** $0.50 per minute of input media - **Max duration:** 10 minutes - **Max file size:** 500 MB - **Min duration:** 1 second - **Supported languages:** English (`en`), Spanish (`es`), French (`fr`), German (`de`), Italian (`it`), Portuguese (`pt`), Russian (`ru`), Japanese (`ja`), Korean (`ko`), Chinese (`zh`) ## Supported Formats ### Input requirements #### Video files | Constraint | Accepted values | |---|---| | **Containers** | MP4, MOV, MKV, WebM, AVI, FLV, TS | | **Video codecs** | H.264, H.265/HEVC, VP8, VP9, AV1, ProRes, MPEG-4 | | **Audio codecs** | AAC, MP3, Opus, Vorbis, PCM, FLAC, AC-3 | | **Audio** | Must contain at least one audio track with speech | #### Audio files | Constraint | Accepted values | |---|---| | **Formats** | WAV, MP3, FLAC, OGG, M4A, AAC, WMA | | **Audio** | Must contain speech | The input must contain audible speech in a [supported language](#overview). Background music, sound effects, and multiple speakers are all handled automatically. ### Output format The output format depends on the input type: - **Video input** -- returned as **MP4** (H.264 video + AAC audio at 192 kbps). Resolution and frame rate match the input. - **Audio-only input** -- returned as **MP3** (192 kbps) or **WAV** depending on the input format. The output URL is a presigned S3 link valid for 48 hours. ### Providing a source URL The `source_url` must be a publicly accessible HTTP(S) URL that returns the media file directly (not an HTML page). Good options: - **Pinch upload endpoint** — Use the [Upload Media](#upload-media-optional) endpoint to get a presigned S3 URL. This is the simplest approach. - **Your own S3 bucket** — Generate a presigned GET URL with a long enough expiry (we recommend at least 1 hour): ```python import boto3 s3 = boto3.client("s3") url = s3.generate_presigned_url( "get_object", Params={"Bucket": "my-bucket", "Key": "media/input.mp4"}, ExpiresIn=3600, ) ``` - **Other cloud storage** — Any direct download URL works (Google Cloud Storage signed URLs, Azure Blob SAS URLs, Cloudflare R2 presigned URLs, etc.) - **CDN / public URL** — Direct links to media files (e.g., `https://cdn.example.com/video.mp4`) > **Note:** Private network URLs (localhost, internal IPs) are rejected for security. The URL must resolve to a public IP address. ## Authentication All API requests require authentication using a Bearer token in the `Authorization` header.
Authorization header
Authorization: Bearer <your-api-token>
**Base URL:** `https://api.startpinch.com` ## Endpoints ### Upload Media (optional) Returns a presigned S3 PUT URL for direct upload. Use this if you need to upload a local file before creating a dubbing job.
POST /api/dubbing/upload-url
#### Request Body
{
  "filename": "string",
  "content_type": "string"
}
**Parameters:** | Field | Type | Required | Description | | --- | --- | --- | --- | | `filename` | string | Yes | Name of the file (e.g., `"video.mp4"`, `"audio.mp3"`) | | `content_type` | string | Yes | MIME type of the file (e.g., `"video/mp4"`) | #### Response
{
  "upload_url": "string",
  "source_url": "string",
  "upload_id": "string",
  "max_file_size_bytes": 0,
  "expires_in_sec": 0
}
**Response Fields:** | Field | Type | Description | | --- | --- | --- | | `upload_url` | string | Presigned S3 PUT URL for uploading the file | | `source_url` | string | The URL to use as `source_url` when creating a dubbing job | | `upload_id` | string | Unique identifier for this upload | | `max_file_size_bytes` | number | Maximum allowed file size in bytes (500 MB) | | `expires_in_sec` | number | Seconds until the upload URL expires | --- ### Create Dubbing Job Submit an audio or video file for dubbing. The file will be processed asynchronously.
POST /api/dubbing/jobs
#### Request Body
{
  "source_url": "string",
  "target_lang": "string",
  "source_lang": "string",
  "reduce_accent": "boolean",
  "translation_lag_time": "number",
  "original_speech_volume": "number"
}
**Parameters:** | Field | Type | Required | Description | | --- | --- | --- | --- | | `source_url` | string | Yes | URL of the audio or video file to dub (public URL or from upload endpoint) | | `target_lang` | string | Yes | Target language code (`en`, `es`, `fr`, `de`, `it`, `pt`, `ru`, `ja`, `ko`, `zh`) | | `source_lang` | string | No | Source language code or `"auto"` for auto-detection (default: `"auto"`) | | `reduce_accent` | boolean | No | Reduce source language accent in dubbed audio. When set to True, produces more natural target language pronunciation at the cost of slightly less voice similarity. (default: `false`) | | `translation_lag_time` | number | No | Seconds to delay translated speech after original segment start. Useful for a "live interpreter" effect. Range: 0–3. (default: `0`) | | `original_speech_volume` | number | No | Mix original speech back in at this volume (0 = off, 0.1 = background voice, 1 = full volume). Useful for a "live interpreter" effect. Range: 0–1.(default: `0`) | #### Response (201 Created)
{
  "job_id": "string",
  "status": "string",
  "source_lang": "string",
  "target_lang": "string",
  "created_at": "string",
  "limits": {
    "max_duration_sec": 0,
    "max_file_size_bytes": 0
  }
}
**Response Fields:** | Field | Type | Description | | --- | --- | --- | | `job_id` | string | Unique identifier for the dubbing job | | `status` | string | Current job status (initially `"pending"`) | | `source_lang` | string | Source language (or `"auto"` if auto-detecting) | | `target_lang` | string | Target language code | | `created_at` | string | ISO 8601 timestamp of job creation | | `limits` | object | Job limits applied | | `limits.max_duration_sec` | number | Maximum input duration in seconds | | `limits.max_file_size_bytes` | number | Maximum file size in bytes | --- ### Get Job Status Poll this endpoint to track the progress of a dubbing job.
GET /api/dubbing/jobs/{id}
#### Response
{
  "job_id": "string",
  "status": "string",
  "source_lang": "string",
  "target_lang": "string",
  "error": "string | null",
  "progress": {},
  "input_duration_sec": 0,
  "cost_usd": 0,
  "output_url": "string | null",
  "output_expires_at": "string | null",
  "created_at": "string",
  "updated_at": "string"
}
**Response Fields:** | Field | Type | Description | | --- | --- | --- | | `job_id` | string | Unique identifier for the dubbing job | | `status` | string | Current status (see Status Flow below) | | `source_lang` | string | Source language | | `target_lang` | string | Target language code | | `error` | string or null | Error message if status is `"failed"` | | `progress` | object | Progress details (stage and percent) | | `input_duration_sec` | number | Duration of the input media in seconds | | `cost_usd` | number | Cost charged for this job in USD | | `output_url` | string or null | Presigned download URL (available when `"completed"`) | | `output_expires_at` | string or null | ISO 8601 expiry time of the download URL | | `created_at` | string | ISO 8601 timestamp of job creation | | `updated_at` | string | ISO 8601 timestamp of last update | --- ### List Jobs Retrieve a paginated list of your dubbing jobs.
GET /api/dubbing/jobs?limit=20&offset=0
#### Query Parameters | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `limit` | number | No | Number of jobs to return (default: `20`, max: `100`) | | `offset` | number | No | Offset for pagination (default: `0`) | #### Response
{
  "jobs": [
    {
      "job_id": "string",
      "status": "string",
      "source_lang": "string",
      "target_lang": "string",
      "created_at": "string",
      "updated_at": "string"
    }
  ],
  "total": 0,
  "limit": 0,
  "offset": 0
}
**Response Fields:** | Field | Type | Description | | --- | --- | --- | | `jobs` | array | Array of job summary objects | | `total` | number | Total number of jobs | | `limit` | number | Limit used in the query | | `offset` | number | Offset used in the query | --- ### Get Download URL (refresh) Generates a fresh presigned download URL for a completed dubbing job. Use this if the original `output_url` has expired.
GET /api/dubbing/jobs/{id}/result
#### Response
{
  "job_id": "string",
  "download_url": "string",
  "expires_at": "string"
}
**Response Fields:** | Field | Type | Description | | --- | --- | --- | | `job_id` | string | The dubbing job identifier | | `download_url` | string | Fresh presigned download URL | | `expires_at` | string | ISO 8601 expiry time of the download URL | --- ## Status Flow Jobs progress through these statuses:
Status transitions
pending → downloading → processing → uploading → completed On failure: any status → failed
| Status | Description | | --- | --- | | `pending` | Job created, waiting to start | | `downloading` | Downloading the source media | | `processing` | Dubbing in progress (stages 1-7) | | `uploading` | Uploading the dubbed output | | `completed` | Done -- `output_url` is available | | `failed` | An error occurred -- check `error` field | During `processing`, the `progress` object contains stage details: ```json { "stage": "stage_3", "stage_name": "Translating", "percent": 42 } ``` --- ## Error Codes
// invalid_url
{
  "error": {
    "code": "invalid_url",
    "message": "The provided source URL is not a valid URL"
  }
}

// unsupported_language
{
  "error": {
    "code": "unsupported_language",
    "message": "Unsupported target language: xx"
  }
}
### Job-Level Errors When a job fails, the `error` field in the job status response contains the error code: | Error Code | Description | | --- | --- | | `video_too_long` | Input exceeds 10 minute limit | | `video_too_short` | Input is less than 1 second | | `video_too_large` | File size exceeds 500 MB limit | | `unsupported_format` | Media format or codec is not supported, or audio track is missing | | `download_failed` | Could not download the source media from the provided URL | | `processing_failed` | An internal error occurred during dubbing | --- ## Example: Full Workflow A complete JavaScript example that uploads a file, creates a dubbing job, polls for completion, and downloads the result. ```jsx const API_KEY = ''; const BASE_URL = 'https://api.startpinch.com'; const headers = { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' }; // Step 1: Get a presigned upload URL const uploadRes = await fetch(`${BASE_URL}/api/dubbing/upload-url`, { method: 'POST', headers, body: JSON.stringify({ filename: 'my-video.mp4', content_type: 'video/mp4' }) }); const { upload_url, source_url } = await uploadRes.json(); // Step 2: Upload the file via PUT const videoFile = /* your File or Blob */; await fetch(upload_url, { method: 'PUT', headers: { 'Content-Type': 'video/mp4' }, body: videoFile }); // Step 3: Create a dubbing job const jobRes = await fetch(`${BASE_URL}/api/dubbing/jobs`, { method: 'POST', headers, body: JSON.stringify({ source_url: source_url, target_lang: 'es', source_lang: 'auto' }) }); const job = await jobRes.json(); console.log('Job created:', job.job_id); // Step 4: Poll for job completion let status = job.status; while (status !== 'completed' && status !== 'failed') { await new Promise(r => setTimeout(r, 5000)); // wait 5 seconds const pollRes = await fetch(`${BASE_URL}/api/dubbing/jobs/${job.job_id}`, { headers: { 'Authorization': `Bearer ${API_KEY}` } }); const pollData = await pollRes.json(); status = pollData.status; console.log(`Status: ${status}`, pollData.progress); if (status === 'completed') { // Step 5: Download the dubbed output console.log('Download URL:', pollData.output_url); const video = await fetch(pollData.output_url); const blob = await video.blob(); // Save or use the blob as needed } if (status === 'failed') { console.error('Job failed:', pollData.error); } } ``` --- ## Guides - [How to Dub Videos with Python](/guides/how-to-dub-videos-with-python) — complete Python tutorial with batch processing and error handling - [AI Video Dubbing API](/guides/ai-video-dubbing-api) — how dubbing APIs work and what to look for - [Dubbing API Pricing Comparison](/guides/dubbing-api-pricing-comparison) — Pinch vs ElevenLabs vs Rask.ai