API reference

Videos

Create an asynchronous video job from text or reference media. Poll the stable OTRouter video ID until it reaches a terminal state, then read the generated bytes from the content endpoint.

GEThttps://router.omnitools.ai/catalog/modelsList logical models, capabilities and public tariffs · public
POSThttps://router.omnitools.ai/v1/videosCreate a video job
GEThttps://router.omnitools.ai/v1/videos/{video_id}Read status and progress
GEThttps://router.omnitools.ai/v1/videos/{video_id}/contentRead completed video content

Choose a model

GET /catalog/models without a Bearer key, then select an entry whose modality is video and whose protocols include videos. Create requests with its logical model ID and advertised capabilities.

Video model catalog entry
{
  "id": "seedance-2.0",
  "modality": "video",
  "protocols": [
    "videos"
  ],
  "capabilities": {
    "modes": [
      "references"
    ],
    "prompt": {
      "min": 10,
      "max": 5000
    },
    "durations": {
      "min": 4,
      "max": 15
    },
    "references": {
      "total": 12,
      "audios": 3,
      "images": 9,
      "videos": 3,
      "audioVideos": 3
    },
    "resolutions": [
      "720p",
      "1080p"
    ],
    "allowsPeople": true,
    "aspectRatios": [
      "16:9",
      "9:16"
    ],
    "combinations": [
      {
        "prompt": {
          "min": 10,
          "max": 5000
        },
        "modes": [
          "references"
        ],
        "resolutions": [
          "1080p"
        ],
        "aspectRatios": [
          "16:9",
          "9:16"
        ],
        "durations": {
          "min": 4,
          "max": 15
        },
        "references": {
          "images": 9,
          "audios": 3,
          "videos": 3,
          "audioVideos": 3,
          "total": 12
        },
        "allowsPeople": true
      },
      {
        "prompt": {
          "min": 10,
          "max": 5000
        },
        "modes": [
          "references"
        ],
        "resolutions": [
          "720p"
        ],
        "aspectRatios": [
          "16:9",
          "9:16"
        ],
        "durations": {
          "min": 4,
          "max": 15
        },
        "references": {
          "images": 9,
          "audios": 3,
          "videos": 3,
          "audioVideos": 3,
          "total": 12
        },
        "allowsPeople": true
      },
      {
        "prompt": {
          "min": 10,
          "max": 5000
        },
        "modes": [
          "references"
        ],
        "resolutions": [
          "720p"
        ],
        "aspectRatios": [
          "16:9",
          "9:16"
        ],
        "durations": {
          "min": 5,
          "max": 15
        },
        "references": {
          "images": 9,
          "audios": 3,
          "videos": 3,
          "audioVideos": 3,
          "total": 12
        },
        "allowsPeople": true
      }
    ]
  },
  "publicPricing": {
    "type": "video",
    "rates": [
      {
        "resolution": "720p",
        "minimumAmount": "12.000000000000000000",
        "secondRate": "1.400000000000000000"
      },
      {
        "resolution": "1080p",
        "minimumAmount": "0.000000000000000000",
        "secondRate": "1.600000000000000000"
      }
    ],
    "effectiveAt": "2026-08-18T00:00:00.000Z"
  }
}

Select publicPricing.rates by the required resolution. Charge = max(minimumAmount, duration × secondRate); a zero secondRate means minimumAmount is the fixed per-request charge. This public tariff is determined before internal routing.

Authentication

Authenticate every /v1/videos request with the same Team API key. Keep the key in a trusted server environment, including status and content requests.

Authorization: Bearer $OTRouter_API_KEY

POST requests require Content-Type: application/json. GET requests do not have a request body.

Create a video

Submit one model-specific generation request. The response is a queued video object; creation does not wait for rendering to finish.

curl https://router.omnitools.ai/v1/videos \
  -H "Authorization: Bearer $OTRouter_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "model": "seedance-2.0",
  "prompt": "A quiet coastal road at sunrise, cinematic camera movement.",
  "mode": "references",
  "aspect_ratio": "16:9",
  "duration": 5,
  "resolution": "720p",
  "client_task_id": "order_20260816_000001"
}'

Create parameters

Always send resolution and duration so OTRouter can select capabilities and determine the public charge before routing. Their supported values, aspect ratios and media capabilities come from the selected video model.

ParameterRequirementDescription
modelRequiredStable logical video model ID from GET /catalog/models.
promptRequiredText description of the requested video. Length limits are model-specific.
modeRequiredUse references for text or reference-media generation.
aspect_ratioOptionalRequested frame ratio. Allowed values depend on the model.
resolutionRequiredRequested output resolution. Must match a value in capabilities.resolutions and publicPricing.rates.
durationRequiredRequested video length in seconds. Must satisfy the selected model's capabilities.durations.
referencesModel-specificReference-media object accepted only with mode: references.
client_task_idRequiredClient idempotency key: 1–128 letters, numbers, underscores, hyphens or periods.

Reference media

Use mode: references for text-only generation or attach publicly reachable media URLs allowed by the selected model. Direct file uploads, base64 values and data URLs are not accepted.

References example
{
  "references": {
    "image": "https://example.com/reference.jpg",
    "videos": [
      "https://example.com/reference-1.mp4",
      "https://example.com/reference-2.mp4"
    ]
  }
}
ParameterTypeDescription
imagestringExactly one reference image URL.
imagesstring[]Two or more reference image URLs.
videomodel-specificExactly one reference video URL.
videosmodel-specific[]Two or more reference video URLs.
audiomodel-specificExactly one reference audio URL.
audiosmodel-specific[]Two or more reference audio URLs.

Each singular/plural pair is mutually exclusive. Plural fields require at least two entries. capabilities.references limits each media type, audioVideos limits audio and video combined, and total limits all assets combined.

Asynchronous lifecycle

Creation returns immediately. Poll the returned video ID at a reasonable interval until status becomes completed or failed.

StatusPhaseDescription
queuedActiveAccepted and waiting to begin generation.
in_progressActiveGeneration is running; progress may increase from 0 through 100.
completedTerminalGeneration succeeded. Read the bytes from the content endpoint.
failedTerminalGeneration failed. metadata.fail_reason contains the public failure reason.
unknownActiveSubmission was attempted but OTRouter cannot confirm its upstream task identity. The same request returns the same OTRouter video ID without another submission; an operator reconciles the upstream task ID before settlement.

Retrieve a video

GET the video resource to read its status, progress and terminal metadata. The video belongs to the Team identified by the API key.

curl https://router.omnitools.ai/v1/videos/video_0123456789abcdef0123456789abcdef \
  -H "Authorization: Bearer $OTRouter_API_KEY"
Create response
{
  "id": "video_0123456789abcdef0123456789abcdef",
  "object": "video",
  "model": "seedance-2.0",
  "status": "queued",
  "progress": 0,
  "created_at": 1786838400
}
Completed response
{
  "id": "video_0123456789abcdef0123456789abcdef",
  "object": "video",
  "model": "seedance-2.0",
  "status": "completed",
  "progress": 100,
  "created_at": 1786838400,
  "completed_at": 1786838460,
  "metadata": {
    "url": "/v1/videos/video_0123456789abcdef0123456789abcdef/content"
  }
}

Video object

The same public ID and model are preserved throughout the job. completed_at and metadata appear when the job reaches a terminal state.

ParameterPresentDescription
idAlwaysOTRouter video ID used by the status and content endpoints.
objectAlwaysThe constant value video.
modelAlwaysStable public model ID submitted at creation.
statusAlwaysCurrent asynchronous lifecycle state.
progressAlwaysBest-known completion percentage from 0 through 100.
created_atAlwaysCreation time as Unix seconds.
completed_atTerminalCompletion or failure time as Unix seconds.
metadata.urlcompletedRelative OTRouter content path for the generated video.
metadata.fail_reasonfailedPublic reason the generation did not complete.

Read video content

After status is completed, GET the content endpoint with the same Bearer credential. The response streams binary video data and supports Range requests; do not parse it as JSON.

curl https://router.omnitools.ai/v1/videos/video_0123456789abcdef0123456789abcdef/content \
  -H "Authorization: Bearer $OTRouter_API_KEY" \
  --continue-at - \
  --output output.mp4

Omit Range to stream the complete file, or send Range: bytes={start}-{end} for a partial or resumed read. Before completion, the endpoint returns 404 video_not_ready. A well-formed but unsatisfiable range returns an empty 416 with only safe Content-Range and Accept-Ranges headers.

Idempotency

client_task_id identifies one logical generation request inside the authenticated Team. Choose it before the first attempt and keep it stable across uncertain transport retries.

First attempt

Create a new client_task_id from your own order or job identity.

Safe replay

The same key and normalized request reuse the original OTRouter video ID and Request without starting or charging a second logical job.

Conflicting replay

The same key with different parameters returns 409 idempotency_conflict.

Submission unknown

A 503 submission_unknown includes the stable OTRouter video ID in x-video-id. Replaying the same request returns that ID and never submits upstream again; an operator reconciles the upstream task ID before settlement.

Accepted job

Keep the returned video ID and use GET for all status and content reads.

New generation

Use a new client_task_id whenever you intend to create a distinct video.

Errors

Errors use the standard OTRouter JSON envelope, except the media content endpoint's empty RFC 416 response. Invalid modes, conflicting media fields and model-specific constraint failures are rejected before generation begins.

Error response
{
  "error": {
    "code": "invalid_params",
    "message": "Invalid video request.",
    "requestId": "REQUEST_ID"
  }
}
HTTPRouter codeDescription
400invalid_paramsThe JSON shape, mode, media combination or model-specific value is invalid.
401invalid_api_keyThe Bearer API key is missing, invalid, expired or deleted.
402insufficient_creditsThe Team cannot fund a new generation.
404model_not_foundThe requested public video model is unavailable.
404task_not_foundThe video ID is invalid, unavailable to this Team or does not exist.
404video_not_readyContent was requested before the video completed.
409idempotency_conflictclient_task_id was already used with a different normalized request.
413payload_too_largeThe create request body exceeds 256 KiB.
416invalid_rangeRange is invalid or contains more than one byte range.
429api_key_spend_limit_exceededThe API key reached a rolling spend limit.
503submission_unknownThe generation may have been accepted but OTRouter cannot identify its upstream task. x-video-id remains stable, the request is not submitted again, and settlement waits for operator reconciliation.

For submission_unknown, keep x-video-id for status checks and operator reconciliation. Keep x-request-id and error.requestId when contacting support.