Dubbing 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. 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 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):
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.
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.
/api/dubbing/upload-url
{
"upload_url": "https://...",
"source_url": "https://...",
"upload_id": "...",
"max_file_size_bytes": 500000000,
"expires_in_sec": 3600
}
Request Body
{
"filename": "string",
"content_type": "string"
}
{
"filename": "video.mp4",
"content_type": "video/mp4"
}
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
}
{
"upload_url": "https://s3.amazonaws.com/pinch-dubbing/uploads/abc123?X-Amz-Algorithm=...",
"source_url": "https://s3.amazonaws.com/pinch-dubbing/uploads/abc123",
"upload_id": "abc123",
"max_file_size_bytes": 500000000,
"expires_in_sec": 3600
}
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.
/api/dubbing/jobs
{
"job_id": "...",
"status": "pending",
"source_lang": "auto",
"target_lang": "es",
"created_at": "..."
}
Request Body
{
"source_url": "string",
"target_lang": "string",
"source_lang": "string",
"reduce_accent": "boolean",
"translation_lag_time": "number",
"original_speech_volume": "number"
}
{
"source_url": "https://s3.amazonaws.com/pinch-dubbing/uploads/abc123",
"target_lang": "es",
"source_lang": "auto",
"reduce_accent": false,
"translation_lag_time": 0,
"original_speech_volume": 0
}
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
}
}
{
"job_id": "dub_7f3a1b2c",
"status": "pending",
"source_lang": "auto",
"target_lang": "es",
"created_at": "2026-03-05T14:30:00Z",
"limits": {
"max_duration_sec": 600,
"max_file_size_bytes": 500000000
}
}
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.
/api/dubbing/jobs/{id}
{
"job_id": "...",
"status": "completed",
"output_url": "https://..."
}
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"
}
{
"job_id": "dub_7f3a1b2c",
"status": "completed",
"source_lang": "auto",
"target_lang": "es",
"error": null,
"progress": {
"stage": "completed",
"percent": 100
},
"input_duration_sec": 61.5,
"cost_usd": 0.51,
"output_url": "https://s3.amazonaws.com/pinch-dubbing/results/dub_7f3a1b2c.mp4?X-Amz-Algorithm=...",
"output_expires_at": "2026-03-06T14:30:00Z",
"created_at": "2026-03-05T14:30:00Z",
"updated_at": "2026-03-05T14:35:22Z"
}
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.
/api/dubbing/jobs?limit=20&offset=0
{
"jobs": [...],
"total": 5,
"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
}
{
"jobs": [
{
"job_id": "dub_7f3a1b2c",
"status": "completed",
"source_lang": "en",
"target_lang": "es",
"created_at": "2026-03-05T14:30:00Z",
"updated_at": "2026-03-05T14:35:22Z"
},
{
"job_id": "dub_9e2d4f8a",
"status": "processing",
"source_lang": "auto",
"target_lang": "fr",
"created_at": "2026-03-05T15:00:00Z",
"updated_at": "2026-03-05T15:02:10Z"
}
],
"total": 5,
"limit": 20,
"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.
/api/dubbing/jobs/{id}/result
{
"job_id": "...",
"download_url": "https://...",
"expires_at": "..."
}
Response
{
"job_id": "string",
"download_url": "string",
"expires_at": "string"
}
{
"job_id": "dub_7f3a1b2c",
"download_url": "https://s3.amazonaws.com/pinch-dubbing/results/dub_7f3a1b2c.mp4?X-Amz-Algorithm=...",
"expires_at": "2026-03-06T14:30:00Z"
}
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:
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:
{
"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” } }
{
"error": {
"code": "unauthorized",
"message": "Invalid or expired API token"
}
}
{
"error": {
"code": "insufficient_balance",
"message": "Your account balance is too low to process this file"
}
}
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Maximum 10 requests per minute."
}
}
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.
const API_KEY = '<your-api-token>';
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 — complete Python tutorial with batch processing and error handling
- AI Video Dubbing API — how dubbing APIs work and what to look for
- Dubbing API Pricing Comparison — Pinch vs ElevenLabs vs Rask.ai