Jobs
Monitor and retrieve results from image generation jobs.
/v1/jobs/{job_id}Get the status and results of a generation job.
Headers
Authorization: Bearer YOUR_API_KEY
Path Parameters
| Parameter | Type | Description |
|---|---|---|
job_id | string | The job ID returned from the generate endpoint. |
Example Request
curl "https://api.productai.photo/v1/jobs/job_abc123xyz" \ -H "Authorization: Bearer YOUR_API_KEY"
Response - Pending/Processing
{
"job_id": "job_abc123xyz",
"status": "processing",
"progress": 45,
"created_at": "2024-01-15T10:30:00Z",
"started_at": "2024-01-15T10:30:05Z",
"model": "nanobanana2",
"num_images": 2
}Response - Completed
{
"job_id": "job_abc123xyz",
"status": "completed",
"progress": 100,
"created_at": "2024-01-15T10:30:00Z",
"started_at": "2024-01-15T10:30:05Z",
"completed_at": "2024-01-15T10:30:35Z",
"model": "nanobanana2",
"num_images": 2,
"images": [
{
"url": "https://cdn.productai.photo/generated/img_001.jpg",
"width": 1024,
"height": 1024
},
{
"url": "https://cdn.productai.photo/generated/img_002.jpg",
"width": 1024,
"height": 1024
}
],
"tokens_used": 10
}Response - Failed
{
"job_id": "job_abc123xyz",
"status": "failed",
"created_at": "2024-01-15T10:30:00Z",
"started_at": "2024-01-15T10:30:05Z",
"failed_at": "2024-01-15T10:30:15Z",
"error": {
"code": "generation_failed",
"message": "Unable to process the input image"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
job_id | string | Unique job identifier. |
status | string | One of: pending, processing, completed, failed |
progress | integer | Progress percentage (0-100). |
images | array | Generated images (only present when completed). |
images[].url | string | CDN URL of the generated image. |
tokens_used | integer | Number of credits consumed. |
error | object | Error details (only present when failed). |
/v1/jobsList all jobs for the authenticated user.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 20 | Number of jobs to return. Max: 100. |
offset | integer | 0 | Number of jobs to skip. |
status | string | all | Filter by status. |
Example Request
curl "https://api.productai.photo/v1/jobs?limit=10&status=completed" \ -H "Authorization: Bearer YOUR_API_KEY"
Success Response (200 OK)
{
"jobs": [
{
"job_id": "job_abc123xyz",
"status": "completed",
"model": "nanobanana2",
"num_images": 2,
"created_at": "2024-01-15T10:30:00Z",
"completed_at": "2024-01-15T10:30:35Z"
}
],
"total": 156,
"limit": 10,
"offset": 0,
"has_more": true
}Polling Pattern
Poll the job status endpoint until the job completes. Recommended polling interval: 2-5 seconds.
Python
import requests
import time
def wait_for_job(job_id, api_key, timeout=120):
url = f"https://api.productai.photo/v1/jobs/{job_id}"
headers = {"Authorization": f"Bearer {api_key}"}
start = time.time()
while time.time() - start < timeout:
response = requests.get(url, headers=headers)
job = response.json()
if job["status"] == "completed":
return job["images"]
elif job["status"] == "failed":
raise Exception(job["error"]["message"])
time.sleep(3)
raise TimeoutError("Job did not complete in time")
# Usage
images = wait_for_job("job_abc123xyz", "YOUR_API_KEY")
for img in images:
print(img["url"])JavaScript
async function waitForJob(jobId, apiKey, timeout = 120000) {
const url = `https://api.productai.photo/v1/jobs/${jobId}`;
const start = Date.now();
while (Date.now() - start < timeout) {
const response = await fetch(url, {
headers: { "Authorization": `Bearer ${apiKey}` }
});
const job = await response.json();
if (job.status === "completed") return job.images;
if (job.status === "failed") throw new Error(job.error.message);
await new Promise(r => setTimeout(r, 3000));
}
throw new Error("Job timed out");
}
// Usage
const images = await waitForJob("job_abc123xyz", "YOUR_API_KEY");
images.forEach(img => console.log(img.url));Webhooks
Instead of polling, set a webhook URL on the API Access page. We'll POST the result of every job to that endpoint as it completes. Your endpoint should return 200 to acknowledge receipt.
Webhook Payload
POST https://your-server.com/webhook
Content-Type: application/json
{
"status": "success",
"image_url": "https://cdn.productai.photo/generated/img_001.jpg",
"job_id": "287344"
}status is success or error. On error, image_url is omitted.
Static source IPs
All webhook requests originate from two static addresses. If your firewall restricts inbound traffic, allowlist both — deliveries are load balanced across the pair, so any individual request may come from either one.
54.88.136.216 54.84.188.199
Verifying your endpoint
When you save a webhook URL we immediately send a test request from the same addresses, so it doubles as a check that your allowlist is correct.
POST https://your-server.com/webhook
User-Agent: ProductAI-Webhook-Validator/1.0
{
"test": true,
"message": "Webhook validation test from ProductAI"
}