Docs / API reference

Reference

API reference

Chasca API v1: JSON over HTTPS with bearer API keys. Work that takes time (generation and edits) returns 202 with a queued record you can poll or stream.

Base URL https://app.trychasca.com/api

Authentication

Every /v1 endpoint except voices and languages needs an API key, sent as Authorization: Bearer chk_live_…. Create keys in the app under Settings → API keys. API access starts on Plus, and API generations use plan minutes at 2x.

Conventions

  • Bodies are JSON (Content-Type: application/json). Timestamps are ISO 8601 in UTC.
  • Queued work answers 202; creating a copy answers 201; deleting answers 204.
  • Explainer status moves queued → generating → rendering → ready, or ends in failed with a readable error. stage and progress (0–1) describe the step in between.
  • Media URLs are signed and expire after about a week. Fetch the explainer again for fresh links.

Endpoints

POST /v1/explainer

Queue a new explainer. Returns 202 with the queued record.

Create explainer body fields
Field Type Description
input string What to explain: a topic, a question, or the full text of a document (up to 200,000 characters). Alias: prompt. One of input, source or sceneGraph is required.
length string | number "M:SS" (like "1:30") or seconds, a multiple of 30 up to 360. Omit for automatic. Alias: lengthSeconds.
aspect string 16:9 (default), 9:16 or 1:1.
voice string A narrator id such as chasca:sulafat (default) or chasca:kore. Every narrator speaks every language. See Voices.
language string Narration language code, e.g. en, es-419, ja. See Languages.
source string Source text to explain from (up to 400,000 characters). The script stays faithful to it.
preset string Drawing style: marker (default), chalkboard, pencil, blueprint, crayon or clean.
fillMode string How color washes in after each outline: A fade, B wipe, C region wash (default), D scribble.
pace string relaxed, balanced (default) or brisk.
music boolean Add a quiet background bed under the narration. Default false.
sceneGraph object Skip the writing step and render your own scene graph (scenes with narration, heading and elements).
Request
curl -X POST https://app.trychasca.com/api/v1/explainer \
  -H "Authorization: Bearer $CHASCA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": "How does compound interest snowball?",
    "length": "1:30",
    "aspect": "9:16",
    "voice": "chasca:sulafat",
    "language": "en",
    "preset": "chalkboard",
    "pace": "balanced"
  }'
202 Accepted
{
  "id": "0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60",
  "status": "queued",
  "stage": null,
  "progress": 0,
  "title": null,
  "prompt": "How does compound interest snowball?",
  "lengthSeconds": 90,
  "voice": "chasca:sulafat",
  "language": "en",
  "aspect": "9:16",
  "preset": "chalkboard",
  "fillMode": "C",
  "pace": "balanced",
  "music": false,
  "videoUrl": null,
  "posterUrl": null,
  "subtitlesUrl": null,
  "durationSeconds": null,
  "watermarked": false,
  "sceneGraph": null,
  "headVersionId": null,
  "source": null,
  "error": null,
  "createdVia": "api",
  "createdAt": "2026-09-23T09:14:02.118Z",
  "updatedAt": "2026-09-23T09:14:02.118Z"
}

GET /v1/explainer/:id

One explainer with everything: status, videoUrl, posterUrl, subtitlesUrl, the editable sceneGraph, per-scene timings in scenes, and every entry in versions.

Request
curl https://app.trychasca.com/api/v1/explainer/0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60 \
  -H "Authorization: Bearer $CHASCA_API_KEY"
200 OK (abridged)
{
  "id": "0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60",
  "status": "ready",
  "stage": null,
  "progress": 1,
  "title": "How compound interest snowballs",
  "lengthSeconds": 90,
  "durationSeconds": 88.4,
  "videoUrl": "https://app.trychasca.com/media/explainers/0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60/v1/video.mp4?exp=1790000000&sig=…",
  "posterUrl": "https://app.trychasca.com/media/explainers/0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60/v1/poster.jpg?exp=1790000000&sig=…",
  "subtitlesUrl": "https://app.trychasca.com/media/explainers/0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60/v1/subtitles.vtt?exp=1790000000&sig=…",
  "sceneGraph": {
    "title": "How compound interest snowballs",
    "language": "en",
    "scenes": [
      {
        "id": "s1",
        "heading": "THE MONEY THAT GROWS ITSELF",
        "narration": "Picture planting a tiny seed…",
        "elements": [
          { "id": "seed", "type": "icon", "icon": "seedling", "label": "SEED", "revealWord": "seed" }
        ]
      }
    ]
  },
  "scenes": [
    { "id": "s1", "index": 0, "heading": "THE MONEY THAT GROWS ITSELF", "start": 0, "duration": 19.3 }
  ],
  "versions": [
    {
      "id": "5f1d…",
      "number": 1,
      "status": "ready",
      "note": null,
      "changedScenes": [],
      "videoUrl": "https://app.trychasca.com/media/…",
      "durationSeconds": 88.4,
      "minutesCharged": 2.947,
      "createdAt": "2026-09-23T09:14:02.301Z",
      "readyAt": "2026-09-23T09:14:49.870Z",
      "error": null
    }
  ],
  "shareUrl": null,
  "error": null
}

GET /v1/explainer?limit=50

Your explainers, newest first. limit is 1–100 and defaults to 50. Summaries omit the scene graph and versions.

Request
curl "https://app.trychasca.com/api/v1/explainer?limit=20" \
  -H "Authorization: Bearer $CHASCA_API_KEY"
200 OK (abridged)
{
  "explainers": [
    { "id": "0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60", "status": "ready", "title": "How compound interest snowballs", "durationSeconds": 88.4, "createdAt": "2026-09-23T09:14:02.118Z" }
  ]
}

POST /v1/explainer/:id/edit

Edit with a plain-language { "instruction": "..." }, or send a full edited { "sceneGraph": { ... } }. Returns 202 with the queued version. Only the scenes that changed are re-rendered, and the first re-render of every video is free. Wait for the version’s status to reach ready; the explainer’s videoUrl then points at it.

Request
curl -X POST https://app.trychasca.com/api/v1/explainer/0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60/edit \
  -H "Authorization: Bearer $CHASCA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "instruction": "In scene 3, call the two savers Ana and Ben" }'
202 Accepted
{
  "id": "8a2c7e10-…",
  "number": 2,
  "status": "queued",
  "note": "In scene 3, call the two savers Ana and Ben",
  "changedScenes": [],
  "videoUrl": null,
  "posterUrl": null,
  "durationSeconds": null,
  "minutesCharged": 0,
  "createdAt": "2026-09-23T09:20:11.004Z",
  "readyAt": null,
  "error": null
}

POST /v1/explainer/:id/revert

Make an earlier, finished version current again. Body: { "versionId": "..." }. Returns the explainer.

Request
curl -X POST https://app.trychasca.com/api/v1/explainer/0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60/revert \
  -H "Authorization: Bearer $CHASCA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "versionId": "5f1d…" }'

POST /v1/explainer/:id/duplicate

Copy an explainer, scene graph and all, so you can take it somewhere new. Returns 201 with the copy.

Request
curl -X POST https://app.trychasca.com/api/v1/explainer/0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60/duplicate \
  -H "Authorization: Bearer $CHASCA_API_KEY"

DELETE /v1/explainer/:id

Delete an explainer. Returns 204 with no body.

Request
curl -X DELETE https://app.trychasca.com/api/v1/explainer/0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60 \
  -H "Authorization: Bearer $CHASCA_API_KEY"

GET /v1/explainer/events

A Server-Sent Events stream with an event: status frame for every status change of any of your explainers. Keep one connection open instead of polling.

Request
curl -N https://app.trychasca.com/api/v1/explainer/events \
  -H "Authorization: Bearer $CHASCA_API_KEY"
Stream
event: status
data: {"type":"status","explainerId":"0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60","status":"generating","stage":"storyboard","progress":0.3}

event: status
data: {"type":"status","explainerId":"0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60","status":"rendering","stage":"drawing","progress":0.72}

event: status
data: {"type":"status","explainerId":"0b9c6f2e-5d1a-4c3e-9a47-2f8e1c7d4b60","status":"ready","stage":null,"progress":1}

GET /v1/voices No key needed

The voice catalog. Every narrator speaks every language, so the list is the same for all of them. Each voice has a tone, a group and a preview_url. More on the Voices page.

Request
curl "https://app.trychasca.com/api/v1/voices"
200 OK (abridged)
{
  "default": "chasca:sulafat",
  "voices": [
    {
      "id": "chasca:sulafat",
      "name": "Sulafat",
      "description": "Warm and natural, like a favorite teacher. The all-purpose default.",
      "tone": "Warm",
      "group": "warm",
      "language": "en",
      "gender": "female",
      "isDefault": true,
      "multilingual": true,
      "kind": "narrator",
      "preview_url": "https://app.trychasca.com/api/v1/voices/chasca%3Anova/preview.mp3"
    },
    {
      "id": "edge:es-MX-DaliaNeural",
      "name": "Dalia",
      "description": "Friendly, Positive. Native es-MX speaker.",
      "language": "es-MX",
      "gender": "female",
      "isDefault": false,
      "multilingual": false,
      "kind": "native",
      "preview_url": "https://app.trychasca.com/api/v1/voices/edge%3Aes-MX-DaliaNeural/preview.mp3"
    }
  ]
}

GET /v1/languages No key needed

Every narration language. beta languages are newer to the voice model, so narration quality can vary.

Request
curl https://app.trychasca.com/api/v1/languages
200 OK (abridged)
{
  "languages": [
    { "code": "en", "label": "English", "native": "English", "beta": false },
    { "code": "es-419", "label": "Spanish (Latin America)", "native": "Español (Latinoamérica)", "beta": false },
    { "code": "pa", "label": "Punjabi", "native": "ਪੰਜਾਬੀ", "beta": true }
  ]
}

GET /v1/account/state

Who the key belongs to, and how many videos they have made.

Request
curl https://app.trychasca.com/api/v1/account/state \
  -H "Authorization: Bearer $CHASCA_API_KEY"
200 OK
{ "videosGenerated": 14, "image": null, "isAdmin": false, "name": "Ada Lovelace" }

GET /v1/billing/status

The plan behind the key and where you are in the period: minutes used and remaining, overage, and entitlements. Free accounts get videoAllowance, videosUsed and videosRemaining instead.

Request
curl https://app.trychasca.com/api/v1/billing/status \
  -H "Authorization: Bearer $CHASCA_API_KEY"
200 OK
{
  "plan": "pro",
  "planLabel": "Pro",
  "billingInterval": "annual",
  "status": "active",
  "trialStatus": null,
  "quotaMinutes": 300,
  "bonusMinutes": 0,
  "minutesUsedThisPeriod": 41.5,
  "minutesRemaining": 258.5,
  "periodStart": "2026-09-01T00:00:00.000Z",
  "periodEnd": "2026-10-01T00:00:00.000Z",
  "cancelAtPeriodEnd": false,
  "overageEnabled": false,
  "overagePerMinute": 0.33,
  "pricePerMonth": 29.99,
  "maxLengthSeconds": 360,
  "entitlements": { "api": true, "branding": true, "voiceClone": true, "priority": false, "watermark": false, "maxLength": 360, "videoAllowance": null }
}

GET /config No key needed

Public configuration for clients: where the site, app and API live.

Request
curl https://app.trychasca.com/api/config
200 OK
{
  "authEnforced": true,
  "googleEnabled": false,
  "requireEmailVerification": false,
  "siteUrl": "https://trychasca.com",
  "appUrl": "https://app.trychasca.com",
  "apiUrl": "https://app.trychasca.com/api",
  "devMail": false
}

GET /health No key needed

Liveness check for load balancers and uptime monitors.

Request
curl https://app.trychasca.com/api/health
200 OK
{ "status": "ok", "service": "chasca-api", "time": "2026-09-23T09:14:00.000Z", "db": "postgres", "tts": "edge", "llmConfigured": true }

Errors

Every error has the same shape, with a stable machine-readable code and a message you can show to people:

402 Payment Required
{
  "error": true,
  "code": "api_requires_plan",
  "message": "API and MCP access start on the Plus plan.",
  "status": 402
}
Common error codes
Status Code Meaning
400 bad_request, empty_input, bad_voice The request is malformed or missing what to explain.
401 unauthenticated No key, or the key is wrong or revoked.
402 api_requires_plan API and MCP access start on Plus.
402 minutes_exhausted No minutes left this period and overage is off.
404 not_found No explainer with that id on your account.
409 still_rendering, no_changes The video is mid-render, or an edit changes nothing.
429 rate_limited Too many requests. Wait for the Retry-After header, then retry.
500 internal Our fault. Details are logged on our side, never leaked.

Rate limits

  • Generation and edits (POST /v1/explainer, …/edit): 20 requests per minute.
  • Everything else: 200 requests per minute.
  • Over the limit you get 429 rate_limited with a Retry-After header in seconds. The SDK retries read requests once after that delay and exposes it on errors as retryAfter.