How does the TubeTranscript REST API v1 work?
By TubeTranscript · Content updated
View as MarkdownThe TubeTranscript REST API v1 exposes one synchronous endpoint, POST /api/v1/extract, that returns a YouTube video's timestamped transcript and optional AI features in a single JSON payload. It requires a Pro plan API key sent as a Bearer Token, and every response uses one envelope: success, data, error.
For a shorter introduction with curl, Python and JavaScript samples, read the YouTube transcript API overview.
How do I call the extract endpoint?
Send POST https://tubetranscript.io/api/v1/extract with header Content-Type: application/json and a JSON body containing a YouTube url. The optional features array requests AI outputs. GET, PUT, PATCH and DELETE return 405 METHOD_NOT_ALLOWED. Set your HTTP client timeout to 300 seconds, because audio transcription can take minutes.
curl -X POST https://tubetranscript.io/api/v1/extract \
-H "Authorization: Bearer $TUBETRANSCRIPT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://www.youtube.com/watch?v=jNQXAC9IVRw", "features": ["hooks", "mindmap", "action_items"]}'
| Property | Value |
|---|---|
| Method | POST |
| URL | https://tubetranscript.io/api/v1/extract |
| Headers | Authorization: Bearer <API_KEY>, Content-Type: application/json |
| Client timeout | 300 s |
| Other methods | GET, PUT, PATCH, DELETE → 405 METHOD_NOT_ALLOWED |
| Field | Type | Required | Default | Constraints |
|---|---|---|---|---|
url |
string | yes | none | Any YouTube video URL: watch, youtu.be, shorts, embed, live. Max 2,048 characters. |
features |
string[] | no | [] (transcript only) |
Any of hooks, mindmap, action_items, each at most once. |
How do I authenticate API requests?
Authenticate every request with the header Authorization: Bearer <API_KEY>. Only API keys belonging to a team on the Pro plan are accepted. A missing or unknown key returns 401 with MISSING_API_KEY or INVALID_API_KEY; a key whose team is not Pro returns 403 PLAN_UPGRADE_REQUIRED. Key handling rules are in Security & Rate Limits.
Authorization: Bearer <API_KEY>
| Condition | HTTP | error.code |
Extra header |
|---|---|---|---|
No Authorization header |
401 | MISSING_API_KEY |
WWW-Authenticate: Bearer |
| Malformed, unknown or revoked key | 401 | INVALID_API_KEY |
WWW-Authenticate: Bearer |
| Key's team not on Pro | 403 | PLAN_UPGRADE_REQUIRED |
none |
What JSON does the endpoint return?
Every response, success or error, returns the same envelope: success (boolean), data (result object or null), and error (null or an object with code, message, optional details). On success, data holds video_id, source, language, the transcript as plain text plus timed chunks, and requested features. Responses carry Cache-Control: no-store.
{
"success": true,
"data": {
"video_id": "jNQXAC9IVRw",
"job_id": null,
"source": "captions",
"language": "en",
"video_title": null,
"channel_name": null,
"transcript": {
"text": "…",
"chunks": [{ "start": 0, "duration": 4.2, "text": "…" }]
},
"features": {
"hooks": [{ "start_time": "…", "end_time": "…", "hook_title": "…", "summary": "…" }],
"mindmap": "mindmap\n Root\n Branch",
"action_items": [{ "assignee": null, "task": "…", "deadline_mentioned": null }]
}
},
"error": null
}
{ "success": false, "data": null, "error": { "code": "INVALID_INPUT", "message": "…" } }
| Envelope field | Type | On success | On error |
|---|---|---|---|
success |
boolean | true |
false |
data |
object | null | Result object | null, except for 202 and 502 |
error |
object | null | null |
{ code, message, details? } |
data field |
Type | Notes |
|---|---|---|
video_id |
string | 11-character YouTube video ID parsed from url. |
job_id |
string | null | Set when the transcript came from an extraction job. |
source |
string | captions, audio or existing_job. |
language |
string | null | Caption language code; set when source is captions. |
video_title |
string | null | Set when the transcript came from a job. |
channel_name |
string | null | Set when the transcript came from a job. |
transcript.text |
string | All chunk texts joined by blank lines (non-timestamped). |
transcript.chunks |
array | { start, duration, text }, start and duration in seconds. |
features |
object | Only the features requested in the body. |
What does the source field mean?
The source field reports where the transcript came from. captions means the video's YouTube captions were fetched synchronously (Tier 1). audio means the audio was transcribed with Whisper (Tier 2). existing_job means your team already had a completed or running job for the same video, so no new extraction ran.
source value |
Engine | Typical latency | job_id |
|---|---|---|---|
existing_job |
Reused team job | Instant, or waits for the running job | Set |
captions |
YouTube captions (Tier 1) | Seconds | null |
audio |
Whisper transcription (Tier 2) | Minutes | Set |
How does TubeTranscript choose between captions and Whisper?
TubeTranscript picks the cheapest available transcript source automatically, in a fixed order. It first reuses your team's existing job for the video, then fetches YouTube captions (Tier 1, seconds), and falls back to Whisper audio transcription (Tier 2, minutes) only when the video has no captions. Callers never select the engine; the same request works for every video.
- Existing job: a completed or still-running job of your team for the same video (the call waits for a running one).
- Tier 1, captions: returned in seconds when the video has captions.
- Tier 2, Whisper: audio transcription when no captions exist; can take minutes.
What happens when Whisper transcription takes too long?
A Tier 2 call waits up to about 280 seconds without features, or about 210 seconds with features, staying inside the 300-second timeout of Make and n8n. If Whisper is still running, the API returns 202 EXTRACTION_IN_PROGRESS with data.job_id and Retry-After: 60. Send the identical request again; it resumes the same job.
| Request | Maximum wait for Whisper | Response if unfinished |
|---|---|---|
Without features |
~280 s | 202 EXTRACTION_IN_PROGRESS, Retry-After: 60 |
With features |
~210 s | 202 EXTRACTION_IN_PROGRESS, Retry-After: 60 |
What are the API rate limits?
Each Pro API key allows 1000 requests per 24 hours, and every call to /api/v1/extract counts once, regardless of how many features it requests. Remaining quota is reported in the X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset response headers. Exceeding it returns 429 RATE_LIMIT_EXCEEDED with a Retry-After header. Details: Security & Rate Limits.
| Tier | Request Limit | REST API v1 | MCP Support | Transcript Storage |
|---|---|---|---|---|
| Guest (no account) | 10 transcripts / 24 h per IP (max 5 Whisper) | No | No | Public page under /c/, no automatic expiry |
| Free account | 24 transcripts / 24 h per team | No | No | Public page under /c/, no automatic expiry |
| Pro | 1,000 requests / 24 h per API key | Yes | Yes (/api/mcp, /api/mcp/tools) |
Private to the team, no automatic expiry |
| Response header | Meaning |
|---|---|
X-RateLimit-Limit |
Requests allowed per 24-hour sliding window. |
X-RateLimit-Remaining |
Requests left in the current window. |
X-RateLimit-Reset |
When the window resets. |
Retry-After |
Seconds to wait; sent with 429. |
Which error codes can the API return?
Errors use the standard envelope with success: false and a machine-readable error.code. Branch on error.code, not on the message text. Only 202 EXTRACTION_IN_PROGRESS and 502 FEATURES_FAILED include a non-null data: the first carries the job ID to retry, the second carries the transcript and every feature that succeeded.
| HTTP | error.code |
Meaning | Action | Retry |
|---|---|---|---|---|
| 202 | EXTRACTION_IN_PROGRESS |
Whisper still running. | Resend the same request after Retry-After seconds. |
Yes |
| 400 | INVALID_JSON |
Body is not valid JSON. | Fix the request body. | No |
| 400 | INVALID_INPUT |
url or features is invalid. |
Fix the field named in message. |
No |
| 401 | MISSING_API_KEY |
No Authorization header. |
Send Authorization: Bearer <API_KEY>. |
No |
| 401 | INVALID_API_KEY |
Malformed, unknown or revoked key. | Use a valid Pro key. | No |
| 403 | PLAN_UPGRADE_REQUIRED |
Key's team is not on Pro. | Upgrade the team to Pro. | No |
| 404 | TRANSCRIPT_NOT_FOUND |
Video unavailable. | Check the URL. | No |
| 405 | METHOD_NOT_ALLOWED |
Method other than POST. |
Use POST. |
No |
| 422 | EXTRACTION_FAILED |
Video could not be transcribed (private, removed, no speech). | Try another video. | No |
| 429 | RATE_LIMIT_EXCEEDED |
24-hour quota used up. | Wait Retry-After seconds. |
Yes |
| 500 | INTERNAL_ERROR |
Unexpected server error. | Retry later. | Yes |
| 500 | WORKER_ERROR |
Extraction worker error. | Retry later. | Yes |
| 502 | FEATURES_FAILED |
Some AI features failed; data holds the transcript and successful features, error.details lists failures. |
Retry for the failed features. | Yes |
| 503 | WORKER_UNAVAILABLE |
Extraction service down or in maintenance. | Retry later. | Yes |
| 503 | RATE_LIMIT_UNAVAILABLE |
Quota store unreachable. | Wait Retry-After seconds (60). |
Yes |
Which AI features can I request?
The features array accepts three values: hooks (timestamped attention hooks with title and summary), mindmap (a Mermaid mindmap string), and action_items (tasks with assignee and mentioned deadline). Transcripts above 120,000 characters, roughly 2 hours of speech, are processed in segments and merged. An empty array returns the transcript only.
features value |
Output in data.features |
Item fields |
|---|---|---|
hooks |
Array of hooks | start_time, end_time, hook_title, summary |
mindmap |
Mermaid mindmap string | none |
action_items |
Array of tasks | assignee, task, deadline_mentioned |
How do I call the creator tools?
POST /api/v1/creator/{tool} runs one creator tool on a video and answers in the same envelope as /api/v1/extract. It needs a Pro API key. Send exactly one of job_id (a completed job of your team, from /api/v1/extract or the MCP get_transcript) or url (a YouTube link; captions only, nothing is stored), plus optional options.
curl -X POST https://tubetranscript.io/api/v1/creator/generate_chapters \
-H "Authorization: Bearer $TUBETRANSCRIPT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "options": {"count": 8}}'
{tool} |
options |
data (besides tool, video_id) |
|---|---|---|
generate_chapters |
style (short, descriptive, emoji), count (3-40) |
chapters (start, clock, title), text |
generate_description |
tone (neutral, friendly, professional, energetic), cta, chapters |
description, tags, hashtags, chars |
find_moments |
length (any, short, medium, long) |
moments (start, end, duration, hook, reason, quote, standalone) |
analyze_hook |
topic (for rewritten hooks) |
opening, hookType, parts, pace, rewrites |
translate_subtitles |
target_languages (1-5 ISO codes), format (srt, vtt, txt), glossary |
outputs (language, name, text, untranslated, warnings), failed |
Times are taken from the transcript lines by code; the model never writes them. Every item is checked against the transcript, and items that fail are left out.
Limits. Each call counts once against the Pro daily limit and is one creator-tool AI run (30 per hour per team; translate_subtitles counts one per language). A run is charged only when it returns a result: UNRELIABLE (too few results passed the checks) and provider errors are free, though at most 90 model calls per hour are allowed. A result already made for the same public video and transcript is returned free. One run per team at a time; a second one gets BUSY.
| HTTP | error.code |
|---|---|
| 400 | INVALID_JSON, INVALID_INPUT |
| 404 | UNKNOWN_TOOL, JOB_NOT_FOUND, TRANSCRIPT_NOT_FOUND |
| 409 | JOB_NOT_READY, BUSY (another run of your team is working) |
| 413 | VIDEO_TOO_LONG (over 120 minutes), TRANSCRIPT_TOO_LONG, FILE_TOO_LARGE |
| 422 | UNRELIABLE |
| 429 | RATE_LIMIT_EXCEEDED, QUOTA_EXCEEDED, BUDGET_REACHED |
| 502 | AI_* provider or validation errors |
| 503 | AI_DISABLED, QUOTA_UNAVAILABLE, WORKER_UNAVAILABLE |