Extraction
Send a video URL and get structured content back, right away or through a job you poll.
Examples use VIDEO_ID and clp_live_... as placeholders.
The request
Send POST /v1/extract with a JSON body:
curl -X POST "https://clypso-eta.vercel.app/v1/extract" \
-H "Authorization: Bearer clp_live_..." \
-H "Content-Type: application/json" \
-d '{"url":"https://www.youtube.com/watch?v=VIDEO_ID"}'| Field | Required | Meaning |
|---|---|---|
url | Yes | A YouTube video URL (watch, youtu.be, shorts, embed or live), or a bare 11-character video ID. |
formats | No | The formats to record on the result, from the list under Formats. Leave it out for all of them. |
lang | No | The caption language to ask for, such as en or en-US. |
Malformed JSON, an unknown field or an unknown format name gets a 400 with invalid_request.
Formats
Key in artifact.content | What it holds |
|---|---|
summary | A written summary. |
key_points | Key points, each with text and a timestamp in at_ms. |
chapters | Chapters, each with its title in text and a timestamp in at_ms. |
quotes | Quotes, with the exact transcript slice in text_verbatim, a readable version in text_display, and at_ms. |
seo | title, meta_description, slug and tags. |
social | tweet, thread and linkedin_post. |
detected_language | The language code of the output. Always present, and not a format you request. |
artifact.content holds every format today, whichever ones you ask for, so read the fields you need. formats is echoed in artifact.formats. A different formats list is a separate cache entry, so it runs again, and the simplest choice is to leave it out.
Check the ok field
A 200 means the request was handled, not that the extraction worked. Check ok before you read anything else. When it's false, the body has an error and no artifact:
{
"ok": false,
"cached": false,
"error": {
"code": "invalid_url",
"message": "invalid YouTube URL: no valid 11-character video ID found in URL",
"detail": null,
"retryable": false,
"provider_attempts": [],
"asr_available": false,
"asr_minutes_remaining": null,
"estimated_credits": null
}
}That one is what the example above returns with VIDEO_ID left in. Replace it with a real video ID. An empty formats list or a malformed lang also returns invalid_url.
Branch on error.code. error.retryable tells you whether trying again could help. See Errors for every code.
Fast runs return 200
The request waits while Clypso works. A cached result, or a run that finishes within about 9 seconds, returns a 200 with the result. Set your client's timeout above that.
| Field | Meaning |
|---|---|
ok | true when the extraction worked. |
cached | Whether the result came from cache. |
artifact.content | The output, as listed under Formats. |
artifact.formats | The formats you asked for. |
artifact.source | The video's video_id, canonical_url and duration_seconds. Its title and channel name are under volatile, which can be null. |
artifact.cache_digest | The ID of this result's transcript. |
transcript_url | A signed link to the transcript. See Transcripts. |
Slower runs return 202
A run that takes longer keeps going in the background, and you get a 202 with a job to poll. The Location header holds the same job's path.
{
"job_id": "JOB_ID",
"status": "running"
}Poll GET /v1/extract/:jobId. In the example, replace the jobId placeholder with the job_id you got:
curl -X GET "https://clypso-eta.vercel.app/v1/extract/{jobId}" \
-H "Authorization: Bearer clp_live_..."status | What to do |
|---|---|
pending | It hasn't started. Poll again. |
running | It's in progress. Poll again. If an attempt dies, Clypso runs the job again and it stays running. |
succeeded | Stop. The body has the same fields as a 200 above, plus job_id and status. |
failed | Stop. The body has an error_code. |
Wait a few seconds between polls. Each poll counts toward the rate limit of 60 a minute, and none of them use your monthly limit. To be told when a job finishes instead, use Webhooks.
A job is visible only to the account that created it. An unknown job and another account's job both get a 404 with job_not_found.
When a job fails
A failed job reports its code in error_code. It has no ok or error, so read status first when you poll:
{
"job_id": "JOB_ID",
"status": "failed",
"error_code": "no_captions"
}A job whose attempts ran out reports internal_error. To try a failed job again, send a new POST /v1/extract.
Cached results
cached is true when Clypso already had this result for the same video, formats and language. A cached result returns right away and doesn't count against your monthly limit. It's still served once your monthly limit or the daily capacity has been reached.
Retry safely
Send an Idempotency-Key header on POST /v1/extract so a retry after a network error can't start a second run. A key can be up to 255 characters. It's scoped to your account and remembered for 24 hours from its first use, then treated as new.
| Same key, and | You get |
|---|---|
| the same body, first request finished | The first response again, with the same status and body. Nothing runs. |
| the same body, first request still running | A 409 with request_in_progress, or a 202 with its job_id if it already has a job. |
| a different body | A 409 with idempotency_key_reused. |
A 200 with ok: false is stored like any other response, so to try again after a failure, use a new key. A replayed 200 also carries the original transcript_url, which may have expired.
Videos without captions
Clypso works from captions and doesn't transcribe audio. A video with no captions it can fetch returns ok: false with no_captions, and error.asr_available is false. On a job, error_code holds the same code. It doesn't count against your monthly limit. error.retryable can be true here, but a video with no captions keeps returning this code, so don't retry it in a loop.
Limits
Every account has these limits during early access.
| Limit | Value | Past it |
|---|---|---|
| Video length | 2 hours | ok: false with video_too_long |
| Videos a month | 30 videos | ok: false with quota_exceeded |
| Daily capacity | Shared across the service | ok: false with spend_ceiling_reached |
| Requests | 60 a minute per key | A 429 with rate_limited |
See Usage and quota and Rate limits for how these work.