Skip to main content

Overview

Patterns that keep an integration reliable: how to store keys, handle errors, avoid duplicate videos, receive results and keep an eye on costs.

Authentication and security

Store API keys securely

Call the API only from your servers. A key in browser or mobile code can be extracted and used to spend your team’s credits.

Use different keys per environment

Separate keys let you delete one without affecting the other.

Rotate keys

Create the new key in Settings → Developer API, deploy it, confirm traffic works, then delete the old key. Deleted keys answer 401 right away.

Error handling

Branch on status and code

  • 400: fix the request; the message names the field.
  • 401: check the key.
  • 402: billing. subscription_required, INSUFFICIENT_CREDITS or missing_credentials (with missingProviders).
  • 403: the team lacks the product, or the request uses another team’s files.
  • 404: not found, or not yours.
  • 429: your team is over its rate limit. Wait Retry-After seconds; nothing ran, so any request can be sent again.
  • 5xx: our side.
The full list is in Error Handling.

Retry GETs, not creates

Reading (GET /v1/project/{id}, lists) is safe to retry with backoff:
A create that timed out or answered 5xx may still have created and charged the project. Only retry a create automatically with an Idempotency-Key header, the same key on every attempt: the retry then returns the first answer instead of creating a second project. Without a key, record the attempt, check whether a project exists, and only then decide to send it again.

Avoiding duplicate videos

Send an Idempotency-Key (your job id works) with every create, and guard them on your side too:
  1. Give each job your own id and save it before calling the API.
  2. Send it in metadata ({ "jobId": "job_123" }), so every webhook tells you which job it belongs to.
  3. Save the returned projectId with the job immediately.
  4. If the call failed without an answer, mark the job “unknown” and check your dashboard or GET /v1/video/list before re-sending.

Performance

Cache resources

Avatars, voices and music change rarely. Cache the lists for a few hours instead of fetching them before every video.

Batch operations

Each create starts real work and may charge credits. Keep a few requests in flight at a time rather than firing a whole list at once, and stop the batch on 401 or 402. See Bulk Generation.

Minimize API calls

  • Use webhooks so you do not need to poll at all, or poll every 10 to 15 seconds rather than every second.
  • Poll the project you care about (GET /v1/project/{projectId}), not the whole video list.

Webhook implementation

Webhooks plus a polling fallback

Webhooks are the cheapest way to hear about finished videos, but each one is tried only 3 times in a row and then dropped. Keep a periodic job that polls projects you have not heard about for a while.

Protect your endpoint

Every webhook call is signed. Verify the Hooked-Signature header against your signing secret (Settings → Webhooks) and reject anything that does not match or is older than five minutes. See Verify the signature for Node and Python code. A secret token in the webhook URL (?token=...) still works as an extra check, but it is optional. For sensitive actions, confirm the result with GET /v1/project/{projectId} before acting on it.

Handle duplicates

A late answer from your endpoint leads to a retry with the same payload. Make processing idempotent, keyed on videoId (or projectId when videoId is null):

Return 2xx immediately

You have 10 seconds. Acknowledge first, then download the video or update records in the background:

Costs

  • On a managed team, generation is charged in credits. A 402 INSUFFICIENT_CREDITS tells you creditsNeeded and creditsAvailable. Credits for failed projects are refunded automatically.
  • GET /v1/project/{projectId} returns usedCredits for each project, so you can track spend per job (it goes back to 0 when a failed project is refunded).
  • On a byok team, generation is billed by your own providers; set up every key your formats need to avoid 402 missing_credentials.
  • Re-rendering with POST /v1/render is free.
See Credits and your own keys.

Logging and monitoring

Structured logging

Log the ids you need to trace a video end to end:
Never log the API key, and strip the webhook token from logged URLs.

Track key metrics

  • Projects created, completed and failed per day.
  • Time from create to COMPLETED.
  • 402 responses (credits or keys running out).
  • Webhooks that never arrived (caught by the polling fallback).

Data validation

Validate before sending

Catch obvious problems before they cost a request:
Use ids from the list endpoints rather than hard-coding them.

Testing

Unit test your client

Mock fetch and check how your code handles each answer:

Integration testing

Run real requests against a development key on a few short scripts. Keep these tests out of every CI run: each one creates a real project.

Code organization

Wrap the API in one small module so the base URL, headers, error handling and retries live in one place:

Deployment

Environment configuration

Health checks

GET /v1/health needs no API key and reports whether the API is up:
It answers 200 with status: ok, or 503 with status: error when the database does not answer, plus a timestamp and the database check (services.database). To check that your key still works, call a cheap authenticated endpoint such as GET /v1/avatar/list?limit=1.

Production checklist

1

Security

API key in a secret store, server-side only; webhook URL protected by a token.
2

Reliability

GETs retried with backoff; creates never retried blindly; projectId stored right after each create.
3

Results

Webhook endpoint answers 2xx within 10 seconds and deduplicates; polling fallback for missing webhooks; videos copied to your own storage.
4

Billing

Enough credits (managed) or every provider key the formats need (byok); 402 alerts in place.
5

Monitoring

Logs with projectId / videoId; failure rate and time to completion tracked.

Quick reference