Skip to content

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"}'
FieldRequiredMeaning
urlYesA YouTube video URL (watch, youtu.be, shorts, embed or live), or a bare 11-character video ID.
formatsNoThe formats to record on the result, from the list under Formats. Leave it out for all of them.
langNoThe 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.contentWhat it holds
summaryA written summary.
key_pointsKey points, each with text and a timestamp in at_ms.
chaptersChapters, each with its title in text and a timestamp in at_ms.
quotesQuotes, with the exact transcript slice in text_verbatim, a readable version in text_display, and at_ms.
seotitle, meta_description, slug and tags.
socialtweet, thread and linkedin_post.
detected_languageThe 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:

JSON
{
  "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.

FieldMeaning
oktrue when the extraction worked.
cachedWhether the result came from cache.
artifact.contentThe output, as listed under Formats.
artifact.formatsThe formats you asked for.
artifact.sourceThe video's video_id, canonical_url and duration_seconds. Its title and channel name are under volatile, which can be null.
artifact.cache_digestThe ID of this result's transcript.
transcript_urlA 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.

JSON
{
  "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_..."
statusWhat to do
pendingIt hasn't started. Poll again.
runningIt's in progress. Poll again. If an attempt dies, Clypso runs the job again and it stays running.
succeededStop. The body has the same fields as a 200 above, plus job_id and status.
failedStop. 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:

JSON
{
  "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, andYou get
the same body, first request finishedThe first response again, with the same status and body. Nothing runs.
the same body, first request still runningA 409 with request_in_progress, or a 202 with its job_id if it already has a job.
a different bodyA 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.

LimitValuePast it
Video length2 hoursok: false with video_too_long
Videos a month30 videosok: false with quota_exceeded
Daily capacityShared across the serviceok: false with spend_ceiling_reached
Requests60 a minute per keyA 429 with rate_limited

See Usage and quota and Rate limits for how these work.