API reference
A REST API over JSON for everything ViddyFlow can do from a Twitch VOD.
Base URL and authentication
Base URL: https://api.viddyflow.com
Create a key in API Access and send it on every request. The key value is shown once, at creation.
X-API-Key: your_api_key_hereWhat the API can and cannot do
The API covers the whole pipeline for Twitch VODs. Three dashboard features are out of reach, because they need a browser session rather than an API key: uploading your own MP4 or MOV, background music, and branding intro/outro presets. Picture-in-picture shorts are also dashboard-only, since every overlay box is placed by hand on a frame of your stream and there is no sensible default to fall back on.
Facecam split framing does work over the API: its regions have real defaults, so you get a working facecam-over-gameplay short without sending a single coordinate. See Vertical shorts.
Quick start
The smallest job that does something useful: a 5 minute reel plus 3 shorts.
curl -X POST "https://api.viddyflow.com/jobs" \
-H "X-API-Key: your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"vod_url": "https://twitch.tv/videos/123456789",
"streamer_name": "SampleCreator",
"final_video_length_minutes": 5,
"num_shorts": 3,
"webhook_url": "https://hooks.example.com/viddyflow"
}'Prefer a ready-made collection? Download the Postman collection and set the apiKey variable.
Endpoints
| Endpoint | Purpose |
|---|---|
POST /jobs | Create a job. Limited to 10 requests per minute. |
GET /jobs | List your jobs, newest first. Supports page, page_size, status, search. |
GET /jobs/{id} | Status of one job. |
GET /jobs/{id}/results | Per-clip download links once the job is completed. |
POST /jobs/{id}/cancel | Cancel a pending or running job and refund on a tiered policy. |
POST /jobs/{id}/feedback | Rate a completed job. Returns 204. |
GET /jobs/{id}/download-all | Stream every artifact as one ZIP, built on demand. |
GET /videos/metadata | Probe a Twitch VOD for title, duration and availability before spending credits. |
Create a job
POST /jobs. Credits are reserved when the job is accepted and refunded automatically if it fails or is cancelled early.
Source and identity
| Field | Type | Description |
|---|---|---|
vod_urlreq | string | Public Twitch VOD URL, for example https://twitch.tv/videos/123456789. The API accepts Twitch VODs only. |
streamer_name | string | Display name used in generated titles and descriptions. |
game_name | string | Primary game or category, used as context when writing metadata. |
streamer_twitch_url | string (url) | Channel link included in generated descriptions. |
streamer_twitter_url | string (url) | Social link included in generated descriptions. |
streamer_pronouns | string | Pronouns used when generating metadata. Defaults to "they". |
webhook_url | string (url) | Receives job.running, job.completed and job.failed callbacks. |
vod_length_hours | number | Optional. Skips the automatic duration probe when you already know the length. Must be greater than 0. |
What gets produced
| Field | Type | Description |
|---|---|---|
final_video_length_minutes | number | Target runtime of the highlight reel, 1 to 30. Send 0 only together with compilation_enabled: false. |
compilation_enabled | boolean | Defaults to true. Set false for a shorts-only job: no reel is rendered and no generation credits are charged for one. Requires num_shorts greater than 0, and only the first run profile is executed. |
num_shorts | integer | Vertical shorts to generate, 0 to 10, charged at 10 credits each. Defaults to 0. Your plan caps this: 3 free or before any purchase, 5 on Starter, 10 on Pro and Studio. Above your cap it is silently reduced, not rejected, and you are billed the reduced count. |
burn_captions | boolean | Burn in captions on the shorts. Defaults to true. A plain .srt sidecar is produced either way. |
model_preset | string | Processing tier: rapid_sprint, stable_tempo, creator_focus, auto_balance, studio_polish, or director_finish. Sets both quality and the per-hour credit rate. |
stream_language | string | ISO 639-1 code for the stream's language, for example "de". Overrides auto-detection, which a multilingual stream can throw off. |
metadata_language | string | ISO 639-1 code for generated titles, descriptions and tags. Defaults to the stream language. |
run_profiles | string[] | Which analysis profiles to run: balanced, audience, streamer. Defaults to ["balanced"]. Each additional profile renders its own reel and costs its own generation credits. |
run_overrides | object | Per-profile tuning, keyed by profile name. See the table below. |
parameters | object | Everything in the pipeline options table below. Unknown keys are ignored rather than rejected. |
Pipeline options, inside parameters
| Field | Type | Description |
|---|---|---|
min_highlight_duration | integer (s) | Shortest a single highlight may be. Clamped to 180s. |
max_highlight_duration | integer (s) | Longest a single highlight may be. Clamped to 180s, and raised to min_highlight_duration if you send a smaller value. |
cold_open_enabled | boolean | Open the reel with a fast teaser montage of its best moments. |
cold_open_max_clips | integer | Clips in that teaser. Clamped to 2 to 6. |
cold_open_snippet_duration | number (s) | Length of each teaser snippet. Clamped to 2 to 10. |
reel_title_card_enabled | boolean | Show a title card over each segment of the highlight reel. |
shorts_title_card_enabled | boolean | Flash the generated clip title at the start of each short. |
shorts_crop_mode | string | "crop" fills the vertical frame and loses the sides, "fit" keeps the whole frame with padding, "split" stacks facecam over gameplay. Defaults to "crop". |
shorts_split_ratio | number | Split mode only. Share of the vertical frame given to the facecam. Clamped to 0.15 to 0.85, defaults to 0.5. |
shorts_facecam_frame_enabled | boolean | Split mode only. Draw a coloured outline around the facecam panel. |
shorts_facecam_frame_color | string | Split mode only. One of white, black, violet. Anything else falls back to white. |
Per-profile tuning, inside run_overrides
| Field | Type | Description |
|---|---|---|
target_duration_minutes | integer | Reel length for this profile, 1 to 30. |
target_duration_seconds | integer | The same in seconds, 30 to 1800. Takes precedence over the minutes form. |
audience_weight | number | 0 to 1. How much Twitch chat reaction drives moment selection. |
streamer_weight | number | 0 to 1. How much the streamer's own presence drives selection. |
similarity_threshold | number | 0 to 1. Higher values allow more similar moments through. |
highlight_min_score | number | 0 to 1. Score a moment must beat to be considered. |
bucket_size_seconds | integer | 1 to 3600. Granularity of the scoring pass. |
Full example
{
"vod_url": "https://twitch.tv/videos/123456789",
"streamer_name": "SampleCreator",
"game_name": "Any Game",
"streamer_twitch_url": "https://twitch.tv/samplecreator",
"webhook_url": "https://hooks.example.com/viddyflow",
"stream_language": "en",
"metadata_language": "en",
"model_preset": "auto_balance",
"run_profiles": ["balanced"],
"run_overrides": {
"balanced": {
"audience_weight": 0.5,
"streamer_weight": 0.5,
"similarity_threshold": 0.7,
"target_duration_minutes": 8
}
},
"final_video_length_minutes": 8,
"num_shorts": 5,
"burn_captions": true,
"parameters": {
"min_highlight_duration": 30,
"max_highlight_duration": 90,
"cold_open_enabled": true,
"cold_open_max_clips": 4,
"cold_open_snippet_duration": 5,
"reel_title_card_enabled": true,
"shorts_title_card_enabled": true,
"shorts_crop_mode": "crop"
}
}Shorts-only job
Skip the highlight reel entirely and pay no generation credits for one. A run profile is still required, because it decides which moments the shorts are cut from, and only the first one runs.
{
"vod_url": "https://twitch.tv/videos/123456789",
"streamer_name": "SampleCreator",
"compilation_enabled": false,
"final_video_length_minutes": 0,
"run_profiles": ["balanced"],
"num_shorts": 6,
"parameters": {
"shorts_crop_mode": "crop",
"shorts_title_card_enabled": true
}
}Response
Returns immediately with an acknowledgement. Processing happens in the background.
{
"id": "abc123-def456-ghi789",
"status": "pending",
"credit_cost": 138,
"webhook_url": "https://hooks.example.com/viddyflow",
"status_endpoint": "/jobs/abc123-def456-ghi789",
"results_endpoint": "/jobs/abc123-def456-ghi789/results",
"message": "Job accepted. Poll /jobs/{id} or await your webhook callback for results."
}Track a job
Poll GET /jobs/{id}, or set a webhook_url and skip polling entirely. Status is one of pending, running, completed, or failed. There is no separate cancelled state: a cancelled job is reported as failed with a "cancelled by user" message.
curl "https://api.viddyflow.com/jobs/abc123-def456-ghi789" \
-H "X-API-Key: your_api_key_here"Results
Once completed, GET /jobs/{id}/results returns a flat deliverables list: one entry per downloadable file, already joined to its title, duration and tags. Links are presigned and expire, so re-request this endpoint rather than storing them.
Files are not kept forever. Around 30 days after a job last changed, its videos, thumbnails, metadata and caption files are permanently deleted from storage, and the job keeps only its record. The deadline for a given job comes back as artifacts_expires_at. See Your results.
{
"job_id": "abc123-def456-ghi789",
"status": "completed",
"deliverables": [
{
"type": "reel",
"title": "Balanced highlight reel",
"duration_seconds": 123.03,
"run_name": "balanced",
"size_bytes": 45234567,
"url": "https://artifacts.viddyflow.com/...&X-Amz-Signature=..."
},
{
"type": "short",
"index": 1,
"title": "Epic Clutch Play",
"duration_seconds": 38.4,
"tags": ["clutch", "gaming"],
"url": "https://artifacts.viddyflow.com/...&X-Amz-Signature=..."
}
],
"zip_url": "https://api.viddyflow.com/jobs/abc123-def456-ghi789/download-all?token=...",
"links_expire_at": "2026-08-24T15:25:44Z"
}Cancel
POST /jobs/{id}/cancel stops a pending or running job and refunds on a tiered policy: the further it got, the less comes back. Anything already completed or failed returns 400.
{ "id": "abc123-def456-ghi789", "status": "failed", "refund_credits": 96 }Feedback
POST /jobs/{id}/feedback on a completed job feeds highlight-detection tuning. Returns 204 with no body; re-submitting overwrites the previous vote. reason applies to negative votes and must be one of wrong_moments, timing_off, needs_editing, or other. Choosing other requires notes.
{ "positive": false, "reason": "wrong_moments" }Check a VOD first
GET /videos/metadata?vod_url=... returns the title and duration, and tells you why a VOD is unusable (private, subscriber-only, deleted, or still live) before you spend anything.
curl "https://api.viddyflow.com/videos/metadata?vod_url=https://twitch.tv/videos/123456789" \
-H "X-API-Key: your_api_key_here"Errors and limits
| Status | Meaning |
|---|---|
401 | Missing or invalid API key. |
402 | Not enough credits for the job as specified. |
404 | Job does not exist, or belongs to another account. |
422 | Payload rejected. The detail field says which field and why: an unreachable VOD, a still-live broadcast, or an impossible combination such as a 0 minute reel without compilation_enabled: false. |
429 | Rate limited. Back off and retry. |
Rate limit: job creation is capped at 10 requests per minute, counted per client IP. The read endpoints are not explicitly limited, though polling every few seconds is plenty. Contact support if you need more headroom.