Generate images with trained models, popular public models, and reference images.
Image Generation
Use POST /images/generation to start image generation jobs. Generation is asynchronous: the trigger request returns a jobId, then you poll GET /jobs/{id} until the job has finished.
For the shared polling pattern, see Jobs.
Image generation requests send the selected model fields directly:
{
"modelID": "gpt-image-2",
"prompt": "<PROMPT>"
}Use POST /images/generation for both trained models and popular public image models. Use
POST /images/upscaling when you want to upscale an existing image.
Model choices
| Model choice | Use when | Required fields |
|---|---|---|
| Trained model ID | Generate identity-consistent images from your model. | modelID. Most workflows also include prompt, preset, or inputImageId. |
| Popular models like NanoBanana Pro, GPT Image 2, Seedream | Generate, edit, or recompose images with public IDs. | modelID, prompt. You can also include preset. |
| Existing image | Create a higher-resolution version of an image asset. | imageID with POST /images/upscaling. |
Generate with a trained model
Use a trained model ID from GET /models when you need identity consistency. Wait until the model
status is ready before starting generation.
Common fields:
| Field | Description |
|---|---|
modelID | Trained model ID returned by GET /models. |
prompt | Text prompt for the generated image. |
preset | Preset tag returned by GET /presets. |
inputImageId | Uploaded reference image ID for trained-model reference workflows. |
width | Output width in pixels. |
height | Output height in pixels. |
numImages | Number of images to generate. |
steps | Generation step count. |
seed | Seed for repeatable outputs. |
runType | Optional run mode. |
enhanceFace | Enable face enhancement for portrait workflows. |
styleStrength | Controls how strongly a reference image influences the result. |
const API_KEY = '<YOUR_API_KEY>'
const BASE_URL = 'https://developer.photogptai.com/api'
const headers = {
Authorization: `Bearer ${API_KEY}`,
'API-Version': '1',
}
const payload = {
modelID: '<MODEL_ID>',
prompt: 'professional studio headshot, charcoal blazer, soft key light',
width: 1024,
height: 1024,
numImages: 1,
steps: 30,
seed: 0,
enhanceFace: true,
}
const response = await fetch(`${BASE_URL}/images/generation`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})
if (!response.ok) {
throw new Error(await response.text())
}
const body = await response.json()
const jobId = body.result.jobId
console.log('Generation queued:', jobId)import requests
API_KEY = "<YOUR_API_KEY>"
BASE_URL = "https://developer.photogptai.com/api"
headers = {
"Authorization": f"Bearer {API_KEY}",
"API-Version": "1",
}
payload = {
"modelID": "<MODEL_ID>",
"prompt": "professional studio headshot, charcoal blazer, soft key light",
"width": 1024,
"height": 1024,
"numImages": 1,
"steps": 30,
"seed": 0,
"enhanceFace": True,
}
response = requests.post(f"{BASE_URL}/images/generation", headers=headers, json=payload)
response.raise_for_status()
job_id = response.json()["result"]["jobId"]
print("Generation queued:", job_id)Use an uploaded reference image
For trained-model reference workflows, upload the reference image first and pass the returned image
ID as inputImageId.
Use type: "auxiliaryType" for reference images. Use type: "modelInput" for model training images.
const referenceFile = document.querySelector('input[type="file"]').files?.[0]
if (!referenceFile) {
throw new Error('Select a reference image before uploading.')
}
const uploadData = {
modelID: '<MODEL_ID>',
type: 'auxiliaryType',
}
const formData = new FormData()
formData.append('file', referenceFile)
formData.append('data', JSON.stringify(uploadData))
const uploadResponse = await fetch(`${BASE_URL}/images/upload`, {
method: 'POST',
headers,
body: formData,
})
if (!uploadResponse.ok) {
throw new Error(await uploadResponse.text())
}
const uploadedImage = await uploadResponse.json()
const inputImageId = uploadedImage.result.id
const payload = {
modelID: '<MODEL_ID>',
prompt: 'cinematic portrait, warm window light',
inputImageId,
styleStrength: 0.35,
numImages: 1,
width: 1024,
height: 1024,
}
const response = await fetch(`${BASE_URL}/images/generation`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})
if (!response.ok) {
throw new Error(await response.text())
}
const body = await response.json()
const jobId = body.result.jobIdimport json
import requests
upload_data = {
"modelID": "<MODEL_ID>",
"type": "auxiliaryType",
}
with open("reference.jpg", "rb") as image_file:
files = {
"file": image_file,
"data": (None, json.dumps(upload_data), "application/json"),
}
upload_response = requests.post(
f"{BASE_URL}/images/upload",
headers=headers,
files=files,
)
upload_response.raise_for_status()
input_image_id = upload_response.json()["result"]["id"]
payload = {
"modelID": "<MODEL_ID>",
"prompt": "cinematic portrait, warm window light",
"inputImageId": input_image_id,
"styleStrength": 0.35,
"numImages": 1,
"width": 1024,
"height": 1024,
}
response = requests.post(f"{BASE_URL}/images/generation", headers=headers, json=payload)
response.raise_for_status()
job_id = response.json()["result"]["jobId"]Generate with popular image models
Use a public model ID when you want high-quality general image generation, image editing, product shots, scene recomposition, or reference-image workflows without training a model first. Popular choices include NanoBanana Pro, GPT Image 2, and Seedream.
These models use the same POST /images/generation endpoint as trained models, but the input rules
are different. The request body is matched against the model-family schema selected by modelID.
| Field | Supported values or behavior |
|---|---|
modelID | nanobanana, nanobanana-pro, nanobanana-2, seedream, seedream-4_5, seedream-5-lite, gpt-image-2. |
prompt | Required. Describe the image, edit, style, subject, and any constraints. |
preset | Optional preset tag returned by GET /presets. The preset will be applied to the image. |
aspectRatio | 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9. Defaults to 1:1. |
numImages | 1 to 4. |
referenceImageURLs | Public image URLs. Limits are model-specific and listed below. |
options | Send only the option group for the selected model: gemini, seedream, or openai. Do not mix option groups. |
NanoBanana
Use modelID: "nanobanana" for Gemini-backed image generation and editing.
| Setting | Values |
|---|---|
| Option group | options.gemini |
| Reference image limit | Up to 3 URLs in referenceImageURLs. |
imageSize | 1K, 2K, 4K. Defaults to 1K. |
safetyFilterLevel | OFF, BLOCK_NONE, BLOCK_ONLY_HIGH, BLOCK_MEDIUM_AND_ABOVE, BLOCK_LOW_AND_ABOVE. |
sequentialMode | disabled, sequential. Use sequential when multiple images should run one after another. |
NanoBanana Pro
Use modelID: "nanobanana-pro" when you want the higher-capability NanoBanana model for more
detailed prompts, sharper image edits, or larger reference sets.
| Setting | Values |
|---|---|
| Option group | options.gemini |
| Reference image limit | Up to 14 URLs in referenceImageURLs. |
imageSize | 1K, 2K, 4K. Defaults to 1K. |
safetyFilterLevel | OFF, BLOCK_NONE, BLOCK_ONLY_HIGH, BLOCK_MEDIUM_AND_ABOVE, BLOCK_LOW_AND_ABOVE. |
sequentialMode | disabled, sequential. Use sequential when multiple images should run one after another. |
NanoBanana 2
Use modelID: "nanobanana-2" for the newer NanoBanana family model when you need the same Gemini
option surface with support for larger reference sets.
| Setting | Values |
|---|---|
| Option group | options.gemini |
| Reference image limit | Up to 14 URLs in referenceImageURLs. |
imageSize | 1K, 2K, 4K. Defaults to 1K. |
safetyFilterLevel | OFF, BLOCK_NONE, BLOCK_ONLY_HIGH, BLOCK_MEDIUM_AND_ABOVE, BLOCK_LOW_AND_ABOVE. |
sequentialMode | disabled, sequential. Use sequential when multiple images should run one after another. |
Seedream
Use modelID: "seedream" for Seedream image generation with the widest Seedream image-size range
and fast optimization mode.
| Setting | Values |
|---|---|
| Option group | options.seedream |
| Reference image limit | Up to 10 URLs in referenceImageURLs. |
imageSize | 1K, 2K, 4K. |
optimizeMode | standard, fast. |
sequentialMode | auto, disabled. Forced to auto when numImages > 1. |
Seedream 4.5
Use modelID: "seedream-4_5" for Seedream 4.5 when you want higher-resolution outputs and do not
need fast optimization mode.
| Setting | Values |
|---|---|
| Option group | options.seedream |
| Reference image limit | Up to 10 URLs in referenceImageURLs. |
imageSize | 2K, 4K. |
optimizeMode | standard. |
sequentialMode | auto, disabled. Forced to auto when numImages > 1. |
Seedream 5 Lite
Use modelID: "seedream-5-lite" for lighter Seedream generation with 2K and 3K output sizes.
| Setting | Values |
|---|---|
| Option group | options.seedream |
| Reference image limit | Up to 10 URLs in referenceImageURLs. |
imageSize | 2K, 3K. |
optimizeMode | standard. |
sequentialMode | auto, disabled. Forced to auto when numImages > 1. |
GPT Image 2
Use modelID: "gpt-image-2" for OpenAI image generation and editing. It is a strong default for
product compositions, marketing assets, and prompt-following edits from reference images.
| Setting | Values or behavior |
|---|---|
| Option group | options.openai |
| Reference image limit | Up to 10 URLs in referenceImageURLs. |
imageSize | 1K, 2K, 4K. Combined with aspectRatio to resolve exact dimensions. |
quality | auto, high, medium, low. |
background | auto, opaque. Defaults to auto. |
moderation | auto, low. Defaults to auto. |
outputFormat | png, jpeg. Defaults to jpeg. |
outputCompression | 0 to 100. Only valid when outputFormat is jpeg. |
Resolved GPT Image 2 dimensions are constrained by the API to a maximum edge of 3840px, a maximum
3:1 aspect ratio, and dimensions that resolve to edge multiples of 16.
const payload = {
modelID: 'gpt-image-2',
prompt: 'minimal product photo of a matte black water bottle on marble',
preset: 'editorial',
aspectRatio: '1:1',
numImages: 1,
referenceImageURLs: ['https://example.com/reference-product.png'],
options: {
openai: {
imageSize: '1K',
quality: 'high',
outputFormat: 'jpeg',
},
},
}
const response = await fetch(`${BASE_URL}/images/generation`, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})
if (!response.ok) {
throw new Error(await response.text())
}
const body = await response.json()
const jobId = body.result.jobIdpayload = {
"modelID": "gpt-image-2",
"prompt": "minimal product photo of a matte black water bottle on marble",
"preset": "editorial",
"aspectRatio": "1:1",
"numImages": 1,
"referenceImageURLs": [
"https://example.com/reference-product.png",
],
"options": {
"openai": {
"imageSize": "1K",
"quality": "high",
"outputFormat": "jpeg",
},
},
}
response = requests.post(f"{BASE_URL}/images/generation", headers=headers, json=payload)
response.raise_for_status()
job_id = response.json()["result"]["jobId"]Poll the job
Poll GET /jobs/{id} with the jobId returned by POST /images/generation. The helper below is
the same pattern described in Jobs.
async function waitForJob(jobId) {
const runningStatuses = new Set(['created', 'queued', 'running'])
while (true) {
const response = await fetch(`${BASE_URL}/jobs/${jobId}`, {
headers,
})
if (!response.ok) {
throw new Error(await response.text())
}
const body = await response.json()
const job = body.result
const status = String(job.status ?? '').toLowerCase()
if (!runningStatuses.has(status)) {
return job
}
await new Promise((resolve) => setTimeout(resolve, 5000))
}
}
const job = await waitForJob(jobId)
const status = String(job.status ?? '').toLowerCase()
if (status === 'success') {
for (const image of job.images ?? []) {
console.log(image.id, image.url)
}
} else {
throw new Error(job.error ?? `Generation failed: ${job.status}`)
}import time
def wait_for_job(job_id: str):
running_statuses = {"created", "queued", "running"}
while True:
response = requests.get(f"{BASE_URL}/jobs/{job_id}", headers=headers)
response.raise_for_status()
job = response.json()["result"]
status = job.get("status", "").lower()
if status not in running_statuses:
return job
time.sleep(5)
job = wait_for_job(job_id)
if job.get("status", "").lower() == "success":
for image in job.get("images", []):
print(image["id"], image["url"])
else:
raise RuntimeError(job.get("error") or f"Generation failed: {job.get('status')}")Fetch generated images
After the job completes, generated images are returned in result.images. You can also inspect image metadata later:
const imageId = '<IMAGE_ID>'
const response = await fetch(`${BASE_URL}/images/${imageId}`, {
headers,
})
if (!response.ok) {
throw new Error(await response.text())
}
const body = await response.json()
const image = body.result
console.log(image.url)image_id = "<IMAGE_ID>"
response = requests.get(f"{BASE_URL}/images/{image_id}", headers=headers)
response.raise_for_status()
image = response.json()["result"]
print(image["url"])To list output images for a model:
Omit cursor or send an empty string for the first page. When hasMore is true,
send the returned nextCursor as cursor to fetch the next page. Keep
filters unchanged between pages; restart with an empty cursor when they change.
pageSize defaults to 20 and accepts values from 1 to 100.
const params = new URLSearchParams({
imageType: 'output',
cursor: '',
pageSize: '20',
})
const response = await fetch(`${BASE_URL}/models/<MODEL_ID>/images?${params}`, {
headers,
})
if (!response.ok) {
throw new Error(await response.text())
}
const body = await response.json()
const images = body.result
const nextCursor = body.hasMore ? body.nextCursor : nullresponse = requests.get(
f"{BASE_URL}/models/<MODEL_ID>/images",
headers=headers,
params={"imageType": "output", "cursor": "", "pageSize": 20},
)
response.raise_for_status()
body = response.json()
images = body["result"]
next_cursor = body["nextCursor"] if body["hasMore"] else None