Pinch PINCH

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

ConstraintAccepted values
ContainersMP4, MOV, MKV, WebM, AVI, FLV, TS
Video codecsH.264, H.265/HEVC, VP8, VP9, AV1, ProRes, MPEG-4
Audio codecsAAC, MP3, Opus, Vorbis, PCM, FLAC, AC-3
AudioMust contain at least one audio track with speech

Audio files

ConstraintAccepted values
FormatsWAV, MP3, FLAC, OGG, M4A, AAC, WMA
AudioMust 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.

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:

FieldTypeRequiredDescription
filenamestringYesName of the file (e.g., "video.mp4", "audio.mp3")
content_typestringYesMIME 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:

FieldTypeDescription
upload_urlstringPresigned S3 PUT URL for uploading the file
source_urlstringThe URL to use as source_url when creating a dubbing job
upload_idstringUnique identifier for this upload
max_file_size_bytesnumberMaximum allowed file size in bytes (500 MB)
expires_in_secnumberSeconds 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:

FieldTypeRequiredDescription
source_urlstringYesURL of the audio or video file to dub (public URL or from upload endpoint)
target_langstringYesTarget language code (en, es, fr, de, it, pt, ru, ja, ko, zh)
source_langstringNoSource language code or "auto" for auto-detection (default: "auto")
reduce_accentbooleanNoReduce 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_timenumberNoSeconds to delay translated speech after original segment start. Useful for a “live interpreter” effect. Range: 0–3. (default: 0)
original_speech_volumenumberNoMix 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:

FieldTypeDescription
job_idstringUnique identifier for the dubbing job
statusstringCurrent job status (initially "pending")
source_langstringSource language (or "auto" if auto-detecting)
target_langstringTarget language code
created_atstringISO 8601 timestamp of job creation
limitsobjectJob limits applied
limits.max_duration_secnumberMaximum input duration in seconds
limits.max_file_size_bytesnumberMaximum 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:

FieldTypeDescription
job_idstringUnique identifier for the dubbing job
statusstringCurrent status (see Status Flow below)
source_langstringSource language
target_langstringTarget language code
errorstring or nullError message if status is "failed"
progressobjectProgress details (stage and percent)
input_duration_secnumberDuration of the input media in seconds
cost_usdnumberCost charged for this job in USD
output_urlstring or nullPresigned download URL (available when "completed")
output_expires_atstring or nullISO 8601 expiry time of the download URL
created_atstringISO 8601 timestamp of job creation
updated_atstringISO 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

ParameterTypeRequiredDescription
limitnumberNoNumber of jobs to return (default: 20, max: 100)
offsetnumberNoOffset 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:

FieldTypeDescription
jobsarrayArray of job summary objects
totalnumberTotal number of jobs
limitnumberLimit used in the query
offsetnumberOffset 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:

FieldTypeDescription
job_idstringThe dubbing job identifier
download_urlstringFresh presigned download URL
expires_atstringISO 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

StatusDescription
pendingJob created, waiting to start
downloadingDownloading the source media
processingDubbing in progress (stages 1-7)
uploadingUploading the dubbed output
completedDone — output_url is available
failedAn 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” } }

Job-Level Errors

When a job fails, the error field in the job status response contains the error code:

Error CodeDescription
video_too_longInput exceeds 10 minute limit
video_too_shortInput is less than 1 second
video_too_largeFile size exceeds 500 MB limit
unsupported_formatMedia format or codec is not supported, or audio track is missing
download_failedCould not download the source media from the provided URL
processing_failedAn 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