Quickstarts

Quickstart: REST API

Make a key, submit one video, download the file. Four curl calls.

Read as Markdown

Your 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.

Terminal
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.

POST/api/v1/estimatesAPI key
Terminal
curl -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 key
Terminal
curl -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 201 with a generation_id (gen_...). Save it. No endpoint lists your generations.
  • Body is strict: maxCredits or any unknown key is a 400.

Wait, then download

GET/api/v1/generations/{id}/waitAPI key

Each call blocks up to 20 seconds. Call again while still_running is true.

Terminal
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.mp4
  • completed: outputs[0].url is 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. Read generation.error.
  • Branch on still_running, not on status.

If it fails

CodeFix
max_credits_exceededSend at least ceil(estimate * 1.1).
insufficient_scopeMake a key with Generate ticked.
invalid_configRead GET /capabilities/veo_31 and fix config.
submission_in_flightOne submit in flight per key. Wait for the first job.
insufficient_creditsTop up in the app.
spend_limit_exceededA workspace cap blocked it. Ask an owner.
moderation_blockedChange the wording. Don't resend the same text.

Every error has code, message and retryable. All codes: Errors.

Next

On this page