# Quickstart

Source: https://docs.foodshot.ai/quickstart

> Your first generation with a test key, in three steps.

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

Test keys never use credits: they run the whole flow and return a sample image. Run the code on your server or your own machine, not in a browser.

### Get a test key

Your test key comes with your dashboard invitation. To ask for access, email [hello@foodshot.ai](mailto:hello@foodshot.ai).

Store the key as an environment variable:

```bash
export FOODSHOT_API_KEY="fs_test_..."
```

### Create a generation

Send a photo and a style. This code sends our sample dish photo: replace it with your photo's `https` URL. The `Idempotency-Key` is one new UUID per photo.

In Node or Python, save the code as `quickstart.mjs` or `quickstart.py`; step 3 adds to the same file.

**curl**

```bash
curl https://api.foodshot.ai/v1/generations \
  -H "Authorization: Bearer $FOODSHOT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "image_url": "https://docs.foodshot.ai/samples/dish.jpg",
    "style_id": "pure-shot"
  }'
```

**Node**

```js
const API = 'https://api.foodshot.ai';
const headers = { Authorization: `Bearer ${process.env.FOODSHOT_API_KEY}` };

const res = await fetch(`${API}/v1/generations`, {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json', 'Idempotency-Key': crypto.randomUUID() },
  body: JSON.stringify({
    image_url: 'https://docs.foodshot.ai/samples/dish.jpg', // your photo's https URL
    style_id: 'pure-shot', // any id from GET /v1/styles
  }),
});
let generation = await res.json();
if (!res.ok) throw new Error(generation.error.message);
console.log(generation.id, generation.status); // gen_... queued
```

**Python**

```python
import os
import time
import uuid

import requests

API = "https://api.foodshot.ai"
HEADERS = {"Authorization": f"Bearer {os.environ['FOODSHOT_API_KEY']}"}

res = requests.post(
    f"{API}/v1/generations",
    headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
    json={
        "image_url": "https://docs.foodshot.ai/samples/dish.jpg",  # your photo's https URL
        "style_id": "pure-shot",  # any id from GET /v1/styles
    },
    timeout=60,
)
res.raise_for_status()
generation = res.json()
print(generation["id"], generation["status"])  # gen_... queued
```

The answer is `202 Accepted` with the new generation (shortened here):

```json
{
  "id": "gen_4Kq9xT2mVb7Zp1Ls8Hc3Rw",
  "object": "generation",
  "status": "queued",
  "style_id": "pure-shot",
  "resolution": "4k",
  "output": null
}
```

### Get the result

Check the generation until its status is `succeeded` or `failed`, then download the file. In production, a [webhook](https://docs.foodshot.ai/guides/webhooks) tells you when a job is done, so you don't have to poll.

**curl**

```bash
# GENERATION_ID is the id from step 2. Repeat until "status" is "succeeded" or "failed".
curl https://api.foodshot.ai/v1/generations/$GENERATION_ID \
  -H "Authorization: Bearer $FOODSHOT_API_KEY"

# OUTPUT_URL is "output.url" from the answer. No key is needed.
curl -o result.jpg "$OUTPUT_URL"
```

**Node**

```js
import { writeFile } from 'node:fs/promises';
import { setTimeout as sleep } from 'node:timers/promises';

// Check every 5 seconds until the job is done.
while (generation.status === 'queued' || generation.status === 'processing') {
  await sleep(5000);
  generation = await (await fetch(`${API}/v1/generations/${generation.id}`, { headers })).json();
}
if (generation.status === 'failed') throw new Error(generation.error.code);

// Download the file and keep your own copy.
const file = await fetch(generation.output.url);
const ext = generation.output.content_type === 'image/png' ? 'png' : 'jpg';
await writeFile(`result.${ext}`, Buffer.from(await file.arrayBuffer()));
```

**Python**

```python
# Check every 5 seconds until the job is done.
while generation["status"] in ("queued", "processing"):
    time.sleep(5)
    generation = requests.get(f"{API}/v1/generations/{generation['id']}", headers=HEADERS, timeout=60).json()
if generation["status"] == "failed":
    raise RuntimeError(generation["error"]["code"])

# Download the file and keep your own copy.
output = generation["output"]
ext = "png" if output["content_type"] == "image/png" else "jpg"
with open(f"result.{ext}", "wb") as f:
    f.write(requests.get(output["url"], timeout=60).content)
```

A finished generation has an `output` (shortened here):

```json
{
  "id": "gen_4Kq9xT2mVb7Zp1Ls8Hc3Rw",
  "object": "generation",
  "environment": "test",
  "status": "succeeded",
  "resolution": "4k",
  "output": {
    "url": "https://api.foodshot.ai/v1/files/gen_4Kq9xT2mVb7Zp1Ls8Hc3Rw/image.jpg?exp=1792406595&sig=VT5DMYyFzJsUzoKdU9fob--vTZ1Nx5PFShO290vXCjc",
    "content_type": "image/jpeg",
    "width": 2048,
    "height": 2048
  }
}
```

The link works for 24 hours, and the file is deleted after 30 days, so keep your own copy.

With a test key, the result is a 2K sample image marked SAMPLE. With a live key, the same code returns your real result and uses 1 credit.
