Image generation and editing
primerouter exposes two OpenAI-compatible image endpoints:
| Endpoint | Use | Request format |
|---|---|---|
POST /v1/images/generations | Text to image | JSON |
POST /v1/images/edits | Image to image, multiple reference images | multipart/form-data |
Available image models and prices are on the pricing page, under "Image models". There are currently two families, gpt-image-2 and grok-imagine-image. The request shape is the same, but whether size takes effect and what comes back differ — see each model's section below.
Text to image
curl https://primerouter.ai/v1/images/generations \
-H "Authorization: Bearer $PRIMEROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "An orange cat on a windowsill in soft morning light",
"output_format": "png",
"response_format": "b64_json"
}'The image comes back in data[].b64_json as a base64 string.
Passing response_format=url returns a data: inline URL — not a downloadable http link.
Image editing and multiple reference images
Image editing goes through /v1/images/edits and must be sent as a form (multipart/form-data), not JSON.
For multiple reference images, repeat image[] — one per file:
curl https://primerouter.ai/v1/images/edits \
-H "Authorization: Bearer $PRIMEROUTER_API_KEY" \
-F model=gpt-image-2 \
-F "image[]=@layout.png" \
-F "image[]=@product.png" \
-F prompt="Place the product from the second image into the layout of the first. Keep all promotional text from the layout." \
-F output_format=pngThe OpenAI SDK works the same way:
from openai import OpenAI
client = OpenAI(base_url="https://primerouter.ai/v1", api_key="sk-...")
result = client.images.edit(
model="gpt-image-2",
image=[open("layout.png", "rb"), open("product.png", "rb")],
prompt="Place the product from the second image into the layout of the first. Keep all promotional text from the layout.",
)Notes:
image,image[], andimage[0]/image[1]are all accepted, and forwarded in the order you gave them- The gateway does not cap the number of reference images; the upstream does. A single request body is limited to 32MB by default
maskis optional (field namemask); only the first file is used- Other form fields (
background,output_format,output_compression,moderation, …) are forwarded as-is. On the JSON/v1/images/generationspath, only standard fields are forwarded
Spell out everything you want kept
Image editing redraws from the prompt — it is not a local edit on the original canvas. Elements in a reference image are not guaranteed to survive unless the prompt asks for them.
If you want the title, feature bullets, and badges from the first image kept, say so:
Place the product from the second image into the layout of the first.
Keep all promotional text from the layout.Without that second sentence, the result often keeps only the main title and drops the rest of the layout. This is not a reference image being ignored — both images reach the model; they just were not asked to be preserved.
gpt-image-2 size and quality limits
Read this first
gpt-image-2 currently cannot control output resolution. This is a limitation of the upstream image endpoint, not a matter of how you write the parameters.
What this means:
sizedoes not set the resolution. Whether you send512x512,1024x1024, or3840x2160, the output is roughly 1.57 megapixelssizeonly influences the aspect ratio in text-to-image, and only for a limited set (1:1, 3:2, 2:3, 3:4, 16:9). Anything outside that set — or a requested pixel count below ~0.69MP — falls back to 1:1- In image editing
sizehas essentially no effect; the model picks the shape from the prompt and the reference images qualityhas no effect at all.low/medium/highmake no difference; upstream normalizes it toauto- Asking for a resolution in the prompt does not work either
Measured output sizes:
Requested size | Actual output | Pixels |
|---|---|---|
| 512×512 | 1254×1254 | 1.57MP |
| 1024×1024 | 1254×1254 | 1.57MP |
| 600×800 | 1254×1254 | 1.57MP |
| 3840×2160 | 1672×941 | 1.57MP |
| 1536×1024 | 1536×1024 | 1.57MP |
If you need exact pixel dimensions, resize the result yourself.
Grok Imagine (grok-imagine-image)
grok-imagine-image uses the same two endpoints with the same request shape — just change model:
curl https://primerouter.ai/v1/images/generations \
-H "Authorization: Bearer $PRIMEROUTER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-image",
"prompt": "An orange cat on a windowsill in soft morning light",
"size": "2048x1152"
}'The biggest difference from gpt-image-2: size takes effect.
How the size is decided
Grok has only two resolutions, 1K and 2K — there is no 4K. The width and height in size are converted into "resolution + aspect ratio" before reaching the model:
- Long edge ≤ 1024 → 1K, otherwise → 2K. Grok has no 4K; requesting a 4K size still yields a 2K image
- The aspect ratio snaps to the nearest of: 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 2:1, 1:2, 19.5:9, 9:19.5, 20:9, 9:20
- The actual pixel dimensions are fixed per resolution and ratio — they will not exactly match the width and height you sent
- You can also send
size=1k/size=2kdirectly to set only the resolution and let the model pick the ratio - Without
size, the image is 2K
Measured:
Requested size | Text-to-image output | Image-edit output |
|---|---|---|
| (none) | 2K, ratio chosen by the model (measured 2496×1664) | 2K, follows the reference image (measured 2048×2048) |
| 1024×1024 | 1024×1024 | 1024×1024 |
| 2048×1152 | 2816×1584 | 2816×1584 |
| 3840×2160 | 2816×1584 | 2816×1584 |
quality has no effect on this model.
Response format
- Text-to-image:
response_formatdefaults tourl, and the URL is a directly downloadable https link (unlike thedata:inline URL fromgpt-image-2). It is a temporary address — save the image promptly.b64_jsonalso works - Image editing: always returns a link;
response_formathas no effect - The image may be JPEG or PNG
Only these parameters are honored
Text-to-image forwards model, prompt, n, size, and response_format; image editing adds image / image[] (up to 3 reference images). output_format, background, moderation and similar fields do nothing for Grok.
Billing
Billed by the tier of the requested size — see the tier table in the Billing section below; without size, 2K. Per-tier prices are on the pricing page.
Billing
Image models are billed by image count × size tier, not by tokens.
The tier is decided by the long edge of the output image:
| Long edge | Tier |
|---|---|
| ≤ 1024 | 1K |
| 1025 – 2048 | 2K |
| > 2048 | 4K |
Per-tier prices are on the pricing page.
Three things that are easy to get wrong:
- The tier comes from the image you actually got, not the size you asked for. Request 4K, receive a 2K image, pay 2K. Grok is the exception: it is billed by the tier of the requested
size(see above) - You pay for the images actually returned, not for the
nyou requested - Group multipliers are not applied. The configured price is the price
gpt-image-2 is always 2K
As covered above, gpt-image-2 always renders about 1.57 megapixels. Its long edge is therefore never smaller than 1254 (the square case), so it can never reach the 1K tier, and practically never reaches 4K.
gpt-image-2 is effectively always billed at 2K. Requesting a small size, or asking for a small image in the prompt, will not lower the price.
Troubleshooting
The admin Logs → Details view shows which tier a request was billed at:
大小 600x800, 请求品质 medium, 生成数量 1, 图片档位 2K(来源:输出尺寸,实际尺寸 1254x1254)大小(size) and请求品质(requested quality) are what you sent, not what upstream accepted实际尺寸(actual size) is decoded from the returned image bytes — billing uses this- A source of
默认(default) means neither the request nor the response yielded a size, and the request was billed at 2K
When a charge looks wrong, start with 图片档位 (tier) and 生成数量 (count).
