---
title: "Create a batch"
method: POST
path: "/v1/batches"
tags: ["Batches"]
---

# Create a batch

`POST /v1/batches`

## Request

- Base URL: `https://api.tryleap.ai`
- URL: `https://api.tryleap.ai/v1/batches`
- Auth: one of:
  - API key in header `x-api-key`
  - HTTP bearer

Starts up to 50 generations in one request. They're priced and held together, so either all start or none do, and each then runs as its own generation with this batch's `batch_id`. Read each one with `GET /v1/generations/{id}`. One request that would sexualise a real person from a photo you uploaded refuses the whole batch with `422 sexualized_real_person`.

Needs an API key with the `generations:write` scope.

## Headers

- `Idempotency-Key` string: A key you generate for this request, such as a UUID, so a retry never starts a second run. A retry with the same key and body returns the same generation as it is now, marked `Idempotent-Replayed: true`. The same key with a different body is a `409`. A key belongs to your workspace and doesn't expire, so use a new one for each new request.

## Request body

- object
  - `requests` object[], required: Up to 50 generations, each a model and its input. All are priced and held together.
    - `model` string, required: The model to call, as creator/name. GET /v1/models lists them.
    - `input` object
    - `webhook` string, uri: An https URL to POST the generation.succeeded, generation.failed or generation.canceled event to when the run ends, signed with your workspace's default secret (GET /v1/webhooks/default/secret).

## Response `201`

The batch, with each generation queued.

- Batch: Up to 50 generations priced and held together, each run on its own.
  - `id` string, required: The batch's ID, `bat_...`.
  - `object` 'batch', required
  - `data` Generation[], required: Its generations, in the order you sent them.
    - `id` string, required: The generation's ID, `gen_` and 24 letters and digits.
    - `object` 'generation', required
    - `model` string, required: The model or preset it runs, with a preset's version.
    - `modality` 'image' | 'video' | 'audio' | '3d' | 'text', required: What it makes: `image`, `video`, `audio`, `3d` or `text`.
    - `status` 'queued' | 'running' | 'succeeded' | 'failed' | 'canceled', required: `queued`, then `running`, then one of `succeeded`, `failed` or `canceled`. Only `succeeded` is charged.
    - `provider` string, nullable, required: Which upstream ran it, once one did. Informational: the model ID and input stay the same whichever runs it.
    - `revision` string, required: The model's schema and price revision the run used.
    - `input` object, required: The input as it runs, with the defaults filled in.
    - `output` object[], required
      - `type` 'image' | 'video' | 'audio' | '3d' | 'text', required: What the file is: `image`, `video`, `audio`, `3d` or `text`.
      - `url` string, uri, required: A signed link to the file. Download what you keep: links expire after 24 hours, and reading the generation again signs fresh ones.
      - `expires_at` string, date-time, required: When `url` stops working, 24 hours after it was signed.
      - `content_type` string, required: The file's media type, such as `image/jpeg` or `video/mp4`.
      - `width` integer, nullable, required: Width in pixels; null for a sound.
      - `height` integer, nullable, required: Height in pixels; null for a sound.
      - `poster` object: For a video: a JPEG of a frame from its middle, about 720 pixels on the long side. Absent when none was made.
        - `url` string, uri, required: A signed link to the poster, which expires with the video's link.
        - `expires_at` string, date-time, required
        - `width` integer, required
        - `height` integer, required
    - `preview` object: While a preset that makes a still first is running: that still, once it exists, before the video it becomes. Absent for every other run, and for a run whose video also reads words or a photo you sent, such as a line to say or a photo the clip starts from: that still comes with the video. Also absent for the rest of a UTC day once 10 of the workspace's runs that showed one failed for their input.
      - `url` string, uri, required: A signed link to a smaller JPEG of the still, about 960 pixels on the long side. It works for 10 minutes; read the run again for a fresh one. The still itself comes with the finished run's output.
      - `expires_at` string, date-time, required
      - `width` integer, nullable, required
      - `height` integer, nullable, required
    - `error` object, nullable, required: Why a `failed` run failed, such as a refused prompt or a provider error; null otherwise. A failed run isn't charged.
      - `message` string, required
    - `failure_cause` 'provider_error' | 'provider_refused' | 'photo_blocked' | 'timeout' | 'outage' | 'input_removed' | 'input_rejected', nullable, required: What made a `failed` run fail, for a program to branch on: `provider_error`, `provider_refused` (the model's safety filter said no), `photo_blocked` (the photo check refused the photo), `timeout`, `outage` (the provider had a declared incident or could not serve Leap), `input_removed` (an input file was gone) or `input_rejected` (the provider couldn't use an input file: too small, an unreadable format, or no face). Null for any other run, and for a failure from before causes were recorded.
    - `usage` object, required: What the run cost your workspace.
      - `cost_usd` string, nullable, required: What the run cost, as an exact decimal string in US dollars; null until it's charged.
    - `source` 'studio' | 'api' | 'mcp', nullable, required: Where it was started: `api`, `mcp` or `studio`.
    - `batch_id` string, nullable, required: The batch it belongs to, `bat_...`; null if none.
    - `created_at` string, date-time, required: When it was created.
    - `started_at` string, date-time, nullable, required: When it started running; null while queued.
    - `completed_at` string, date-time, nullable, required: When it reached a final status; null before.
    - `slow_at` string, date-time, nullable, required: While a preset runs: when it counts as slow, one and a half times what Leap's runs of it take after `started_at`, scaled to this run's length, resolution and count. From then on a cancel works whatever the run has done, even once its `preview` shows. Null for any other run.
    - `deadline_at` string, date-time, nullable, required: While a preset runs: when Leap stops it if it hasn't finished, three times what Leap's runs of it take after `started_at`, scaled to this run's length, resolution and count, between 5 minutes (2 for a picture) and 12. It then fails with the `failure_cause` `timeout` and costs nothing. Null for any other run.

## Other responses

- `400`: `invalid_parameter`, `conflicting_credentials`
- `401`: `authentication_required`
- `402`: `insufficient_credit`, `top_up_required`
- `403`: `permission_denied`
- `409`: `conflict`, `unpaid_failure_cap`
- `410`: `gone`
- `422`: `photo_blocked`, `sexualized_real_person`
- `429`: `rate_limit_exceeded`
- `503`: `service_unavailable`

---

[API](https://skmtc.dev/tryleap/apis/leap-api.md) · [All operations](https://skmtc.dev/tryleap/apis/leap-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tryleap/apis/leap-api/revisions/c01f98d3e835?raw)
