Quickstart: REST API
Make a key, submit one video, download the file. Four curl calls.
Read as MarkdownYour first video from a terminal in four calls. Base URL https://app.riffads.com/api/v1.
You need: a RiffAds workspace on a paid plan, curl and jq.
Server side only
No CORS. Never call the API from a browser.
Make a key
Owners and admins create keys at app.riffads.com/api-keys. Tick Generate, or submits fail with 403 insufficient_scope. The key starts with sk_live_ and is shown once.
export RIFFADS_API_KEY="sk_live_..."Estimate
Text-to-video with veo_31: one prompt, one generation. The estimate returns the number you need for max_credits.
/api/v1/estimatesAPI keycurl -s -X POST https://app.riffads.com/api/v1/estimates \
-H "Authorization: Bearer $RIFFADS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"capability_id": "veo_31",
"config": { "prompt": "A matte black water bottle on wet slate, morning light" }
}' | jq '.credits'Submit
POST/api/v1/generationsAPI keycurl -s -X POST https://app.riffads.com/api/v1/generations \
-H "Authorization: Bearer $RIFFADS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"capability_id": "veo_31",
"config": { "prompt": "A matte black water bottle on wet slate, morning light" },
"max_credits": 440
}' | jq -r '.generation_id'max_credits must be above the estimate
max_credits is the spend cap for this job. Send at least ceil(estimate * 1.1). The bare estimate is refused with 402 max_credits_exceeded. The 440 above is only an example.
- Success is
201with ageneration_id(gen_...). Save it. No endpoint lists your generations. - Body is strict:
maxCreditsor any unknown key is a400.
Wait, then download
GET/api/v1/generations/{id}/waitAPI keyEach call blocks up to 20 seconds. Call again while still_running is true.
GEN=gen_... # from the submit
while :; do
BODY=$(curl -s "https://app.riffads.com/api/v1/generations/$GEN/wait" \
-H "Authorization: Bearer $RIFFADS_API_KEY")
[ "$(echo "$BODY" | jq -r '.still_running')" = "false" ] && break
done
echo "$BODY" | jq -r '.generation.status'
echo "$BODY" | jq -r '.generation.outputs[0].url' | xargs curl -L -o ad.mp4completed:outputs[0].urlis a signed link that expires in 600 seconds. Download it, don't store it. Expired? Call wait again for a fresh one.failed: no file. Readgeneration.error.- Branch on
still_running, not onstatus.
If it fails
| Code | Fix |
|---|---|
max_credits_exceeded | Send at least ceil(estimate * 1.1). |
insufficient_scope | Make a key with Generate ticked. |
invalid_config | Read GET /capabilities/veo_31 and fix config. |
submission_in_flight | One submit in flight per key. Wait for the first job. |
insufficient_credits | Top up in the app. |
spend_limit_exceeded | A workspace cap blocked it. Ask an owner. |
moderation_blocked | Change the wording. Don't resend the same text. |
Every error has code, message and retryable. All codes: Errors.