Browse the API docs
Endpoint reference

Projects

The Crayo Projects API composes scenes, narration, music, text and captions into a video spec, then renders it with POST /v1/projects/{id}/export. Creating a project is free and the export costs 1 export credit per second of finished video.

Create project

POST/v1/projects

Builds a project from a scene list plus optional narration, music, and captions. The schema is strict and every field is validated up front, so a project either comes back ready to export or comes back with the exact field that was wrong. Creating a project costs nothing on its own: you pay when you export it.

  • Scenes play in order and may not overlap. The end_ms of the last scene is the length of the finished video.
  • Set a scene's volume to 0 when it plays under narration, otherwise both audio tracks are heard.
  • Captions are timed from the narration track, so subtitles without narration are rejected.
  • One create or export runs per account at a time. Serialize your calls rather than firing them in parallel.
Credits:
Free, unless captions are enabled. Transcribing the narration costs 1 voiceover credit per second.
Rate limit:
Heavy tier, 2/s and 10/min

Parameters

NameInTypeRequiredDescription
titlebodystringYesName of the project. Max 200 characters.
scenesbodyobject[]YesThe visual timeline, in order. Each scene shows one asset for a window of the finished video. 1 to 50 scenes. Scenes may not overlap, and the last end_ms sets the video length.
scenes.asset_idbodystringYesA video or image asset you own.
scenes.start_msbodyintegerYesWhen the scene starts on the timeline. 0 or greater, and never before the previous scene ends.
scenes.end_msbodyintegerYesWhen the scene ends on the timeline. Greater than start_ms, and at most 10 minutes.
scenes.trim_start_msbodyintegerNoHow far into the source video the scene starts playing. Video scenes only. The trimmed window must fit inside the source video.default 0
scenes.volumebodyintegerNoVolume of the scene's own audio. Set 0 to mute the source video under narration. 0 to 100.default 100
scenes.fitbodystringNoHow the asset fills the vertical canvas. cover scales and center-crops it edge to edge; contain shows the whole frame with black bars.default covercovercontain
narrationbodyobjectNoVoice track that plays across the whole video.
narration.asset_idbodystringYesAn audio asset you own.
narration.volumebodyintegerNoNarration volume. 0 to 100.default 100
musicbodyobjectNoBackground music bed. Pass exactly one of track_id or asset_id.
music.track_idbodystringNoA track_id from GET /v1/music.
music.asset_idbodystringNoAn audio asset you own, to use your own music.
music.volumebodyintegerNoMusic volume. 0 to 100.default 50
textsbodyobject[]NoText overlays such as hooks and calls to action. They use the same title card as the editor, so they stay editable there. Up to 20 entries. Texts may overlap each other and any scene.
texts.textbodystringYesThe words to show. Max 200 characters.
texts.start_msbodyintegerYesWhen the text appears. 0 or greater.
texts.end_msbodyintegerYesWhen the text disappears. Greater than start_ms, and no later than the end of the last scene.
texts.positionbodystringNoWhere the text sits on the canvas.default toptopcenterbottom
texts.font_sizebodyintegerNoFont size in canvas pixels on the 1080 by 1920 frame. 24 to 200.default 75
texts.colorbodystringNoText color as a hex value.default #000000
texts.background_colorbodystringNoColor of the rounded card behind the text, as a hex value or transparent.default #ffffff
subtitlesbodyobjectNoBurned-in captions, timed from the narration track. Requires narration.
subtitles.style_idbodystringYesA style_id from GET /v1/subtitle-styles.
subtitles.enabledbodybooleanNoSet false to keep the style on the project without rendering captions.default true
subtitles.positionbodystringNoWhere caption lines sit on the canvas.default centertopcenterbottom

Request

curl
curl -X POST "https://api.crayo.ai/v1/projects" \
  -H "Authorization: Bearer $CRAYO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Morning habits short",
  "scenes": [
    {
      "asset_id": "cm7x1k2p40001abcd",
      "start_ms": 0,
      "end_ms": 4000,
      "volume": 0
    },
    {
      "asset_id": "cm7x1k2p40002abcd",
      "start_ms": 4000,
      "end_ms": 11500,
      "trim_start_ms": 2000,
      "volume": 0,
      "fit": "cover"
    }
  ],
  "narration": {
    "asset_id": "cm7x1j5bb0002abcd",
    "volume": 100
  },
  "music": {
    "track_id": "clq0ax9as00d2udfzijarenj2",
    "volume": 35
  },
  "texts": [
    {
      "text": "3 habits that changed my mornings",
      "start_ms": 0,
      "end_ms": 3000,
      "position": "top"
    }
  ],
  "subtitles": {
    "style_id": "9",
    "enabled": true,
    "position": "bottom"
  }
}'

Response

200 application/json
{
  "success": true,
  "project": {
    "id": "cm7x1k9r70004abcd",
    "title": "Morning habits short",
    "duration_ms": 11500,
    "scene_count": 2,
    "created_at": "2026-08-21T14:05:44.000Z",
    "edit_url": "https://crayo.ai/edit/cm7x1k9r70004abcd"
  }
}

Errors

  • 400UNKNOWN_FIELDThe body carries a field the schema does not define.
  • 400VALIDATION_ERRORA scene overlaps, runs past the source, or breaks a range; a text runs past the last scene or uses a bad color.
  • 400INVALID_SUBTITLE_STYLEsubtitles.style_id is not in the style catalog.
  • 401MISSING_API_KEYNo Authorization header was sent, or it does not carry a crayo_sk_ key.
  • 401UNAUTHORIZEDThe key sent is malformed, expired, or revoked.
  • 402INSUFFICIENT_CREDITSCaptions are on and the transcript cannot be paid for.
  • 403PROJECT_LIMIT_REACHEDYou already have 200 projects.
  • 404NOT_FOUNDA referenced asset or music track does not belong to your account.
  • 429CONCURRENT_REQUESTAnother create or export for this account is still in flight.
  • 429RATE_LIMITEDOver the rate limit. Wait Retry-After seconds.
  • 500SERVER_ERRORSomething failed on our side. Retry with backoff.

List projects

GET/v1/projects

Lists every project on the account, newest first, including ones made in the Crayo editor and clips produced by AutoClip. The source field tells you which is which: api for projects this API created, app for everything else.

  • Because app projects are listed too, anything made in the editor or by AutoClip can be exported through this API.
Credits:
Free
Rate limit:
Polling tier, 2/s and 120/min

Parameters

NameInTypeRequiredDescription
limitqueryintegerNoProjects per page. 1 to 100.default 25
offsetqueryintegerNoNumber of projects to skip.default 0

Request

curl
curl "https://api.crayo.ai/v1/projects" \
  -H "Authorization: Bearer $CRAYO_API_KEY"

Response

200 application/json
{
  "success": true,
  "total": 42,
  "hasMore": true,
  "projects": [
    {
      "id": "cm7x1k9r70004abcd",
      "title": "Morning habits short",
      "thumbnail_url": "https://cdn-crayo.com/user-uploads/cm7x0v8qa0000abcd/9f1.../thumb.jpg",
      "duration_ms": 11500,
      "created_at": "2026-08-21T14:05:44.000Z",
      "updated_at": "2026-08-21T14:05:44.000Z",
      "source": "api"
    }
  ]
}

Errors

  • 401MISSING_API_KEYNo Authorization header was sent, or it does not carry a crayo_sk_ key.
  • 401UNAUTHORIZEDThe key sent is malformed, expired, or revoked.
  • 429RATE_LIMITEDOver the rate limit. Wait Retry-After seconds.
  • 500SERVER_ERRORSomething failed on our side. Retry with backoff.

Get project

GET/v1/projects/{id}

Returns one project. Projects this API created also return the spec they were built from, which makes it easy to read the current state, change one field, and send it back with PATCH.

Credits:
Free
Rate limit:
Polling tier, 2/s and 120/min

Parameters

NameInTypeRequiredDescription
idpathstringYesThe project id.

Request

curl
curl "https://api.crayo.ai/v1/projects/cm7x1k9r70004abcd" \
  -H "Authorization: Bearer $CRAYO_API_KEY"

Response

200 application/json
{
  "success": true,
  "project": {
    "id": "cm7x1k9r70004abcd",
    "title": "Morning habits short",
    "thumbnail_url": "https://cdn-crayo.com/user-uploads/cm7x0v8qa0000abcd/9f1.../thumb.jpg",
    "duration_ms": 11500,
    "created_at": "2026-08-21T14:05:44.000Z",
    "updated_at": "2026-08-21T14:05:44.000Z",
    "source": "api",
    "spec": {
      "title": "Morning habits short",
      "scenes": [
        {
          "asset_id": "cm7x1k2p40001abcd",
          "start_ms": 0,
          "end_ms": 4000,
          "volume": 0
        },
        {
          "asset_id": "cm7x1k2p40002abcd",
          "start_ms": 4000,
          "end_ms": 11500,
          "trim_start_ms": 2000,
          "volume": 0,
          "fit": "cover"
        }
      ],
      "narration": {
        "asset_id": "cm7x1j5bb0002abcd",
        "volume": 100
      },
      "music": {
        "track_id": "clq0ax9as00d2udfzijarenj2",
        "volume": 35
      },
      "texts": [
        {
          "text": "3 habits that changed my mornings",
          "start_ms": 0,
          "end_ms": 3000,
          "position": "top"
        }
      ],
      "subtitles": {
        "style_id": "9",
        "enabled": true,
        "position": "bottom"
      }
    }
  }
}

Errors

  • 401MISSING_API_KEYNo Authorization header was sent, or it does not carry a crayo_sk_ key.
  • 401UNAUTHORIZEDThe key sent is malformed, expired, or revoked.
  • 404NOT_FOUNDNo project with that id belongs to your account.
  • 429RATE_LIMITEDOver the rate limit. Wait Retry-After seconds.
  • 500SERVER_ERRORSomething failed on our side. Retry with backoff.

Update project

PATCH/v1/projects/{id}

Changes a project in place. Send only the fields you want to change: they are merged over the stored spec and the whole result is validated again. Sending a field as null removes it, which is how you drop music or captions.

  • The title can be changed on any project. Scenes, narration, music, texts, and subtitles can only be changed on projects this API created.
  • Editor projects and AutoClip clips can still be renamed and exported through the API.
Credits:
Free, unless the change re-times captions against narration that has not been transcribed yet.
Rate limit:
Default tier, 2/s and 50/min

Parameters

NameInTypeRequiredDescription
idpathstringYesThe project id.
titlebodystringNoName of the project. Max 200 characters.
scenesbodyobject[]YesThe visual timeline, in order. Each scene shows one asset for a window of the finished video. 1 to 50 scenes. Scenes may not overlap, and the last end_ms sets the video length.
scenes.asset_idbodystringYesA video or image asset you own.
scenes.start_msbodyintegerYesWhen the scene starts on the timeline. 0 or greater, and never before the previous scene ends.
scenes.end_msbodyintegerYesWhen the scene ends on the timeline. Greater than start_ms, and at most 10 minutes.
scenes.trim_start_msbodyintegerNoHow far into the source video the scene starts playing. Video scenes only. The trimmed window must fit inside the source video.default 0
scenes.volumebodyintegerNoVolume of the scene's own audio. Set 0 to mute the source video under narration. 0 to 100.default 100
scenes.fitbodystringNoHow the asset fills the vertical canvas. cover scales and center-crops it edge to edge; contain shows the whole frame with black bars.default covercovercontain
narrationbodyobjectNoVoice track that plays across the whole video.
narration.asset_idbodystringYesAn audio asset you own.
narration.volumebodyintegerNoNarration volume. 0 to 100.default 100
musicbodyobjectNoBackground music bed. Pass exactly one of track_id or asset_id.
music.track_idbodystringNoA track_id from GET /v1/music.
music.asset_idbodystringNoAn audio asset you own, to use your own music.
music.volumebodyintegerNoMusic volume. 0 to 100.default 50
textsbodyobject[]NoText overlays such as hooks and calls to action. They use the same title card as the editor, so they stay editable there. Up to 20 entries. Texts may overlap each other and any scene.
texts.textbodystringYesThe words to show. Max 200 characters.
texts.start_msbodyintegerYesWhen the text appears. 0 or greater.
texts.end_msbodyintegerYesWhen the text disappears. Greater than start_ms, and no later than the end of the last scene.
texts.positionbodystringNoWhere the text sits on the canvas.default toptopcenterbottom
texts.font_sizebodyintegerNoFont size in canvas pixels on the 1080 by 1920 frame. 24 to 200.default 75
texts.colorbodystringNoText color as a hex value.default #000000
texts.background_colorbodystringNoColor of the rounded card behind the text, as a hex value or transparent.default #ffffff
subtitlesbodyobjectNoBurned-in captions, timed from the narration track. Requires narration.
subtitles.style_idbodystringYesA style_id from GET /v1/subtitle-styles.
subtitles.enabledbodybooleanNoSet false to keep the style on the project without rendering captions.default true
subtitles.positionbodystringNoWhere caption lines sit on the canvas.default centertopcenterbottom

Request

curl
curl -X PATCH "https://api.crayo.ai/v1/projects/cm7x1k9r70004abcd" \
  -H "Authorization: Bearer $CRAYO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Morning habits short v2",
  "music": {
    "track_id": "clq0ax9as00d2udfzijarenj2",
    "volume": 20
  }
}'

Response

200 application/json
{
  "success": true,
  "project": {
    "id": "cm7x1k9r70004abcd",
    "title": "Morning habits short v2",
    "duration_ms": 11500,
    "scene_count": 2,
    "created_at": "2026-08-21T14:05:44.000Z",
    "edit_url": "https://crayo.ai/edit/cm7x1k9r70004abcd"
  }
}

Errors

  • 400UNKNOWN_FIELDThe body carries a field the schema does not define.
  • 400VALIDATION_ERRORA scene overlaps, runs past the source, or breaks a range; a text runs past the last scene or uses a bad color.
  • 400INVALID_SUBTITLE_STYLEsubtitles.style_id is not in the style catalog.
  • 401MISSING_API_KEYNo Authorization header was sent, or it does not carry a crayo_sk_ key.
  • 401UNAUTHORIZEDThe key sent is malformed, expired, or revoked.
  • 404NOT_FOUNDA referenced asset or music track does not belong to your account.
  • 404NOT_FOUNDNo project with that id belongs to your account.
  • 409NOT_API_PROJECTThe project was made in the editor, so its content cannot be rewritten here.
  • 429RATE_LIMITEDOver the rate limit. Wait Retry-After seconds.
  • 500SERVER_ERRORSomething failed on our side. Retry with backoff.

Delete project

DELETE/v1/projects/{id}

Permanently deletes a project. Videos already exported from it are separate assets and stay in your library.

Credits:
Free
Rate limit:
Default tier, 2/s and 50/min

Parameters

NameInTypeRequiredDescription
idpathstringYesThe project id.

Request

curl
curl -X DELETE "https://api.crayo.ai/v1/projects/cm7x1k9r70004abcd" \
  -H "Authorization: Bearer $CRAYO_API_KEY"

Response

200 application/json
{
  "success": true
}

Errors

  • 401MISSING_API_KEYNo Authorization header was sent, or it does not carry a crayo_sk_ key.
  • 401UNAUTHORIZEDThe key sent is malformed, expired, or revoked.
  • 404NOT_FOUNDNo project with that id belongs to your account.
  • 429RATE_LIMITEDOver the rate limit. Wait Retry-After seconds.
  • 500SERVER_ERRORSomething failed on our side. Retry with backoff.

Export project

POST/v1/projects/{id}/export

Queues a render of the project and returns immediately with an export id. Credits are charged at this moment, based on the length of the finished video. Register an export.completed webhook to be told when the file is ready.

  • Any project on the account can be exported, including editor projects and AutoClip clips.
  • Credits are charged when the render is queued and stay spent if the render fails.
Credits:
1 export credit per second of finished video
Rate limit:
Heavy tier, 2/s and 10/min

Parameters

NameInTypeRequiredDescription
idpathstringYesThe project id.

Request

curl
curl -X POST "https://api.crayo.ai/v1/projects/cm7x1k9r70004abcd/export" \
  -H "Authorization: Bearer $CRAYO_API_KEY"

Response

200 application/json
{
  "success": true,
  "export": {
    "id": "cm7x1kb2p0007abcd",
    "project_id": "cm7x1k9r70004abcd",
    "status": "queued",
    "credits_charged": 12
  }
}

Errors

  • 400EMPTY_PROJECTThe project has no content to render.
  • 401MISSING_API_KEYNo Authorization header was sent, or it does not carry a crayo_sk_ key.
  • 401UNAUTHORIZEDThe key sent is malformed, expired, or revoked.
  • 402INSUFFICIENT_CREDITSYour export balance is below the video length.
  • 404NOT_FOUNDNo project with that id belongs to your account.
  • 429EXPORT_LIMIT_REACHEDYou already have 2 exports running.
  • 429CONCURRENT_REQUESTAnother create or export for this account is still in flight.
  • 429RATE_LIMITEDOver the rate limit. Wait Retry-After seconds.
  • 500SERVER_ERRORSomething failed on our side. Retry with backoff.
New to the Crayo API? See what it is forUse cases, pricing and the console walkthrough live on the API feature page.

FAQs

Frequent questions

Creating and updating a project is free unless captions are enabled, in which case transcribing the narration costs 1 voiceover credit per second. Exporting costs 1 export credit per second of finished video, charged when the render is queued.

Yes. GET /v1/projects lists every project on the account with a source of api or app, and any of them can be renamed and exported. Scenes, narration, music, texts and subtitles can only be changed on projects this API created.

Scenes are measured in milliseconds, play in order and may not overlap. A project holds 1 to 50 scenes, each scene ends at most 10 minutes in, and the end_ms of the last scene is the length of the finished video.

One create or export runs per account at a time, and a second one answers 429 CONCURRENT_REQUEST. An account holds up to 200 projects and 2 running exports.

Still have questions?

Contact our 24/7 support team for any concerns or inquiries.

Get in touch