Genace

API generate video: the full request and response, end to end

The request

curl -X POST https://genace.ai/api/v1/video/generations \
  -H "Authorization: Bearer $GENACE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedance-2.0-fast",
    "prompt": "a cyclist on a coastal road at golden hour, camera tracking alongside",
    "duration_sec": 3
  }'

Both snake_case and camelCase are accepted for field names.

The response is a job

{
  "job_id": "abc123",
  "state": "queued",
  "model": "seedance-2.0-fast",
  "credits_held": 25
}

202 Accepted. The render takes minutes; credits_held is what was reserved and comes back in full if the job fails or you cancel it.

Collecting the output

curl "https://genace.ai/api/v1/jobs/abc123?wait=30" \
  -H "Authorization: Bearer $GENACE_API_KEY"

On success:

{
  "job_id": "abc123",
  "state": "succeeded",
  "outputs": [{ "url": "https://...", "type": "video" }]
}

The two that surprise people

A failed job is a 200. It describes a failure in the body, so raise_for_status() and equivalents will not fire. Check state.

Both 429s need opposite handling. Rate limiting clears with time; concurrency clears only when one of your own jobs finishes. Retrying blindly helps in one case and spins in the other. Branch on error.code.

Choosing a model

ModelCredits per clipDurationResolution
Seedance253–15s720p, 1080p
Kling355 or 10s1080p
Veo 31008s fixed720p, 1080p

Duration control usually decides this more than price does — AI video API duration limits.

A working Python implementation with the polling loop is in how to generate video with an API in Python.

Models covered on this page

Where these facts come from

  • codebase: src/ai/providers/*.ts — every pricing() and paramsSchema
  • codebase: src/ai/api/quota.ts — per-tier ceilings
  • codebase: src/app/api/v1/video/generations/route.ts — request and response shape