API reference
Full parameters, response and billing for POST /api/v1/generate
Photopurr exposes one synchronous generation endpoint: send a request, wait for the run to finish, get image URLs back in a single response. Built for scripts and server-side calls. First time here? Start with the quick start.
POST /api/v1/generate
Authorization: Bearer sk_your_key
Content-Type: application/jsonRequires the generate:write scope. A single request can run for up to 5 minutes. Four 4K images can take a minute or two, so don't set your client timeout too low.
Request parameters
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | one of the two | Prompt, up to 4000 characters |
ref_image_ids | string[] | one of the two | Reference image ids, see reference images |
model | string | no | Default auto, see models |
ratio | string | no | 1:1 / 3:4 (default) / 4:5 / 16:9 / 9:16 |
resolution | string | no | 1K / 2K / 4K. Omit to use the model's default |
quality | string | no | low / medium (default) / high, GPT models only |
n | number | no | 1 (default) / 2 / 4 |
session_id | string | no | Which session to file this under, see sessions |
purpose_id | string | no | Purpose, see purposes |
At least one of prompt and ref_image_ids is required. Both together is fine.
Bad parameters are never silently ignored. An unknown model, a ratio the model doesn't support, or more reference images than the limit all return 400. We never swap in a default and report success.
Models
model | Ratios | Resolution | Quality | Reference limit | Credits per image | Status |
|---|---|---|---|---|---|---|
auto | — | — | — | — | Per the model actually chosen | Default |
nano-banana-2 | All | 1K / 2K / 4K, default 2K | None | 14 | 12 / 16 / 23 | Stable |
gpt-image-2 | All | 1K / 2K / 4K, default 1K | low / medium / high | 16 | By quality 5 / 15 / 50, +1 per reference image | Stable |
gpt-image-2.5 | All | Same as above | Same as above | 16 | Same as above | Testing |
seedream-5-0-pro | No 4:5 | 1K / 2K, default 1K | None | 10 | 6 / 12 | Testing |
qwen-image-3.0-pro | No 4:5 | 1K / 2K, default 1K | None | 3 | 6 / 11 | Testing |
flux-kontext-pro | All | 1K | None | 1 | 5 | Edit only |
How auto picks: nano-banana-2 by default; gpt-image-2 when the prompt asks for text in the image (brand name, slogan, price); another model that fits when the reference count exceeds the preferred model's limit. The response has no field telling you which model Auto chose. Pass model explicitly when you need to know.
On GPT models an omitted quality is billed as medium (15 credits). Other models ignore quality.
Purposes
purpose_id decides which set of e-commerce photography rules is added on top of your prompt. Omitting it means free.
purpose_id | Meaning |
|---|---|
free | Free generation, prompt only |
main_white | Marketplace hero image |
detail | Detail shot |
buyer_show | Buyer show |
pure_white | White background, no person |
Reference images
ref_image_ids takes image ids. Today the API can obtain ids from exactly one source: images[].id returned by earlier calls to this endpoint. Images uploaded on the website have no externally visible id yet, and there is no upload endpoint.
Ids must belong to the current account. If an image has been deleted or cleaned up you get 400 reference_image_missing; it is never skipped silently. Retention rules are on the image download page.
Sessions
Every generation belongs to a session. Omit session_id and a new one is created, titled "API generation", visible under "Recent conversations" in the website sidebar. Pass the session_id from a previous response to group several generations into one session so you can review them together on the website.
A session_id that doesn't exist or isn't yours returns 404 session_not_found.
Response
{
"ok": true,
"turn_id": "t_xxxxxxxxxx",
"session_id": "s_xxxxxxxxxx",
"credits_charged": 16,
"duration_ms": 18320,
"warnings": [],
"images": [
{
"id": "img_xxxxxxxx",
"filename": "xxxxxxxx.png",
"url": "/api/file/xxxxxxxx.png",
"width": 1536,
"height": 2048
}
]
}| Field | Description |
|---|---|
turn_id | Id of this generation |
session_id | The session it belongs to; pass it back next time |
credits_charged | Credits actually charged. On partial success this is already net of the refund |
duration_ms | Generation time |
warnings | Parts of your request the vendor could not honor, see below |
images[].id | Image id, usable as ref_image_ids next time |
images[].url | Relative path; request it with the same key, see image download |
images[].width / height | Actual pixels |
The response carries an X-Request-Id header. Include it when reporting a problem.
warnings
An empty array means the images match your request exactly. A non-empty array deserves a look. It lists what you asked for that the vendor could not deliver:
{ "type": "downgraded", "feature": "resolution", "details": "Upstream returned only 1024×1365. Upscaled locally 1.50x to 1536×2048, so the detail is not native." }| Field | Description |
|---|---|
type | unsupported: the vendor doesn't support it at all; downgraded: supported, but not at the spec you asked for |
feature | Which one: ratio / resolution / quality / n and so on |
details | Human-readable explanation. It follows the language: the website uses the interface language, the API uses Accept-Language, and the default is Chinese |
Asking for 4 and getting 2 also shows up here (feature: "n"), billed for 2.
Billing
- Once validation passes, credits are reserved (estimated from
n), then the model is called - Whole run failed: full refund, endpoint returns
500 - Partial success: billed for the actual count, difference refunded automatically,
credits_chargedis the net figure - Insufficient balance:
402 insufficient_credits, nothing reserved,messagestates how much is needed and how to top up
Accounts younger than 7 days can spend at most 2000 credits in any 24 hours. Past that you get 429 new_user_daily_limit.
Errors
Every error uses the same body:
{
"error": {
"code": "invalid_model",
"message": "Unsupported model.",
"request_id": "req_xxxxxxxxxxxxxxxxxxxx"
}
}Common ones on this endpoint:
| Status | code | Meaning |
|---|---|---|
400 | invalid_request and per-parameter sub-codes | Invalid parameters, full list under error format |
401 | unauthorized / invalid_api_key | No key / key invalid, revoked or expired |
402 | insufficient_credits | Not enough credits |
403 | insufficient_scope | Key lacks generate:write |
404 | session_not_found | session_id doesn't exist or isn't yours |
429 | rate_limited / new_user_daily_limit | Key over its rate limit / new account over its daily spend cap |
500 | internal_error | Generation failed, credits refunded. Report it with the request_id |
Full example
curl -X POST https://paitumao.com/api/v1/generate \
-H "Authorization: Bearer sk_your_key" \
-H "Content-Type: application/json" \
-d '{
"prompt": "White-background T-shirt hero shot, model facing front, soft lighting",
"model": "nano-banana-2",
"ratio": "3:4",
"resolution": "2K",
"n": 2,
"purpose_id": "main_white"
}'This reserves 32 credits (16 per 2K image, two images).
Photopurr Docs