# Quickstart

> HTML version: https://storeshot.art/docs/quickstart/

This guide takes one raw app screenshot and turns it into a 1290 × 2796 App Store image. It uses a
free test key, so nothing is charged while you try it.

## Before you start

- A raw screenshot of your app saved as `home.png` (a simulator or device capture; PNG, JPEG or WebP).
- One of: `curl` and `jq`, Node.js 18 or later, or Python 3 with the `requests` package.

## 1. Create a test key

Sign in to the [developer console](https://console.storeshot.art/), open **API keys** and create a
key with the **Test** environment. It starts with `ss_test_` and is shown once: copy it into your
environment as `STORESHOT_API_KEY`. Keep keys out of source control and out of apps you ship.

Test keys run the whole API (uploads, validation, the queue, statuses, idempotency and result URLs)
but never spend credits: each generation returns a sample image of exactly the requested size.

## 2. Upload a screenshot and generate an image

Pick your language. Each sample uploads `home.png` with `POST /uploads`, then creates one
generation with `POST /generations` and waits up to 60 seconds for it to finish.

<!-- tabs -->
```sh
# STORESHOT_API_KEY holds your key (an ss_test_… key is free while you integrate)

# 1. Upload a raw screenshot
UPLOAD=$(curl -sS -H "Authorization: Bearer $STORESHOT_API_KEY" \
  -F file=@home.png https://api.storeshot.art/v1/uploads | jq -r .id)

# 2. Generate a 1290×2796 App Store image (waits up to 60 s)
curl -sS https://api.storeshot.art/v1/generations \
  -H "Authorization: Bearer $STORESHOT_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: home-en-1' -H 'Prefer: wait=60' \
  -d '{"inputs":[{"uploadId":"'$UPLOAD'"}],"store":"app_store",
       "device":"app_store_1290x2796","app":{"name":"Trailmate"}}' \
  | jq '{status, url: .result.image.url}'
```

```js
import { readFile } from 'node:fs/promises';

const api = 'https://api.storeshot.art/v1';
const headers = { Authorization: `Bearer ${process.env.STORESHOT_API_KEY}` };

const form = new FormData();
form.append('file', new Blob([await readFile('home.png')]), 'home.png');
const upload = await (await fetch(`${api}/uploads`, { method: 'POST', headers, body: form })).json();

const res = await fetch(`${api}/generations`, {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json',
    'Idempotency-Key': 'home-en-1', Prefer: 'wait=60' },
  body: JSON.stringify({
    inputs: [{ uploadId: upload.id }],
    store: 'app_store',
    device: 'app_store_1290x2796',
    app: { name: 'Trailmate' },
  }),
});
const generation = await res.json();
console.log(generation.status, generation.result?.image.url);
```

```python
import os, requests

api = "https://api.storeshot.art/v1"
h = {"Authorization": f"Bearer {os.environ['STORESHOT_API_KEY']}"}

upload = requests.post(f"{api}/uploads", headers=h,
                       files={"file": open("home.png", "rb")}).json()

generation = requests.post(
    f"{api}/generations",
    headers={**h, "Idempotency-Key": "home-en-1", "Prefer": "wait=60"},
    json={"inputs": [{"uploadId": upload["id"]}], "store": "app_store",
          "device": "app_store_1290x2796", "app": {"name": "Trailmate"}},
).json()
print(generation["status"], (generation["result"] or {}).get("image", {}).get("url"))
```

## 3. Read the result

`POST /generations` answers `200` with the finished generation, or `202` when it is still running
after the wait. In that case poll `GET /generations/{id}` every few seconds until `status` is
`succeeded`, `failed` or `canceled`.

- `status: succeeded`: `result.image.url` is the PNG to download. The URL is signed and expires
  after 15 minutes; get the generation again for a fresh one.
- `status: failed`: `error.code` says why (for example `GENERATION_FAILED`), and a live key's
  credit is refunded automatically.

The `Idempotency-Key` header makes the request safe to retry: the same key and body return the
original generation instead of creating (or charging) a second one.

## 4. Go live

Create a key with the **Live** environment (`ss_live_…`) and use it in place of the test key. Live
generations cost credits: your first API key comes with 4 free credits, and the
[pricing page](/pricing/) lists plans and credit packs, which you buy in the
[console](https://console.storeshot.art/billing). Without enough credits a request answers
`402 INSUFFICIENT_CREDITS` and nothing is created.

## Next steps

- [Authentication](/docs/authentication/): scopes, creating and revoking keys, request IDs.
- [Generations](/docs/generations/): every option, polling, idempotent retries, cancel and delete.
- [Screenshot sets](/docs/screenshot-sets/): a whole store listing in one call, as a ZIP in
  fastlane folders.
- [Uploads](/docs/uploads/): URL imports and presigned uploads.
- [Webhooks](/docs/webhooks/): signed callbacks when a generation or set finishes.
- [API reference](/docs/api/): every operation, field and error code.
