{"openapi":"3.1.0","info":{"title":"cheapaiapi","description":"OpenAI-compatible image-generation API.\n\n**Auth.** All `/v1/*` routes require a Bearer API key. Click the 🔒 **Authorize** button (top right) to paste your `sk_cheap_...` key — every Try-it-out call below will then carry it.\n\n**Endpoints.**\n- `POST /v1/images/generations` — create an image-generation job. Sync by default; pass `?async=true` to return immediately with `status=queued` and recover via `GET /v1/jobs/{id}`.\n- `GET /v1/jobs/{id}` — poll a queued / running job.\n- `GET /v1/models` — your available SKUs and per-call price.\n- `GET /v1/balance` — your current account balance.\n- `GET /v1/usage` — your spend over a window.\n\n**Image inputs.** The `image` field on `POST /v1/images/generations` accepts either public `https://` URLs OR `data:image/<mime>;base64,...` URIs (raw bytes inlined). You don't have to host the file yourself.\n\n**Latency.** Image generation often finishes in **20-60 seconds**, but some jobs can take several minutes. For production integrations, prefer `?async=true`, store the returned `job_id`, and poll `GET /v1/jobs/{id}` every 5-10 seconds. Sync calls are best-effort: they may return `status=running` with the same `job_id` while the background job continues.\n\n**Errors** are a closed taxonomy: `RateLimited`, `UpstreamDown`, `NoFunds`, `InvalidPrompt`, `Unauthorized`. Anything else is `UpstreamDown` and is safe to retry.","version":"0.1.0"},"paths":{"/v1/balance":{"get":{"tags":["v1"],"summary":"Get Balance","description":"Return ``{balance_usd, currency}`` for the authenticated account.\n\nDecimal serialised as a string so clients aren't forced into float math.","operationId":"get_balance_v1_balance_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Get Balance V1 Balance Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/usage":{"get":{"tags":["v1"],"summary":"Get Usage","description":"Return aggregate usage stats for the authenticated account.","operationId":"get_usage_v1_usage_get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO-8601 lower bound (inclusive). Defaults to 30 days ago.","title":"From"},"description":"ISO-8601 lower bound (inclusive). Defaults to 30 days ago."},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO-8601 upper bound (exclusive). Defaults to now.","title":"To"},"description":"ISO-8601 upper bound (exclusive). Defaults to now."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Usage V1 Usage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/jobs/{job_id}":{"get":{"tags":["v1"],"summary":"Get Job","description":"Return the public-visible projection of one job.","operationId":"get_job_v1_jobs__job_id__get","security":[{"HTTPBearer":[]}],"parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Job V1 Jobs  Job Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/models":{"get":{"tags":["v1"],"summary":"List Models","description":"Return ``{\"data\": [{id, price_per_call_usd, currency}, ...]}``.","operationId":"list_models_v1_models_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"items":{"additionalProperties":true,"type":"object"},"type":"array"},"type":"object","title":"Response List Models V1 Models Get"}}}}},"security":[{"HTTPBearer":[]}]}},"/v1/images/generations":{"post":{"tags":["v1"],"summary":"Generate","description":"Create one image-generation job. Sync by default; ``?async=true`` to skip wait.","operationId":"generate_v1_images_generations_post","security":[{"HTTPBearer":[]}],"parameters":[{"name":"async","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Async"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenerationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageGenerationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AspectRatio":{"type":"string","enum":["4:3","3:4","16:9","9:16","2:3","3:2","1:1","4:5","5:4","21:9"],"title":"AspectRatio","description":"Closed enum from spec § 2.5.1. Anything else → 422."},"ErrorEnvelope":{"properties":{"type":{"type":"string","title":"Type"},"code":{"type":"string","title":"Code"},"message":{"type":"string","title":"Message"},"param":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Param"}},"additionalProperties":false,"type":"object","required":["type","code","message"],"title":"ErrorEnvelope","description":"Error sub-object — same shape as the global error envelope (§ 2.5.5)."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ImageDataItem":{"properties":{"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"},"b64_json":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"B64 Json"},"revised_prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Revised Prompt"}},"additionalProperties":false,"type":"object","title":"ImageDataItem","description":"One element in the ``data`` array of a successful response."},"ImageGenerationRequest":{"properties":{"model":{"type":"string","maxLength":128,"minLength":1,"title":"Model","description":"SKU from `GET /v1/models`, e.g. `nano-banana-pro-2k`."},"prompt":{"type":"string","maxLength":4096,"minLength":1,"title":"Prompt","description":"Generation or edit instruction. Up to 4096 chars."},"response_format":{"type":"string","enum":["url","b64_json"],"title":"Response Format","description":"`url` — return a hosted image URL (default, cheapest); `b64_json` — return the image inline as base64.","default":"url"},"aspect_ratio":{"anyOf":[{"$ref":"#/components/schemas/AspectRatio"},{"type":"null"}],"description":"Optional aspect ratio. Must be one of the closed enum."},"image":{"items":{"type":"string"},"type":"array","title":"Image","description":"Reference images for img2img / multi-ref edit. Each entry is either a public `https://` URL OR a `data:image/<mime>;base64,...` URI. Empty list = pure text-to-image. Max ~10 items."},"n":{"type":"integer","maximum":1.0,"minimum":1.0,"title":"N","default":1},"extra":{"additionalProperties":true,"type":"object","title":"Extra","description":"Advanced generation options. Routing controls are ignored."},"_meta":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Meta","description":"Caller-side debug context (bot_request_id, flow_type, resolve_errors, etc). Persisted for internal cross-system tracing; never sent to the image-generation service."}},"additionalProperties":false,"type":"object","required":["model","prompt"],"title":"ImageGenerationRequest","description":"``POST /v1/images/generations`` body.\n\nField semantics:\n  * ``model`` — our ``our_model`` key; must match an active ``model_routes`` row.\n  * ``prompt`` — required, non-empty.\n  * ``response_format`` — default ``\"url\"`` (cheapest, no base64 expansion).\n  * ``aspect_ratio`` — optional; if provided must be in :class:`AspectRatio`.\n  * ``image`` — optional list of reference images for img2img / multi-ref edit.\n    Each entry can be either:\n      - a public ``https://`` URL (the image service fetches it), or\n      - a ``data:image/<mime>;base64,<...>`` URI (pass-through).\n    Mix-and-match is fine; cap is 10 entries / ~6 MB total per the\n    upstream spec.\n  * ``n`` — accepted for OpenAI compat but pinned to 1; we charge per call.\n  * ``extra`` — escape hatch for advanced image-generation options.\n\nLatency: image jobs often finish in 20-60 s, but some may take several\nminutes. Production clients should prefer ``?async=true``, store the\nreturned job id, and poll ``GET /v1/jobs/{id}`` every 5-10 s instead of\nholding one long HTTP connection open."},"ImageGenerationResponse":{"properties":{"id":{"type":"string","title":"Id"},"created":{"type":"integer","title":"Created"},"status":{"type":"string","enum":["queued","running","done","failed"],"title":"Status"},"data":{"items":{"$ref":"#/components/schemas/ImageDataItem"},"type":"array","title":"Data"},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["id","created","status"],"title":"ImageGenerationResponse","description":"``POST /v1/images/generations`` body, both happy + ``running`` paths.\n\nOn ``status=\"done\"``: ``data`` is populated, ``error`` is null.\nOn ``status=\"running\"``: ``data=[]``, client polls ``GET /v1/jobs/{id}``.\nOn ``status=\"queued\"``: same as running but for ``?async=true`` returns.\nOn ``status=\"failed\"``: ``data=[]``, ``error`` populated."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}},"securitySchemes":{"HTTPBearer":{"type":"http","description":"Your `sk_cheap_...` API key issued via `cheap-admin keys issue`.","scheme":"bearer","bearerFormat":"API key (sk_cheap_...)"}}}}