# Create generation

Source: https://docs.foodshot.ai/reference/create-generation

> Creates a generation from one photo.

`POST https://api.foodshot.ai/v1/generations`

> **The quality of your photo strongly affects the result.** See the [Photo guide](https://docs.foodshot.ai/photo-guide).

The answer is `202 Accepted` with the generation, usually within seconds, and the job runs in the background. Our objective is about 30–40 seconds once it starts.

Every request needs an `Idempotency-Key`: one UUID per photo, reused with the same body when you retry. A live job takes 1 credit, and a failed job is refunded.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | string | yes | One unique value per photo, such as a UUID. Reuse it with the same body when you retry. Remembered for 24 hours. |

## Request body

Content type: `application/json`

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `image_url` | string | no | An `https` URL of the photo, up to 2,048 characters. JPEG, PNG, WebP or HEIC, up to 20 MB. On a retry, send the same URL. |
| `image_base64` | string | no | The photo file in base64, if it has no URL. The whole request body can be up to 10 MB. |
| `style_id` | string | no | A style id from `GET /v1/styles`. |
| `instruction` | string | no | A short note in plain words, such as "Keep the blue bowl". Up to 10,000 characters. Without a `style_id`, it describes the whole look. |
| `aspect_ratio` | "1:1" \| "2:3" \| "3:2" \| "3:4" \| "4:3" \| "4:5" \| "5:4" \| "9:16" \| "16:9" \| "21:9" | no | Width to height of the result. Default: `"1:1"`. |
| `resolution` | "2k" \| "4k" | no | The size of the result, `4k` by default. Both cost 1 credit. Default: `"4k"`. |
| `format` | "jpeg" \| "png" | no | The file type of the result, `jpeg` by default. The file is made and kept in this type. Default: `"jpeg"`. |
| `metadata` | object | no | Up to 10 string pairs that you choose, returned with the generation. Keys and values up to 200 characters. Opaque ids only, never personal data. |
| `test_scenario` | "succeeded" \| "content_refused" \| "generation_failed" \| "slow" | no | Test keys only. Picks the outcome of a test job; `slow` stays `processing` for about 2 minutes, then succeeds. Default: `"succeeded"`. |
| `webhook_url` | string | no | Test keys only. An `https` URL that gets this job's webhooks instead of your test endpoint. |

```json
{
  "image_url": "https://docs.foodshot.ai/samples/dish.jpg",
  "style_id": "pure-shot",
  "aspect_ratio": "1:1",
  "metadata": {
    "dish_id": "d_456",
    "vendor_id": "v_1029"
  }
}
```

## Result sizes

Width × height in pixels, for each `aspect_ratio` and `resolution`.

| `aspect_ratio` | 2K | 4K (default) |
| --- | --- | --- |
| `1:1` | 2048 × 2048 | 4096 × 4096 |
| `2:3` | 1696 × 2528 | 3392 × 5056 |
| `3:2` | 2528 × 1696 | 5056 × 3392 |
| `3:4` | 1792 × 2400 | 3584 × 4800 |
| `4:3` | 2400 × 1792 | 4800 × 3584 |
| `4:5` | 1856 × 2304 | 3712 × 4608 |
| `5:4` | 2304 × 1856 | 4608 × 3712 |
| `9:16` | 1536 × 2752 | 3072 × 5504 |
| `16:9` | 2752 × 1536 | 5504 × 3072 |
| `21:9` | 3168 × 1344 | 6336 × 2688 |

## Responses

### 202

Accepted: the new generation, `queued`. A retry with the same `Idempotency-Key` and body returns the same generation, with `Idempotent-Replayed: true`, and isn't charged again.

```json
{
  "id": "gen_4Kq9xT2mVb7Zp1Ls8Hc3Rw",
  "object": "generation",
  "environment": "live",
  "status": "queued",
  "created_at": "2026-10-18T10:42:07.512Z",
  "started_at": null,
  "completed_at": null,
  "style_id": "pure-shot",
  "instruction": null,
  "aspect_ratio": "1:1",
  "resolution": "4k",
  "format": "jpeg",
  "metadata": {
    "dish_id": "d_456",
    "vendor_id": "v_1029"
  },
  "credits_charged": 1,
  "output": null,
  "error": null,
  "delivery": {
    "status": "none",
    "attempts": 0,
    "last_status_code": null,
    "last_attempt_at": null
  }
}
```

### 400

The request can't be accepted as sent. Nothing was charged.

```json
{
  "error": {
    "code": "invalid_request",
    "message": "The Idempotency-Key header is required. Send one unique value per photo, such as a UUID, and reuse it when you retry.",
    "param": "Idempotency-Key",
    "request_id": "req_daJCWaeLcd6uXjfMaJeplG"
  }
}
```

### 401

The key is missing, wrong or revoked.

```json
{
  "error": {
    "code": "invalid_api_key",
    "message": "The API key is missing, malformed, unknown or revoked.",
    "param": null,
    "request_id": "req_NPfzXU77n1f2QYM559fGL8"
  }
}
```

### 402

No live credits are left. Nothing was charged.

```json
{
  "error": {
    "code": "insufficient_credits",
    "message": "Your account has no credits left for live jobs.",
    "param": null,
    "request_id": "req_NPfzXU77n1f2QYM559fGL8"
  }
}
```

### 403

This key may not make this request.

```json
{
  "error": {
    "code": "permission_denied",
    "message": "Live keys work only from your server.",
    "param": null,
    "request_id": "req_NPfzXU77n1f2QYM559fGL8"
  }
}
```

### 409

This `Idempotency-Key` was used with a different body in the last 24 hours.

```json
{
  "error": {
    "code": "idempotency_conflict",
    "message": "This Idempotency-Key was used in the last 24 hours with a different request body. Resend the identical body, or use a new key for a new photo.",
    "param": "Idempotency-Key",
    "request_id": "req_UTyanAcKR2uQdufRHA9T5L",
    "generation_id": "gen_4Kq9xT2mVb7Zp1Ls8Hc3Rw"
  }
}
```

### 413

The photo or the request body is too large.

```json
{
  "error": {
    "code": "image_too_large",
    "message": "The photo is too large. Files can be up to 20 MB and 100 megapixels.",
    "param": "image_url",
    "request_id": "req_utTipsOfv9dKW2NR0nfFye"
  }
}
```

### 415

The body isn't JSON, or the photo isn't JPEG, PNG, WebP or HEIC.

```json
{
  "error": {
    "code": "unsupported_media_type",
    "message": "Send the request body as application/json.",
    "param": null,
    "request_id": "req_utTipsOfv9dKW2NR0nfFye"
  }
}
```

### 429

Over a limit, or busy. Retry after `Retry-After` seconds with the same `Idempotency-Key` and body.

```json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Retry after the number of seconds in Retry-After.",
    "param": null,
    "request_id": "req_BTBBPZA0oirxnw23MU0t7E"
  }
}
```

### 500

Something went wrong on our side. Retry; for a `POST`, with the same `Idempotency-Key` and body.

```json
{
  "error": {
    "code": "internal_error",
    "message": "Something went wrong on our side. Retry the request; for POST /v1/generations, use the same Idempotency-Key.",
    "param": null,
    "request_id": "req_8d4DxP5n53EgewaJFOqGyS"
  }
}
```

### 503

We can't accept new jobs right now. Retry after `Retry-After` seconds with the same `Idempotency-Key` and body.

```json
{
  "error": {
    "code": "service_unavailable",
    "message": "The service is temporarily unavailable. Retry after the number of seconds in Retry-After.",
    "param": null,
    "request_id": "req_8d4DxP5n53EgewaJFOqGyS"
  }
}
```

## Code samples

### curl

```bash
curl -sS https://api.foodshot.ai/v1/generations \
  --max-time 60 \
  -H "Authorization: Bearer $FOODSHOT_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 5f0c2b7e-9a4d-4c1b-8e3f-6d2a1b9c7e40' \
  -d '{
    "image_url": "https://docs.foodshot.ai/samples/dish.jpg",
    "style_id": "pure-shot",
    "aspect_ratio": "1:1",
    "metadata": { "dish_id": "d_456" }
  }'
```

### Node

```js
import { randomUUID } from 'node:crypto';

const idempotencyKey = randomUUID();
const res = await fetch('https://api.foodshot.ai/v1/generations', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.FOODSHOT_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': idempotencyKey,
  },
  body: JSON.stringify({
    image_url: 'https://docs.foodshot.ai/samples/dish.jpg',
    style_id: 'pure-shot',
    aspect_ratio: '1:1',
    metadata: { dish_id: 'd_456' },
  }),
  signal: AbortSignal.timeout(60_000),
});
const text = await res.text();
if (res.status !== 202) throw new Error(`FoodShot ${res.status}: ${text}`);
const generation = JSON.parse(text);
console.log(generation.id, generation.status); // gen_…, queued
```

### Python

```python
import os
import uuid

import requests

idempotency_key = str(uuid.uuid4())
response = requests.post(
    "https://api.foodshot.ai/v1/generations",
    headers={
        "Authorization": f"Bearer {os.environ['FOODSHOT_API_KEY']}",
        "Idempotency-Key": idempotency_key,
    },
    json={
        "image_url": "https://docs.foodshot.ai/samples/dish.jpg",
        "style_id": "pure-shot",
        "aspect_ratio": "1:1",
        "metadata": {"dish_id": "d_456"},
    },
    timeout=60,
)
if response.status_code != 202:
    raise RuntimeError(f"FoodShot {response.status_code}: {response.text}")
generation = response.json()
print(generation["id"], generation["status"])  # gen_…, queued
```
