API Reference
One key, eight endpoints, one currency. Every response is JSON, every request is a plain HTTP call — no SDK, no OAuth dance, no quota units to budget.
Introduction
The base URL is https://api.transcriptapi.io. All endpoints are served over HTTPS; plain HTTP is redirected. Requests need no body unless the reference says otherwise, and parameters go in the query string.
# every call looks like this curl "https://api.transcriptapi.io/transcript?video_id=dQw4w9WgXcQ" \ -H "Authorization: Bearer $API_KEY"
Authentication
Pass your API key as a bearer token on every request. Keys are created on first sign-in and shown in your dashboard.
Authorization: Bearer ta_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
A missing, malformed, or unknown key returns 401. Keys carry no scopes — one key reaches every endpoint on this page.
Your key is your balance. Calling the API straight from a browser or mobile app exposes it to anyone who opens devtools. Proxy through your own backend instead.
Credits
One credit is one billable call. New accounts start with 20 free credits, no card required.
- Every endpoint below costs 1 credit, except where noted.
- Translation adds 1 credit per 40 transcript segments, minimum 2 — a long video genuinely costs more to translate, so it is priced that way rather than as a flat fee.
- Cache hits are free. A repeated request served from cache never touches your balance.
- Failures are refunded. If a request is charged and then fails, the credits are returned automatically and the response says so.
The exact cost of a call is recorded per request, so your dashboard total always reconciles against what you were actually served.
Errors
Errors use standard status codes and always carry a detail string.
| Status | Meaning | Credits |
|---|---|---|
401 | Missing or invalid API key | Not charged |
402 | Insufficient credits for this call | Not charged |
422 | A parameter is missing or out of range | Not charged |
502 | YouTube could not be reached or returned something unusable | Refunded |
{
"detail": "Upstream request failed: IpBlocked. Your credits have been refunded."
}Caching & limits
Responses are cached server-side and served free of charge on a repeat request. Transcripts are cached indefinitely — a transcript does not change once published. Search and channel search are cached for one hour, channel listings and playlists for six.
There is no enforced request-rate limit today. Please stay reasonable; one will be introduced before it becomes a problem, and it will be documented here first.
Returns the full timed transcript of a public video. Works with both auto-generated and manually uploaded captions.
| Parameter | Type | Default | Description |
|---|---|---|---|
video_id required | string | — | The YouTube video ID — the v= part of a watch URL. |
language optional | string | en | Preferred caption language code. Falls back to English, then to whatever the video actually has. |
translate_to optional | string | — | Translate the transcript into this language using AI. Accepts a code or a name, e.g. de or German. |
curl "https://api.transcriptapi.io/transcript?video_id=dQw4w9WgXcQ&translate_to=de" \ -H "Authorization: Bearer $API_KEY"
{
"video_id": "dQw4w9WgXcQ",
"translated_to": "de",
"transcript": [
{
"start": 0.0,
"duration": 3.52,
"text": "Wir kennen uns beide gut aus"
}
]
}start and duration are seconds from the beginning of the video, rounded to two decimals. Segments are returned in playback order.
Search all of YouTube and get structured results back.
| Parameter | Type | Default | Description |
|---|---|---|---|
q required | string | — | The search query. |
limit optional | integer | 20 | How many results to return, 1–50. |
{
"query": "fastapi tutorial",
"results": [
{
"id": "0sOvCWFmrtA",
"title": "FastAPI - A python framework",
"channel": "Amigoscode",
"views": "412K views",
"duration": "27:38"
}
]
}List a channel's uploads, newest first. Accepts a channel ID, a handle, or a custom URL slug.
| Parameter | Type | Default | Description |
|---|---|---|---|
channel_id required | string | — | Channel ID (UC…), @handle, or custom URL name. |
limit optional | integer | 50 | How many videos to return, 1–200. |
{
"channel_id": "UC_x5XG1OV2P6uZZ5FSM9Ttw",
"title": "Google for Developers",
"total": 50,
"videos": [
{
"id": "kN_iMEAi1dw",
"title": "Build a live translation broadcast app",
"views": "236 views",
"duration": "12:37",
"published": "1 hour ago"
}
]
}Search within one channel's uploads.
| Parameter | Type | Default | Description |
|---|---|---|---|
channel_id required | string | — | Channel ID, @handle, or custom URL name. |
q required | string | — | Query matched against video titles. |
limit optional | integer | 20 | How many results to return, 1–50. |
{
"channel_id": "UC_x5XG1OV2P6uZZ5FSM9Ttw",
"query": "gemini",
"results": [
{
"id": "kacf2bib-X0",
"title": "Hands on with Gemini 3.7 Flash",
"channel": "Google for Developers",
"views": "2.7K views",
"duration": "4:21"
}
]
}Read every video in a public playlist, in playlist order.
| Parameter | Type | Default | Description |
|---|---|---|---|
playlist_id required | string | — | The playlist ID — the list= part of a playlist URL. |
limit optional | integer | 100 | How many videos to return, 1–500. |
{
"playlist_id": "PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab",
"total": 16,
"videos": [
{
"id": "fNk_zzaMoSs",
"title": "Vectors | Chapter 1, Essence of linear algebra",
"views": "12M views",
"duration": "9:52",
"published": "10 years ago"
}
]
}Get a POST to your own URL when a channel publishes something new, instead of polling for it yourself. Send a JSON body.
| Parameter | Type | Default | Description |
|---|---|---|---|
channel_id required | string | — | The channel to watch. |
webhook_url required | url | — | Where the notification is delivered. Must be a valid absolute URL. |
curl -X POST "https://api.transcriptapi.io/channel/subscribe" \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d '{"channel_id":"UC_x5XG1OV2P6uZZ5FSM9Ttw","webhook_url":"https://you.example/hook"}'
Subscribing again for the same channel replaces the existing hook rather than creating a second one.
Stop notifications for a channel.
| Parameter | Type | Default | Description |
|---|---|---|---|
channel_id required | string | — | The channel to stop watching. Passed in the query string. |
curl -X DELETE "https://api.transcriptapi.io/channel/subscribe?channel_id=UC_x5XG1OV2P6uZZ5FSM9Ttw" \ -H "Authorization: Bearer $API_KEY"
Webhook payload
Subscribed channels are checked every 15 minutes. When a new upload appears, we POST this to your URL:
{
"channel_id": "UC_x5XG1OV2P6uZZ5FSM9Ttw",
"video_id": "kN_iMEAi1dw"
}There is no retry and no signature yet. Treat the payload as a nudge to go fetch the video, not as trusted input, and do not rely on it for anything you cannot reconcile by polling. The first check after subscribing only records the current latest video — you are notified from the next upload onwards.
Liveness check. Needs no API key and never costs a credit.
{ "status": "ok" }