Introduction
Capslane provides one API for retrieving YouTube captions and generating transcripts when captions are unavailable.
Using Claude Code, Codex or Cursor? Install the transcript skill or connect the MCP tools. Claude Code also has a plugin that installs both.
Overview
Capslane checks the cache before applying the requested mode. A cached result can come from native captions or audio generation in any mode. Check source and cached in the response; mode controls what happens on a cache miss.
| Mode | On a cache miss |
|---|---|
| native | Fetch a native caption track. Never start audio generation. Return transcript_unavailable if no usable track exists. |
| auto | Try native captions, then start generation only after transcript_unavailable. A temporary upstream failure remains an error. |
| generate | Start or reuse an asynchronous generation job without first trying native extraction. |
Get an API key
Open your workspace, then create a server key in API Keys. Copy it immediately; the complete key is shown only once. Store it as CAPSLANE_API_KEY in your server environment.
Do not commit the key or expose it in browser and mobile bundles.
Authentication
Send the workspace API key in the x-api-key header on every request.
This first request uses native mode and never starts generation. These commands use Bash syntax and curl 7.76 or later. In PowerShell, use curl.exe and $env:CAPSLANE_API_KEY.
curl --fail-with-body --get "https://capslane.com/v1/transcript" \
--header "x-api-key: $CAPSLANE_API_KEY" \
--data-urlencode "url=dQw4w9WgXcQ" \
--data-urlencode "mode=native"The production base URL is https://capslane.com. A dashboard session cookie does not authenticate the public API.
Node.js quickstart
GET /v1/transcript returns HTTP 200 with content as a segment array by default, or a string when text=true. An accepted generation returns HTTP 202 with jobId, status and requestId, without content. Both SDK transcript methods return these same response bodies. Read content first, then retain jobId if generation was accepted.
npm install @webba_tech/capslaneimport { CapslaneClient, CapslaneError } from '@webba_tech/capslane'
const apiKey = process.env.CAPSLANE_API_KEY
if (!apiKey) throw new Error('Set CAPSLANE_API_KEY')
const client = new CapslaneClient({ apiKey, timeoutMs: 45_000 })
const signal = AbortSignal.timeout(20 * 60_000)
try {
let result = await client.transcript({ url: 'dQw4w9WgXcQ', mode: 'auto', signal })
if (!('content' in result)) {
console.error('Accepted job:', result.jobId)
result = await client.waitForTranscript(result, { signal })
}
console.log(JSON.stringify(result, null, 2))
} catch (error) {
if (error instanceof CapslaneError) {
console.error(error.status, error.code, error.requestId)
}
throw error
}
Use Node.js 22 or later and run node transcript-sdk.mjs. The shared abort signal bounds requests and waits. Download the example or read the Node.js guide for direct HTTP calls.
Python quickstart
GET /v1/transcript returns HTTP 200 with content as a segment array by default, or a string when text=true. An accepted generation returns HTTP 202 with jobId, status and requestId, without content. Both SDK transcript methods return these same response bodies. Read content first, then retain jobId if generation was accepted.
python -m pip install capslaneimport json
import os
import sys
from capslane import CapslaneClient, CapslaneError
client = CapslaneClient(api_key=os.environ["CAPSLANE_API_KEY"], timeout=45)
try:
result = client.transcript("dQw4w9WgXcQ", mode="auto")
if "content" not in result:
print("Accepted job:", result["jobId"], file=sys.stderr)
result = client.wait_for_transcript(result, timeout=20 * 60)
print(json.dumps(result, indent=2))
except CapslaneError as error:
print(error.code, error.status, error.request_id, file=sys.stderr)
raise
Use Python 3.10 or later and run python transcript_sdk.py. This example allows 45 seconds per call and a twenty-minute polling window. Download the example; the Python HTTP alternative provides a shared deadline.
Stopping a client request or reaching a client timeout does not cancel an accepted server job. Resume with its job ID. JavaScript SDK 0.1.3 bounds both waits and in-flight reads within the polling deadline. Python checks its polling deadline between iterations. Each HTTP request also has its own timeout.
Use with a coding assistant
Building a retrieval or question-answering application? The LangChain integration loads timestamped Documents through the Python SDK.
Install the Capslane agent skill to give Codex, Claude Code or Cursor a workflow for retrieving transcripts, following jobs and citing timestamps. It uses an existing MCP connection or the bundled HTTP helper.
Use Context7 to look up the Capslane SDK before writing an integration. Give your assistant the library ID for your language so it can retrieve the right documentation.
The maintained JavaScript method reference and Python method reference document exact signatures and timeout defaults. Their HTTP response examples cover completed, failed and expired jobs.
| Language | Context7 library ID |
|---|---|
| JavaScript and TypeScript | /webba-creative-technologies/capslane-js |
| Python | /webba-creative-technologies/capslane-python |
Open the JavaScript reference or Python reference in Context7. Once Context7 is connected to your assistant, try this request:
Use the Capslane Python SDK to retrieve a YouTube transcript. Fetch its documentation from Context7 with library ID /webba-creative-technologies/capslane-python. Handle immediate results and accepted jobs, and keep the API key in an environment variable.Context7 supplies documentation. To let an assistant call the transcript service, connect the Capslane MCP server with the setup for Claude Code, Codex or Cursor. See how Context7 indexes documentation for details about its sources.
You can also provide this page in Markdown, the Markdown API reference or OpenAPI JSON directly. The documentation index lists the public sources. Keep API keys out of your prompts.
/v1/transcript
GET /v1/transcript returns HTTP 200 with content as a segment array by default, or a string when text=true. An accepted generation returns HTTP 202 with jobId, status and requestId, without content. Both SDK transcript methods return these same response bodies. Read content first, then retain jobId if generation was accepted.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | string | Required | An 11-character video ID or HTTPS YouTube watch, Shorts or youtu.be URL. |
| lang | string | Source selection | Preferred language: 2 or 3 letters, optionally followed by a 2-letter region, such as en or fr-FR. |
| mode | enum | auto | native, auto or generate. Applied after cache lookup. |
| text | boolean | false | Return one string in an immediate response. Job results remain segmented. |
| chunkSize | integer | Original segments | Character budget from 50 to 10,000 for grouping whole segments in an immediate response. |
lang is a preference, not a translation request. Inspect lang for the selected language and availableLangs for the tracks reported by the source.
Immediate responses support text=true for one string, or timestamped segments by default. Segment offsets and durations are milliseconds. chunkSize groups whole segments using a character budget from 50 to 10,000; an individual source segment can exceed that budget. text=true takes precedence over chunkSize.
200 response
{
"content": [
{
"text": "Example segment.",
"offset": 8150,
"duration": 1200,
"lang": "en"
}
],
"lang": "en",
"availableLangs": [
"en"
],
"source": "native",
"cached": false,
"requestId": "req_example"
}202 response
A generation job was accepted. Retain its jobId and follow the status endpoint below.
{
"jobId": "job_550e8400-e29b-41d4-a716-446655440000",
"status": "queued",
"requestId": "req_example"
}/v1/transcript/:jobId
Poll GET /v1/transcript/{jobId} with a key from the same workspace. HTTP 200 can contain a pending or failed job. Wait for content, stop on failed, cancelled or completed without content, and set a deadline. Progress is optional. Keep the accepted job ID to resume waiting without submitting another transcript request.
Set CAPSLANE_JOB_ID to the returned ID. This command checks once.
curl --fail-with-body "https://capslane.com/v1/transcript/$CAPSLANE_JOB_ID" \
--header "x-api-key: $CAPSLANE_API_KEY"For Node.js, use the saved-job example. It preserves the ID across interruptions and stops when a completed job has no stored content.
The public job endpoint returns canonical timestamped segments when complete. It does not reapply text or chunkSize from the initial request. Join the returned segment text locally if you need one string. A completed response also has jobId, so use content to recognize a ready transcript.
{
"content": [
{
"text": "Example segment.",
"offset": 8150,
"duration": 1200,
"lang": "en"
}
],
"lang": "en",
"availableLangs": [
"en"
],
"source": "generated",
"cached": true,
"requestId": "req_example",
"jobId": "job_550e8400-e29b-41d4-a716-446655440000",
"status": "completed"
}Errors
API request errors include error, message and requestId. A failed job may contain an error code without a message. Network and proxy failures may not return JSON.
| HTTP | Code | Meaning and action |
|---|---|---|
| 400 | invalid_request | A header, query or job ID fails validation. A missing or too-short x-api-key header also returns this code. |
| 400 | invalid_video_id | The video URL or identifier is invalid. Correct the input. |
| 401 | invalid_api_key | The supplied key is invalid, expired or revoked. |
| 404 | transcript_unavailable | No usable native track exists. Consider auto only if generation is permitted. |
| 404 | job_not_found | The job is absent or belongs to another workspace. |
| 429 | request_failed | Short rate limit. Respect Retry-After before trying again. |
| 429 | monthly_limit_exceeded | The monthly transcript request allowance is exhausted. |
| 429 | generation_minutes_limit_exceeded | The Free generated-minute allowance cannot cover this generation. |
| 429 | generation_concurrency_exceeded | The Free workspace already has an active generation. |
| 502 | upstream_unavailable | The upstream service could not complete the request. Use bounded backoff. |
| 503 | capacity_exceeded | The generation queue is full. Try again later within a retry budget. |
For a short rate limit, HTTP 429 uses request_failed with a Retry-After header. Monthly request and generated-minute limits require an allowance change or monthly reset. A concurrency limit can clear when the active generation finishes. Retry temporary upstream or capacity failures with bounded backoff; when polling, retry the same job ID.
See the error handling guide for retry decisions.
Monitor the workspace
The Overview shows monthly usage, success rate, cache rate, latency and recent API activity.

Test a request
The Playground runs through the server, so you can inspect the real response without exposing an API key in the browser.

Limits and billing
A transcript request reserves one monthly unit before extraction and cache lookup. A cache hit uses one unit, and a later extraction failure can still consume that unit. Repeating the initial request can consume another unit. Job status and account checks do not reserve transcript units. A request rejected for an exhausted monthly allowance adds no unit.
Transcript submission and job status endpoints each allow 120 requests per minute per client IP. Account checks allow 60. Free workspaces include 50 transcript requests and 15 generated minutes per month, with one active generation at a time. Check the pricing page for current plan allowances.
NextView plans