---
title: "Create a generation"
method: POST
path: "/v1/generations"
tags: ["Generations"]
---

# Create a generation

`POST /v1/generations`

## Request

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

Starts a run of any model or preset. Every model takes this same shape; only `input` differs, as the model's `input_schema` says. The price is held from your balance first, and charged only if the run succeeds.

Without `Prefer`, the answer comes at once with the generation `queued`. With `Prefer: wait=60`, it comes when the run finishes or after 60 seconds, whichever is first: most images finish inside the wait, and video doesn't. Send an `Idempotency-Key` so a retry never starts a second run.

A run that would sexualise a real person from a photo you uploaded is refused with `422 sexualized_real_person` before anything is held: words that ask to undress them or show them in a sexual way, on the photo or on an image Leap made from it, or a face swap or a recast into a sexual picture or clip.

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.
- `Prefer` string: `wait=N` holds the request open for up to N seconds (at most 60) until the generation finishes, and `Preference-Applied` echoes the wait used. Most images finish inside the wait. If the run is still going when it ends, you get it back `queued` or `running`; poll it from there. Without this header the generation comes back queued at once.

## Request body

- GenerationRequest
  - `model` string, required: The model or preset to run, such as `black-forest-labs/flux-2-pro` or `leap/restore-photo@1`. A preset ID without `@N` runs its latest version.
  - `input` object: The model's input, as its `input_schema` from `GET /v1/models/{creator}/{name}` describes it. Fields you leave out take their defaults; unknown fields are refused. Files are `file_...` IDs.
  - `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 generation: `queued` at once, or as it is when the wait ends.

- Generation: One run of a model or preset, with its status, its outputs and what it cost.
  - `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)
