# Credits and billing

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

Live API keys (`ss_live_…`) spend credits; test keys (`ss_test_…`) never do
([Test mode](/docs/test-mode/)).

## What things cost

| What | Credits |
|---|---|
| One generation (one image) with a live key | 1 |
| A screenshot set with a live key | 1 per image (screens × devices, plus the feature graphic) |
| Anything with a test key | 0 |
| Uploads, palettes, store requirements, webhooks, reads | 0 |

Your first API key comes with 4 free credits for live images (once per account).

- A credit is charged when the generation or set is created (`creditsCharged`), all at once for a
  set: without enough credits the request is `402 INSUFFICIENT_CREDITS`
  (`details.requiredCredits`, `details.availableCredits`, `details.topUpUrl`) and nothing is created.
- Quote a set first with `POST /screenshot-sets?dryRun=true`: it answers `credits` and whether your
  balance is `sufficient`, and charges nothing.
- A generation that fails, or that you cancel before it starts, is refunded automatically
  (`creditsRefunded`). Retrying failed images of a set charges only the images it retries.
- Credits are spent from your monthly plan first, then from trial credits, then from purchased
  packs. Plan credits reset every month (they do not roll over); purchased credits do not expire.

## Check the balance and usage

`GET /credits` and `GET /usage` need the `usage:read` scope. Usage covers live generations of the
whole account (every key and surface) per UTC day, per API key (`groupBy=key`) or per source
(`groupBy=source`), for a range of up to 366 days (`from` inclusive, `to` exclusive, `YYYY-MM-DD`).

<!-- tabs -->
```sh
curl -sS https://api.storeshot.art/v1/credits \
  -H "Authorization: Bearer $STORESHOT_API_KEY" | jq .

curl -sS 'https://api.storeshot.art/v1/usage?from=2026-10-01&to=2026-11-01&groupBy=key' \
  -H "Authorization: Bearer $STORESHOT_API_KEY" \
  | jq -r '.data[] | "\(.key) generations=\(.generations) credits=\(.creditsUsed)"'
```

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

const credits = await (await fetch(`${api}/credits`, { headers })).json();
console.log('credits left:', credits.total, credits.balances);

const usage = await (
  await fetch(`${api}/usage?from=2026-10-01&to=2026-11-01&groupBy=source`, { headers })
).json();
for (const bucket of usage.data) console.log(bucket.key, bucket.generations, bucket.creditsUsed);
```

```python
import os, requests

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

credits = requests.get(f"{api}/credits", headers=headers).json()
print("credits left:", credits["total"], credits["balances"])

usage = requests.get(
    f"{api}/usage", headers=headers, params={"from": "2026-10-01", "to": "2026-11-01"}
).json()
for day in usage["data"]:
    if day["generations"]:
        print(day["key"], day["generations"], day["creditsUsed"])
```

A [webhook](/docs/webhooks/) with the `credits.low` event tells you when a charge takes the live
balance below a threshold you choose.

## Monthly plans

| Plan | Price | Credits / month | Per credit | Requests / min (key · account) | Concurrent | Daily credits |
|---|---|---|---|---|---|---|
| Pro | $12 / month | 60 | $0.20 | 120 · 240 | 4 | 200 |
| Agency | $29 / month | 150 | $0.19 | 120 · 240 | 6 | 500 |
| Scale | $79 / month | 430 | $0.18 | 300 · 600 | 10 | 1000 |
| No plan | $0 | packs only | — | 60 · 120 | 3 | 100 |

## Credit packs

| Pack | Price | Per credit |
|---|---|---|
| 15 credits | $3.99 | $0.27 |
| 50 credits | $11.99 | $0.24 |

Buy a plan or a pack in the [developer console](https://console.storeshot.art/billing); the
[pricing page](/pricing/) compares them. Checkouts through the API (`POST /billing/checkout`) need
a live key with `billing:write` or a console session; test keys never buy.
