Image generation API: what changes when you move from a UI to code
Most people arrive here from a web UI where you type a prompt and wait. Four things change when the same models are behind an API.
1. You get a job, not an image
{ "job_id": "abc123", "state": "queued", "credits_held": 4 }
Poll it, stream it, or receive a webhook. The upside is that nothing has to
stay connected — the downside is that a failed job arrives as a successful
HTTP response describing a failure, and your error handling will not catch it
unless you check state.
2. Billing is per call, not per month
| Model | Credits per image | Batch | Resolution |
|---|---|---|---|
| Flux | 5 | up to 4 | 480p, 720p, 1080p |
| Nano Banana | 4 | up to 4 | 480p, 720p, 1080p |
| Ideogram | 6 | up to 4 | 480p, 720p, 1080p |
No subscription tier gates which model you can use. You hold a credit balance and each call draws down from it; failed jobs refund in full.
3. There are ceilings, and they are per key
| Tier | Requests per minute | Concurrent jobs |
|---|---|---|
| Free | 5 | 1 |
| Pro | 60 | 5 |
One in-flight job on the free tier. Exceeding either returns a 429 — and the
two 429s need opposite responses, so branch on error.code rather than the
status. See api rate limit exceeded and
CONCURRENCY_LIMIT_EXCEEDED.
4. Parameters are validated locally
A value outside a model’s schema fails before anything is submitted, with the
offending field named in error.param. No credits are touched. Each model has
a different schema — see
AI image API for the per-model references.
The minimal working call
curl -X POST https://genace.ai/api/v1/images/generations \
-H "Authorization: Bearer $GENACE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"a red ceramic mug on a white table"}'
No model needed — it defaults to Nano Banana at
4 credits
($0.04 at the Enterprise rate).
Models covered on this page
Where these facts come from
- codebase: src/ai/providers/*.ts — every pricing() and paramsSchema
- codebase: src/ai/api/quota.ts — per-tier ceilings