> ## Documentation Index
> Fetch the complete documentation index at: https://docs.modelslab.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Video Batch

> Submit many text-to-video and image-to-video requests under one model in a single call. Every request in a batch is charged at 50% of its single-request price.

### Video Batch

Generate several videos with one request. A batch takes **3 to 20** requests that all run on the same `model_id`, queues each one as its own video, and returns one `batch_id` you can poll for all of them together.

Each request in a batch is billed at **half** the price the matching single endpoint ([Text to Video Ultra](/video-api/text-to-video-ultra) or [Image to Video Ultra](/video-api/image-to-video-ultra)) would charge. In exchange, a request that has not finished **8 minutes** after submission is failed and refunded automatically.

<Note>
  Supported `model_id` values are `wan2.2`, `ltx-2.3`, `h3-minimax-t2v`, `h3-minimax-r2v` and `h3-minimax-start-end-frame`. Requests with an `init_image` take the image-to-video path; the rest take text-to-video. Both kinds can be mixed in one batch.
</Note>

## Request

Make a `POST` request to below endpoint and pass the required parameters in the request body.

```curl curl theme={"theme":{"light":"github-light","dark":"github-dark"}}
--request POST 'https://modelslab.com/api/v6/video/batch' \
```

## Body

```json json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
    "key": "your_api_key",
    "model_id": "wan2.2",
    "track_id": "campaign-42",
    "webhook": null,
    "defaults": {
        "negative_prompt": "blurry, low quality, watermark",
        "num_inference_steps": 20,
        "fps": 16
    },
    "requests": [
        { "track_id": "shot-1", "prompt": "A golden retriever running along a beach at sunset, slow motion" },
        { "track_id": "shot-2", "prompt": "Aerial drone shot over a misty pine forest at dawn" },
        { "track_id": "shot-3", "prompt": "The camera slowly pushes in", "init_image": "https://assets.modelslab.ai/generations/91c519f4-20fe-4834-ac47-fe5c39c44b34" }
    ]
}
```

* `requests` — 3 to 20 entries. Each entry accepts the same parameters as the single text-to-video / image-to-video endpoint for the chosen model.
* `defaults` — optional. Merged under every entry; a value set on an entry wins over the same key in `defaults`.
* `track_id` — an entry's own `track_id` is used for its webhook and result; entries without one inherit the top-level value.
* `webhook` — called once per request as it finishes, exactly as the single endpoints do, with that request's `id` and `track_id`.

## Validation

The batch is validated as a whole before anything is queued. If any entry is invalid, the entire batch is refused, nothing is queued and nothing is charged. The error names the first failing entry and `errors` lists every one:

```json json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
    "status": "error",
    "code": "validation_error",
    "message": "requests.1: The prompt field is required.",
    "errors": [
        { "index": 1, "message": "The prompt field is required." }
    ]
}
```

## Billing

Every request in a batch is charged at **50% of its single-request price**, at submission. The estimate is computed per entry — so a 10-second request costs twice a 5-second one, then both are halved — and the batch is only accepted if your plan allowance or wallet covers the discounted total:

```json json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
    "status": "error",
    "code": "insufficient_balance",
    "message": "Your balance does not cover this batch, please top up or upgrade your plan",
    "estimated_cost": 0.9375
}
```

A request still processing **8 minutes** after submission is marked failed and its charge is refunded. The refund is the discounted amount that was actually taken, and the result reports `refunded: true`. A request that finishes later than that is not delivered.

## Concurrency

A batch of N counts as N queued requests against your plan's [request queue limit](/rate-limits). The whole batch must fit in the slots you have free at submission — a plan with 10 slots and 4 videos already processing can submit a batch of at most 6:

```json json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
    "status": "error",
    "code": "rate_limited",
    "message": "Batch of 8 exceeds your available concurrency slots",
    "available_slots": 6
}
```

It also counts as N requests in the hourly window. The `RateLimit-*` response headers reflect the full weight.

## Response

The response is keyed like a single video response — `status`, `id`, `output`, `message`, `tip` — with the batch fields on top. `id` and `batch_id` are the same integer, drawn from the same counter as video ids, so a batch id never collides with a video id. Each entry in `results` is the same body the matching single endpoint would have returned for that request, plus its `index` and `track_id`.

```json json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
    "status": "processing",
    "id": 48213,
    "batch_id": 48213,
    "message": "Batch queued for processing. Try to fetch request after estimated seconds.",
    "tip": "Get 20x faster image generation using enterprise plan. Click here : https://modelslab.com/enterprise",
    "eta": 80,
    "fetch_result": "https://modelslab.com/api/v6/video/batch/fetch/48213",
    "output": [],
    "future_links": [
        "https://cdn2.modelslab.com/generations/48214.mp4",
        "https://cdn2.modelslab.com/generations/48215.mp4",
        "https://cdn2.modelslab.com/generations/48216.mp4"
    ],
    "meta": { "model_id": "wan2.2", "track_id": "campaign-42", "webhook": null },
    "timeout_minutes": 8,
    "discount": 0.5,
    "request_counts": { "total": 3, "processing": 3, "succeeded": 0, "failed": 0 },
    "results": [
        {
            "index": 0,
            "track_id": "shot-1",
            "status": "processing",
            "id": 48214,
            "eta": 80,
            "fetch_result": "https://modelslab.com/api/v6/video/fetch/48214",
            "output": [],
            "future_links": ["https://cdn2.modelslab.com/generations/48214.mp4"],
            "meta": { "prompt": "A golden retriever running along a beach at sunset, slow motion", "..." : "..." }
        },
        { "index": 1, "track_id": "shot-2", "status": "processing", "id": 48215, "...": "..." },
        { "index": 2, "track_id": "shot-3", "status": "processing", "id": 48216, "...": "..." }
    ]
}
```

Poll the whole batch on [Fetch Video Batch](/video-api/fetch-video-batch) with `batch_id`, or any single request on [Fetch Video](/video-api/fetch-video) with its own `id`.


## OpenAPI

````yaml POST /video/batch
openapi: 3.1.0
info:
  title: ModelsLab Video API
  description: >-
    A comprehensive API for AI-driven video generation including text-to-video,
    image-to-video, scene transitions, and video management capabilities
  license:
    name: MIT
  version: 6.0.0
servers:
  - url: https://modelslab.com/api/v6
security: []
paths:
  /video/batch:
    post:
      summary: Generate many videos in one batch
      description: >-
        Submits 3 to 20 text-to-video / image-to-video requests under one model.
        Every request is charged at 50% of its single-request price and is
        failed and refunded if it has not finished within 8 minutes. The batch
        is validated as a whole: one invalid entry refuses all of them before
        anything is queued or charged. A batch of N counts as N queued requests
        against your concurrency limit.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/VideoBatchRequest'
      responses:
        '200':
          description: >-
            Batch accepted, or refused whole (validation_error,
            insufficient_balance, rate_limited). Always HTTP 200.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/VideoBatchResponse'
                  - $ref: '#/components/schemas/VideoBatchError'
        '401':
          description: Unauthorized - invalid API key
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    VideoBatchRequest:
      type: object
      required:
        - key
        - model_id
        - requests
      properties:
        key:
          type: string
          description: Your API Key used for request authorization
        model_id:
          type: string
          enum:
            - wan2.2
            - ltx-2.3
            - h3-minimax-t2v
            - h3-minimax-r2v
            - h3-minimax-start-end-frame
          default: wan2.2
          description: The video model every request in the batch runs on
        requests:
          type: array
          minItems: 3
          maxItems: 20
          items:
            $ref: '#/components/schemas/VideoBatchEntry'
          description: >-
            3 to 20 requests. Each takes the same parameters as the single
            text-to-video / image-to-video endpoint for the chosen model; an
            entry with init_image takes the image-to-video path
        defaults:
          type: object
          additionalProperties: true
          description: >-
            Parameters merged under every entry. A value set on an entry wins
            over the same key here
        webhook:
          type: string
          format: uri
          description: >-
            Called once per request as it finishes, with that request's id and
            track_id
        track_id:
          type: string
          description: Inherited by every entry that does not set its own
    VideoBatchResponse:
      type: object
      description: >-
        Keyed like a single video response (status, id, output, message, tip)
        with the batch fields on top
      properties:
        status:
          type: string
          enum:
            - processing
            - success
            - failed
          description: >-
            processing on submit and while any request is still running; success
            once all succeeded; failed once all failed
        id:
          type: integer
          description: >-
            The batch id. Same value as batch_id; drawn from the same counter as
            video ids so the two never collide
        batch_id:
          type: integer
        message:
          type: string
        tip:
          type: string
        output:
          type: array
          items:
            type: string
            format: uri
          description: >-
            Every finished video across the batch in request order; empty on
            submit
        future_links:
          type: array
          items:
            type: string
            format: uri
          description: 'On submit: the URL every request will publish to, in request order'
        fetch_result:
          type: string
          format: uri
          description: 'On submit: URL to poll the whole batch'
        eta:
          type: integer
          description: 'On submit: the largest per-request estimate, in seconds'
        meta:
          type: object
          additionalProperties: true
          description: 'On submit: the envelope inputs (model_id, track_id, webhook)'
        timeout_minutes:
          type: integer
          description: 'On submit: requests not finished by then are failed and refunded'
        discount:
          type: number
          description: 'On submit: fraction taken off every request''s single-request price'
        request_counts:
          type: object
          properties:
            total:
              type: integer
            processing:
              type: integer
            succeeded:
              type: integer
            failed:
              type: integer
        results:
          type: array
          items:
            $ref: '#/components/schemas/VideoBatchResult'
    VideoBatchError:
      type: object
      required:
        - status
        - code
        - message
      properties:
        status:
          type: string
          enum:
            - error
        code:
          type: string
          enum:
            - validation_error
            - insufficient_balance
            - rate_limited
        message:
          type: string
        errors:
          type: array
          items:
            type: object
            properties:
              index:
                type: integer
              message:
                type: string
          description: 'On validation_error: every invalid entry'
        estimated_cost:
          type: number
          description: >-
            On insufficient_balance: the discounted total the batch would have
            cost
        available_slots:
          type: integer
          description: 'On rate_limited: queued-request slots currently free'
    Error:
      type: object
      required:
        - status
        - message
      properties:
        status:
          type: string
          enum:
            - error
        message:
          type: string
          description: Error message description
    VideoBatchEntry:
      type: object
      additionalProperties: true
      properties:
        prompt:
          type: string
          description: Text prompt describing the video content. Required for text-to-video
        init_image:
          type: string
          format: uri
          description: >-
            Initial image URL (or base64 with base64: true). Present: the entry
            is image-to-video
        negative_prompt:
          type: string
          description: Items you don't want in the video
        track_id:
          type: string
          description: This entry's correlation id, returned in its result and webhook
        seed:
          oneOf:
            - type: integer
            - type: 'null'
          description: Seed for reproducible results
        num_frames:
          type: integer
          description: Number of frames in the video
        num_inference_steps:
          type: integer
          description: Number of denoising steps
        guidance_scale:
          type: number
          minimum: 0
          maximum: 8
          description: Scale for classifier-free guidance
        fps:
          type: integer
          minimum: 16
          maximum: 25
          description: Frames per second rate
        duration:
          type: number
          minimum: 1
          description: Video length in seconds. The price scales with it
        portrait:
          type: boolean
          default: false
          description: Portrait mode gives output in 9:16 aspect ratio
        sample_shift:
          type: integer
          description: Controls the sampling shift in the generation process
        base64:
          type: boolean
          default: false
          description: init_image is base64 rather than a URL
        temp:
          type: boolean
          default: false
          description: Store in temporary storage
    VideoBatchResult:
      type: object
      description: >-
        One request of the batch: the same body the single endpoint returns for
        it, plus its position and correlation id
      additionalProperties: true
      properties:
        index:
          type: integer
          description: Position in the submitted requests array
        track_id:
          oneOf:
            - type: string
            - type: 'null'
        status:
          type: string
          enum:
            - processing
            - success
            - failed
            - error
        id:
          type: integer
          description: This request's own video id, usable on /video/fetch/{id}
        output:
          oneOf:
            - type: array
              items:
                type: string
                format: uri
            - type: string
          description: The finished video URL(s); empty until then
        future_links:
          type: array
          items:
            type: string
            format: uri
        fetch_result:
          type: string
          format: uri
        eta:
          type: integer
        message:
          type: string
        refunded:
          type: boolean
          description: 'On a failed result: whether its charge was reversed'

````