1. Sign up
Create an account and verify your email.
Quickstart
Create an API key in the dashboard, send an audio file to POST /v1/transcribe, and read the
transcript from the JSON response.
Create an account and verify your email.
Open the dashboard and create an API key.
Upload audio as multipart form data.
curl -X POST https://api.transcribeapi.com/v1/transcribe \
-H "Authorization: Bearer YOUR_API_KEY" \
-F "file=@audio.mp3" \
-F "language=en" \
-F "model=whisper-large-v3-turbo" \
-F "vtt_granularity=sentence"
SDKs
The SDKs expose the same API surface as REST, but choose the right upload flow automatically and return plain JSON objects.
pip install transcribe-api
from transcribe_api import TranscribeAPI
client = TranscribeAPI(api_key="YOUR_API_KEY")
result = client.transcribe(
file="meeting.mp3",
language="en",
vtt_granularity="sentence",
)
print(result["text"])
npm install @transcribe-api/sdk
import { TranscribeAPI } from "@transcribe-api/sdk";
const client = new TranscribeAPI({ apiKey: "YOUR_API_KEY" });
const result = await client.transcribe({
file: "meeting.mp3",
language: "en",
vttGranularity: "sentence",
});
console.log(result.text);
Local file paths and remote signed URLs are supported by the SDKs.
client.batch.transcribe() creates the job, uploads files, and returns a job id.
The Node.js SDK can resume multipart uploads from local path uploads.
Sync transcription
POST /v1/transcribe returns the transcript in the HTTP response.Use sync when the caller should wait for the transcript. Send one audio file as multipart form data.
| Field | Type | Required | Description |
|---|---|---|---|
file |
File | Yes | Audio file in multipart form data. |
model |
String | No | Model id. Defaults to whisper-large-v3-turbo. |
language |
String | No | Language code such as en. Omit for auto-detect. |
task |
String | No | transcribe or translate. |
vad_filter |
Boolean | No | Set to true to filter non-speech sections. |
initial_prompt |
String | No | Context for names, acronyms, product terms, or vocabulary. |
vtt_granularity |
String | No | sentence or word. |
exclude |
String | No | Comma-separated fields to omit from the response. |
Pass one of these values in language, or omit the field to auto-detect.
Afrikaans (af), Amharic (am), Arabic (ar), Assamese
(as), Azerbaijani (az), Bashkir (ba), Belarusian
(be), Bulgarian (bg), Bengali (bn), Tibetan (bo),
Breton (br), Bosnian (bs), Catalan (ca), Czech
(cs), Welsh (cy), Danish (da), German (de), Greek
(el), English (en), Spanish (es), Estonian (et),
Basque (eu), Persian (fa), Finnish (fi), Faroese
(fo), French (fr), Galician (gl), Gujarati (gu),
Hausa (ha), Hawaiian (haw), Hebrew (he), Hindi
(hi), Croatian (hr), Haitian Creole (ht), Hungarian
(hu), Armenian (hy), Indonesian (id), Icelandic
(is), Italian (it), Japanese (ja), Javanese (jw),
Georgian (ka), Kazakh (kk), Khmer (km), Kannada
(kn), Korean (ko), Latin (la), Luxembourgish
(lb), Lingala (ln), Lao (lo), Lithuanian (lt),
Latvian (lv), Malagasy (mg), Maori (mi), Macedonian
(mk), Malayalam (ml), Mongolian (mn), Marathi
(mr), Malay (ms), Maltese (mt), Myanmar (my),
Nepali (ne), Dutch (nl), Norwegian Nynorsk (nn), Norwegian
(no), Occitan (oc), Punjabi (pa), Polish (pl),
Pashto (ps), Portuguese (pt), Romanian (ro), Russian
(ru), Sanskrit (sa), Sindhi (sd), Sinhala (si),
Slovak (sk), Slovenian (sl), Shona (sn), Somali
(so), Albanian (sq), Serbian (sr), Sundanese
(su), Swedish (sv), Swahili (sw), Tamil (ta),
Telugu (te), Tajik (tg), Thai (th), Turkmen
(tk), Tagalog (tl), Turkish (tr), Tatar (tt),
Ukrainian (uk), Urdu (ur), Uzbek (uz), Vietnamese
(vi), Yiddish (yi), Yoruba (yo), Chinese (zh),
Cantonese (yue)
{
"job_id": "job_...",
"billed_seconds": 600,
"price_per_hour": 0.15,
"cost": 0.025,
"remaining_balance": 152.4758,
"text": "The full transcript...",
"vtt": "WEBVTT\\n\\n00:00.000 --> 00:04.120\\n...",
"segments": [
{
"id": 0,
"start_seconds": 0,
"end_seconds": 4.12,
"text": "The first sentence."
}
],
"detected_language": "en"
}
{
"error": "Attach an audio file in the `file` field.",
"code": "missing_file"
}
Async jobs
Async jobs support direct uploads and remote signed URLs. Create the job, upload or import audio, then poll the job or receive a webhook.
{
"type": "big_file",
"size_bytes": 157286400,
"content_type": "audio/mpeg",
"model": "whisper-large-v3-turbo",
"webhook_url": "https://example.com/transcribe-webhook"
}
{
"type": "big_file",
"source": {
"type": "url",
"url": "https://storage.example.com/audio.mp3?signature=..."
},
"model": "whisper-large-v3-turbo",
"webhook_url": "https://example.com/transcribe-webhook"
}
| Step | Endpoint | Use |
|---|---|---|
| Create | POST /v1/jobs |
Create a big_file job. |
| Resume | POST /v1/jobs/{job_id}/resume-upload |
Refresh upload instructions for an active upload. |
| Status | GET /v1/transcribe/{job_id} |
Poll job metadata and status. |
| Upload complete | POST /v1/transcribe/{job_id}/upload-completed |
Start processing after all uploads for the job have finished. |
{
"job_id": "job_123",
"status": "uploading",
"total_files": 1,
"uploads": [
{
"reference_id": "file_000001",
"upload": {
"type": "put",
"url": "https://...",
"headers": {}
}
}
]
}
{
"job_id": "job_123",
"job_status": "queued",
"billed_seconds": 0,
"cost": 0,
"total_files": 1
}
{
"job_id": "job_123",
"status": "completed",
"billed_seconds": 709,
"price_per_hour": 0.15,
"cost": 0.029542,
"remaining_balance": 4.970458,
"result_url": "https://...",
"expires_at": "2026-06-04T12:00:00.000Z"
}
{
"job_id": "job_123",
"upload": {
"type": "multipart",
"key": "usr_123/job_123/audio.mp3",
"upload_id": "4~VJ...",
"part_size": 5242880,
"parts": [
{
"part_number": 1,
"url": "https://..."
}
],
"complete_url": "https://...",
"abort_url": "https://..."
}
}
Jobs move through a finite set of status values. The API returns these raw values from
GET /v1/transcribe/{job_id}.
| Status | Meaning | Terminal |
|---|---|---|
uploading |
Client upload is still in progress. | No |
verifying |
Uploaded or imported audio is being validated and prepared for transcription. | No |
queued |
Job is ready and waiting for transcription execution. | No |
completed |
Job finished and webhook delivery succeeded or no webhook was configured. | Yes |
completed_with_errors |
Batch job finished with per-file failures, and the result is available. | Yes |
result_available |
Job finished, but webhook delivery did not succeed. Fetch the result manually. | Yes |
result_available_with_errors |
Batch job finished with per-file failures and webhook delivery did not succeed. | Yes |
failed_* |
Terminal failure during upload validation, import, probing, verification, billing reconciliation, or result finalization. | Yes |
Poll GET /v1/transcribe/{job_id}. When status becomes completed, the response
includes result_url.
Public terminal failure values use the failed_* pattern. Examples include
failed_batch_file_count_mismatch, failed_batch_size_mismatch,
failed_batch_too_large, failed_batch_total_size_mismatch,
failed_insufficient_credits, failed_manifest_mismatch,
failed_missing_batch_file, failed_result_missing,
failed_source_missing, failed_unsupported_batch_format,
failed_upload_missing, and failed_upload_size_mismatch.
Batch jobs
Batch is async only. The SDK is the simplest path because it creates the manifest, uploads files, and returns the job id.
const batch = await client.batch.transcribe({
files: [
{ reference_id: "episode_1", file: "episode-1.mp3" },
{ reference_id: "episode_2", file: "episode-2.wav" },
],
webhookUrl: "https://example.com/transcribe-webhook",
});
const job = await client.jobs.get(batch.job_id);
const resultUrl = job.result_url ?? null;
{
"type": "batch",
"files": [
{
"reference_id": "file_000001",
"url": "https://storage.example.com/one.mp3?signature=..."
}
],
"webhook_url": "https://example.com/transcribe-webhook"
}
After creating a batch job, call POST /v1/jobs/{job_id}/upload-urls for upload URLs.
Use signed HTTPS URLs when files already live in S3, R2, GCS, or another storage provider.
Use the webhook payload or poll GET /v1/transcribe/{job_id} until result_url is returned.
{
"job_id": "job_123",
"uploads": [
{
"file_id": "file_000001",
"filename": "episode-1.mp3",
"key": "usr_123/job_123/file_000001.mp3",
"upload": {
"type": "put",
"key": "usr_123/job_123/file_000001.mp3",
"url": "https://...",
"headers": {
"Content-Type": "audio/mpeg"
}
}
}
]
}
The upload object returned by POST /v1/jobs,
POST /v1/jobs/{job_id}/upload-urls, and
POST /v1/jobs/{job_id}/resume-upload is either a simple signed put upload or a
multipart upload with upload_id, part URLs, complete_url, and
abort_url.
Webhooks
Pass webhook_url when creating a job or webhookUrl in the SDK. Your endpoint
should accept JSON and return a 2xx response. Webhook requests are signed with Ed25519.
{
"type": "transcription.completed",
"job_id": "job_...",
"status": "completed",
"result_url": "https://...",
"expires_at": "2026-06-04T18:00:00.000Z"
}
Successful jobs use completed. Batch jobs can also return completed_with_errors.
If your endpoint misses an event, poll GET /v1/transcribe/{job_id}.
The signed result URL contains the transcript data for the finished job.
Each webhook includes X-TranscribeAPI-Timestamp, X-TranscribeAPI-Signature, and
X-TranscribeAPI-Key-Id. Verify the signature against the raw request body using the public key
published at GET /.well-known/transcribeapi-webhook-keys.json.
${timestamp}.${raw_request_body}
Reject old timestamps, for example anything older than 5 minutes, and make webhook processing idempotent.
The current key id is v1.
Billing
Sync transcription charges after processing. Async and batch jobs reserve credit when the job starts and finalize the charge when the result is ready.
billed_seconds is the rounded billable duration.cost is the final request cost.remaining_balance is the account balance after the charge.Pricing
Pricing is linear and based on billable audio seconds. Each transcription is rounded up to the next second.
Responses include price_per_hour so charges can be reconciled.
Example cost for 10 minutes of billable audio.
Example cost for one hour of billable audio.
Async and batch use the same transcription rate.
Limits
These limits apply to public API usage. Use async jobs or batch jobs when a request does not fit sync.
| Limit | Free | Pay as you go |
|---|---|---|
| Transcription requests | 10 requests/min | 300 requests/min |
| Sync file size | 30 MB | 30 MB |
| Sync duration | 10 minutes | 10 minutes |
| Batch files | 10,000 files | 10,000 files |
| Batch total size | 10 GB | 10 GB |
| API keys | 5 keys | 5 keys |
When rate limited, the API returns rate_limit_exceeded with the active plan limit.
Upload URLs are valid for 1 hour. Result URLs are valid for 7 days.
Use audio files: mp3, mpeg, mpga, m4a, wav, or webm. MP4 video uploads are not supported.
Errors
Error responses use the shape { "error": "...", "code": "..." }. Some codes
also include extra fields such as limits, required credit, or the current job status.
| Status | Meaning |
|---|---|
400 |
Invalid request body, invalid form fields, upload metadata, or sync limit violation. |
401 |
Missing or invalid API key. |
402 |
Account balance is too low for the requested transcription. |
403 |
API key belongs to an account with an unverified email. |
404 |
Route or job was not found. |
409 |
Job exists, but the requested action is not valid in its current state. |
429 |
Per-minute transcription rate limit was exceeded. |
500 |
Unexpected server-side failure. |
502 |
Temporary server-side transcription failure. |
| Status | Code | Meaning |
|---|---|---|
| 400 | audio_too_long |
Sync transcription exceeded the 10 minute limit. Use async jobs. |
| 400 | batch_too_large |
Batch total size exceeded 10 GB. |
| 400 | file_too_large |
Sync upload exceeded the 30 MB file size limit. |
| 400 | invalid_file_metadata |
Batch upload file metadata or file format is invalid. |
| 400 | invalid_form_data |
Request was not valid multipart/form-data. |
| 400 | invalid_job_request |
Async job request body failed validation. |
| 400 | invalid_json |
Request body was not valid JSON. |
| 400 | invalid_request_options |
One or more sync transcription fields had invalid values. |
| 400 | invalid_upload |
Resume-upload payload is missing required upload fields or has invalid values. |
| 400 | invalid_upload_page |
Batch upload URL request included too few or too many files. |
| 400 | invalid_upload_type |
upload.type must be multipart or put. |
| 400 | missing_file |
No audio file was attached in the file field. |
| 400 | too_many_files |
Batch job exceeded the maximum file count. |
| 401 | invalid_api_key |
API key is invalid. |
| 401 | missing_api_key |
Authorization: Bearer YOUR_API_KEY header was missing. |
| 402 | insufficient_credit |
Account does not have enough credit for sync or async transcription. |
| 403 | email_not_verified |
The API key owner must verify their email before using the API. |
| 404 | job_not_found |
The requested async or batch job does not exist for this account. |
| 404 | not_found |
The requested route does not exist. |
| 409 | invalid_job_status |
The requested upload action is not allowed for the job’s current status. |
| 409 | result_not_available |
Job result URL is not available yet for the current status. |
| 429 | rate_limit_exceeded |
Account exceeded the per-minute transcription request limit. |
| 500 | internal_error |
The API hit an unexpected server error. |
| 502 | transcription_failed |
Temporary server-side transcription failure. |
Models
whisper-large-v3-turbo.This is the public model id for sync, async, SDK, and batch transcription.
Default model for all public transcription requests.
Pass language when you know it, or omit it to detect automatically.
Latency
These are typical completion targets for healthy traffic. Actual completion depends on file duration, upload speed, and queue depth.
For short clips sent to /v1/transcribe.
For sync requests near the sync boundary.
Jobs are queued as soon as upload or import completes.
Async jobs complete in the background and report by webhook or polling.
Batch timing depends on total audio, file count, and active queue depth.
Endpoint reference
Except for /health, use API key auth with Authorization: Bearer YOUR_API_KEY.
| Method | Path | Description |
|---|---|---|
| GET | /health |
Health check for the API service. |
| GET | /.well-known/transcribeapi-webhook-keys.json |
Public webhook signing keys for signature verification. |
| POST | /v1/transcribe |
Sync transcription. Multipart audio in, transcript JSON out. |
| POST | /v1/jobs |
Create a big_file or batch async job. |
| POST | /v1/jobs/{job_id}/upload-urls |
Get signed upload URLs for batch upload pages. |
| POST | /v1/jobs/{job_id}/resume-upload |
Refresh upload instructions for an active big-file upload. |
| GET | /v1/transcribe/{job_id} |
Poll async job status and metadata. |
| POST | /v1/transcribe/{job_id}/upload-completed |
Mark uploads complete and queue the async job for processing. |