# Add CheaperGen to this project (instructions for coding agents)

You are a coding agent, and your developer wants this codebase to generate AI video or images through CheaperGen: one REST API for official models (Seedance, Kling, Veo, Wan, MiniMax, FLUX, Nano Banana and more), billed per second of video or per image from a prepaid balance. Add a small, idiomatic integration, prove it works without spending money, and tell the developer what to do next. Keep your messages short.

## Ground rules

- Read the codebase first and follow its language, framework, HTTP client, config and error-handling conventions. No SDK is needed: plain HTTPS + JSON. Don't add dependencies the project doesn't already use unless there is no reasonable alternative.
- The API key lives in the environment as `CHEAPERGEN_API_KEY`. Never hardcode, print, log or commit it. Add the variable (empty) to the project's env example file if it has one, and make sure real env files are git-ignored. The developer creates a key at https://cheapergen.com/dashboard/keys and sets it themselves — never ask them to paste a key into the chat.
- Don't spend the developer's balance without asking. Test with the free quote endpoint. Run a real generation only if they say so, at the cheapest settings, with `max_cost_usd` set.
- Keep the change scoped to what was asked, and list the files you changed.

## Facts

- Base URL: `https://cheapergen.com/api/v1`. JSON over HTTPS. Header: `Authorization: Bearer $CHEAPERGEN_API_KEY`.
- Models: `GET https://cheapergen.com/api/v1/models` (catalog with prices, no auth). Before writing code for a model, read its reference: `https://cheapergen.com/models/{vendor}/{model}/llms.txt` (every mode, field, allowed value and price, in Markdown) or `https://cheapergen.com/models/{vendor}/{model}/openapi.json`. A mode's endpoint id looks like `bytedance/seedream-5.0-lite/text-to-image` or `bytedance/seedance-2.0/text-to-video`.
- Flow:
  1. `POST https://cheapergen.com/api/v1/quote/{endpoint}` with the input — free, nothing runs: returns `amount_usd`, `list_usd` (with `list_price_source`: `official`, or `reference` named by `list_price_label`, e.g. fal API — don't call a reference official), `available`. Use it to test the key and to show a price.
  2. `POST https://cheapergen.com/api/v1/run/{endpoint}` with the input → a generation `{ id, status: "queued", … }`. Send an `Idempotency-Key` header (reuse it when retrying the same request). Optional body fields: `max_cost_usd` (refuse to run above it), `webhook_url`, `retention` (`immediate` | `1h` | `1d` | `7d` | `30d`; default `1d`).
  3. Poll `GET https://cheapergen.com/api/v1/generations/{id}` every 3–5 s until `status` is `succeeded`, `failed` or `canceled` (images take seconds, video usually 1–5 minutes), or pass `webhook_url` and verify the `CheaperGen-Signature` header with the code on https://cheapergen.com/docs/webhooks. The signing secret: `GET https://cheapergen.com/api/v1/webhooks/secret` with the API key returns `{ "key": "whsec_…" }` (store it like the key). Deduplicate on the `CheaperGen-Webhook-Id` header: a failed delivery is retried for up to an hour with the same id.
  4. Read `output.video.url` (video) or `output.images[].url` (images). Download or re-host outputs promptly: URLs expire with the retention.
- Media inputs (first frames, references, images to edit) are public https URLs; for local files, `POST https://cheapergen.com/api/v1/uploads` with `{ filename, content_type, size }` returns an upload URL: PUT exactly `size` bytes with Content-Length (no chunked/streaming body), or the storage answers 403.
- Errors are `{ "error": { "code", "message", "param"? } }`. Map them from https://cheapergen.com/docs/errors (every code and what to do). The ones to handle first: 402 `insufficient_balance` (the developer adds credits at https://cheapergen.com/dashboard/billing); 429, 502 and 503 are retried with backoff and the same `Idempotency-Key`. A failed generation is refunded automatically.
- Limits (higher limits on request: contact@cheapergen.com): bursts of 20 requests, then 10 per second; 20 generations at once per account (https://cheapergen.com/docs/rate-limits). Past that, a generation is still accepted and waits as `queued` in the account's queue, then starts by itself (first come, first served; waiting never times it out; while it waits, cancel or DELETE refunds it at once). Only a full queue (10 × the limit) returns 429 — so a batch job can submit freely and wait for webhooks. The account's own limits are in `GET /api/v1/account` → `limits` (`concurrent_generations`: how many run at once; `in_flight`: in progress now, queued ones included; `requests_per_second`, `burst`; `null` with `unlimited: true` when it has none). A 429 `rate_limited` says which limit was hit, with `Retry-After`.
- Every generation reports its `cost`. Prices are per second of video or per image, below the official list price.

## Steps

1. Find where generation belongs in this project (ask the developer if it isn't clear which feature or which model).
2. Add `CHEAPERGEN_API_KEY` to the config the way this project reads secrets.
3. Write a small client: `quote(endpoint, input)`, `run(endpoint, input, { idempotencyKey, maxCostUsd })`, `wait(id)` (poll with a timeout) or a webhook handler — with timeouts and the errors above mapped to the project's error handling.
4. Add a check the developer can run that only calls the quote endpoint (it costs nothing), and run it if the key is set.
5. Tell the developer: where to create the key, how to run the check, and what one real generation with their settings would cost (from the quote).

## Example: Seedream 5.0 Lite (text to image)

Quote (free) — the same request to `/quote/` instead of `/run/`:

```bash
curl -X POST https://cheapergen.com/api/v1/quote/bytedance/seedream-5.0-lite/text-to-image \
  -H "Authorization: Bearer $CHEAPERGEN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "prompt": "Editorial product photo of a matte-black espresso machine on travertine, soft window light, 85mm",
  "aspect_ratio": "1:1",
  "size": "2K"
}'
```

Run, then poll:

```javascript
const API = "https://cheapergen.com/api/v1";
const headers = {
  Authorization: `Bearer ${process.env.CHEAPERGEN_API_KEY}`,
  "Content-Type": "application/json",
};

// 1. Submit the job
let job = await fetch(`${API}/run/bytedance/seedream-5.0-lite/text-to-image`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    "prompt": "Editorial product photo of a matte-black espresso machine on travertine, soft window light, 85mm",
    "aspect_ratio": "1:1",
    "size": "2K"
  }),
}).then((r) => r.json());

// 2. Poll until it finishes (or pass webhook_url and skip polling)
while (job.status === "queued" || job.status === "running") {
  await new Promise((r) => setTimeout(r, 3000));
  job = await fetch(`${API}/generations/${job.id}`, { headers }).then((r) => r.json());
}

console.log(job.status === "succeeded" ? job.output.images[0].url : job.error);
```

```python
import os, time, requests

API = "https://cheapergen.com/api/v1"
headers = {"Authorization": f"Bearer {os.environ['CHEAPERGEN_API_KEY']}"}

# 1. Submit the job
job = requests.post(f"{API}/run/bytedance/seedream-5.0-lite/text-to-image", headers=headers, json={
    "prompt": "Editorial product photo of a matte-black espresso machine on travertine, soft window light, 85mm",
    "aspect_ratio": "1:1",
    "size": "2K"
}).json()

# 2. Poll until it finishes (or pass "webhook_url" and skip polling)
while job["status"] in ("queued", "running"):
    time.sleep(3)
    job = requests.get(f"{API}/generations/{job['id']}", headers=headers).json()

print(job["status"], job["output"]["images"][0]["url"] if job["status"] == "succeeded" else job["error"])
```

## Reference

This guide is a checklist; these pages are the reference and are always current. Read them when you need more detail.

- API reference: https://cheapergen.com/docs/api
- Each model: https://cheapergen.com/models/{vendor}/{model}/llms.txt (Markdown), `/openapi.json`, `/api` (page)
- Errors and retries: https://cheapergen.com/docs/errors
- Webhooks and signature verification: https://cheapergen.com/docs/webhooks
- Rate limits: https://cheapergen.com/docs/rate-limits
- Billing: https://cheapergen.com/docs/billing · All models: https://cheapergen.com/models
