Conventions
Base URL, field format, pagination, async jobs and expiring links: the rules that apply to every route.
The same rules apply to all routes. Read them once and the reference becomes predictable.
Base URL
https://api.cut.pro/api/v1HTTPS only. The version is part of the path.
Request
Send the body as JSON with Content-Type: application/json. The routes also accept application/x-www-form-urlencoded and multipart/form-data with the same fields, which helps in no-code tools.
Fields
| Rule | Example |
|---|---|
Every field is snake_case | credits_cost, output_resolution |
The resource's own id is always id | { "id": "7412908371004912" } |
| A reference to another resource carries its name | video_id, edit_id, connection_id |
| IDs are strings | store them as strings, never convert to a number |
| Date and time in ISO 8601, UTC | "2026-10-29T18:04:51.000Z" |
| Duration and position in the video in seconds | "start_time": 42.5 |
A field with no value comes as null, it does not disappear | "completed_at": null |
The only exception is metadata in POST /posts: the keys there are each network's own (privacyLevel, categoryId, privacyStatus) and go to the platform exactly as you wrote them.
State and error
status describes where a job is and comes in lowercase: queued, processing, completed, failed.
code describes what went wrong and comes in uppercase: INSUFFICIENT_CREDITS, SUBMISSION_EXPIRED. Every error has the same body, with no text message:
{ "code": "INSUFFICIENT_CREDITS", "extra": { "credits_needed": 121, "current_balance": 40 } }Use a switch on code and translate it for your interface. See Errors.
Lists and pagination
Every list responds with the resource name and a pagination block:
{
"clips": [{ "id": "7412908412887301" }],
"pagination": {
"current_page": 1,
"total_pages": 3,
"total_count": 52,
"has_next_page": true,
"has_previous_page": false,
"limit": 20
}
}Paginate with page (starts at 1) and limit (maximum 100). The default limit changes per route and is in each route's reference.
curl "https://api.cut.pro/api/v1/renders?page=2&limit=100" \
-H "X-Api-Key: $CUTPRO_API_KEY"Keep going while has_next_page is true.
Async jobs
Clipping, render, post, transcription, generation and persona do not finish in the response. Creation returns an id and you poll until the end:
- Create the job and keep the
id. - Poll the job's read route every 5 to 10 seconds.
- Stop when the
statusreaches a final state.
| Job | Read route | Final states |
|---|---|---|
| Clipping | GET /clips/{videoId}/submissions/{submissionId} | completed, failed |
| Render | GET /renders/{renderId} | completed, failed, cancelled, expired |
| Post | GET /posts/{id} | completed, partial, failed, cancelled |
| Transcription | GET /transcriptions/{transcriptionId} | ready, no_speech, failed |
| Generation | GET /generations/{id} | ready, failed |
This pace fits comfortably within the rate limit. The API does not send webhooks: the end of a job is always found by polling.
Expiring links
| What | Validity |
|---|---|
download_url, play_url and thumbnail_url of a clip | short: ask again when you are going to use it |
GET /renders/{renderId}/download link | 1 hour |
| A render's MP4 file | set by the plan, in render_expiry_hours of GET /renders/limits |
Edit (edit_id) | does not expire |
Do not store a signed link in your database. Store the id and ask for the link on the spot. If the MP4 was already deleted, POST /renders with the edit_id generates a new one.
OpenAPI specification
The complete specification is at https://api.cut.pro/api/v1/openapi.json. Use it to generate a typed client, import into Postman or Insomnia, or give context to a coding agent.