PicassoIA Image Editor Pro uses the model ID picassoia/picassoia-image-editor-pro. AI image editing with up to 4 input images, 1 or 2 images per request and 7 aspect ratios.
Calling the API
Send requests to https://api.picassoia.com/v1/models/picassoia/picassoia-image-editor-pro/predictions with a JSON body whose input matches the parameters below.
Setup
Keep your API key on your server, in an environment variable, and send it in the Authorization header of every request.
export PICASSOIA_API_KEY="pia_sk_…"Request Parameters
promptstringRequired- DefaultNone
- What the output should show. Up to 4000 characters.
imagesarrayRequired- DefaultNone
- The images to edit or combine. Refer to them in the prompt as image 1, image 2 and so on. 1 to 4 images. A PNG, JPEG or WebP image, as a public https URL or as a data URL of up to 5 MB.
aspect_ratiostring- Default
"match_input_image" - The shape of the output. Where match_input_image is an option, it keeps the proportions of the input image, or of the first one when there are several. Options: match_input_image, 1:1, 16:9, 9:16, 4:3, 3:4, 3:2 and 2:3.
num_outputsinteger- Default
1 - How many results to make. From 1 to 2.
output_formatstring- Default
"webp" - The file format of the result. Options: webp, jpg and png.
output_qualityinteger- Default
95 - Compression quality, for JPG and WebP. From 0 to 100.
seedinteger- DefaultNone
- Set it to reproduce a result; leave it out for a random one.
Modes
The mode depends on which files, if any, you send with the prompt. Every mode uses the same endpoint.
Example Request
Image Editing
curl -s -X POST https://api.picassoia.com/v1/models/picassoia/picassoia-image-editor-pro/predictions \
-H "Authorization: Bearer $PICASSOIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":{"prompt":"put the cat from image 1 on the sofa from image 2","images":["https://example.com/cat.png","https://example.com/sofa.jpg"]}}'const API = 'https://api.picassoia.com/v1'
const headers = {
Authorization: `Bearer ${process.env.PICASSOIA_API_KEY}`,
'Content-Type': 'application/json',
}
const sleep = (s) => new Promise((resolve) => setTimeout(resolve, s * 1000))
const input = {
prompt: "put the cat from image 1 on the sofa from image 2",
images: [
"https://example.com/cat.png",
"https://example.com/sofa.jpg"
]
}
const created = await fetch(`${API}/models/picassoia/picassoia-image-editor-pro/predictions`, {
method: 'POST',
headers,
body: JSON.stringify({ input }),
})
let prediction = await created.json()
if (!created.ok) throw new Error(`${prediction.code}: ${prediction.detail}`)
while (!['succeeded', 'failed', 'canceled'].includes(prediction.status)) {
await sleep(prediction.eta?.next_poll_in_seconds ?? 2)
prediction = await (await fetch(prediction.urls.get, { headers })).json()
}
if (prediction.status !== 'succeeded') throw new Error(prediction.error ?? prediction.status)
console.log(prediction.output)import os, time, requests
API = "https://api.picassoia.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PICASSOIA_API_KEY']}"}
payload = {"input": {
"prompt": "put the cat from image 1 on the sofa from image 2",
"images": [
"https://example.com/cat.png",
"https://example.com/sofa.jpg",
],
}}
response = requests.post(f"{API}/models/picassoia/picassoia-image-editor-pro/predictions", json=payload, headers=HEADERS)
prediction = response.json()
if not response.ok:
raise RuntimeError(f"{prediction['code']}: {prediction['detail']}")
while prediction["status"] not in ("succeeded", "failed", "canceled"):
time.sleep((prediction.get("eta") or {}).get("next_poll_in_seconds", 2))
prediction = requests.get(prediction["urls"]["get"], headers=HEADERS).json()
if prediction["status"] != "succeeded":
raise RuntimeError(prediction["error"] or prediction["status"])
print(prediction["output"])Authentication
Every request needs an API key in the Authorization header, as a Bearer token. Create keys in your account; the full key is shown once.
Authorization: Bearer $PICASSOIA_API_KEYQueue and Polling
Every prediction takes three steps: create it, poll it until it finishes, then read the result.
Submit
The API answers 201, usually within seconds, with the prediction in the starting state, an estimate in eta and the URL to poll in urls.get. If the prediction fails right away, for example when the safety filter refuses the input, the answer is still 201, with the prediction in the failed state and its error.
curl -s -X POST https://api.picassoia.com/v1/models/picassoia/picassoia-image-editor-pro/predictions \
-H "Authorization: Bearer $PICASSOIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":{"prompt":"put the cat from image 1 on the sofa from image 2","images":["https://example.com/cat.png","https://example.com/sofa.jpg"]}}'const API = 'https://api.picassoia.com/v1'
const headers = {
Authorization: `Bearer ${process.env.PICASSOIA_API_KEY}`,
'Content-Type': 'application/json',
}
const input = {
prompt: "put the cat from image 1 on the sofa from image 2",
images: [
"https://example.com/cat.png",
"https://example.com/sofa.jpg"
]
}
const created = await fetch(`${API}/models/picassoia/picassoia-image-editor-pro/predictions`, {
method: 'POST',
headers,
body: JSON.stringify({ input }),
})
let prediction = await created.json()
if (!created.ok) throw new Error(`${prediction.code}: ${prediction.detail}`)
console.log(prediction.id, prediction.status) // api_…, startingimport os, requests
API = "https://api.picassoia.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PICASSOIA_API_KEY']}"}
payload = {"input": {
"prompt": "put the cat from image 1 on the sofa from image 2",
"images": [
"https://example.com/cat.png",
"https://example.com/sofa.jpg",
],
}}
response = requests.post(f"{API}/models/picassoia/picassoia-image-editor-pro/predictions", json=payload, headers=HEADERS)
prediction = response.json()
if not response.ok:
raise RuntimeError(f"{prediction['code']}: {prediction['detail']}")
print(prediction["id"], prediction["status"]) # api_…, starting{
"id": "api_3f9c2b7e8d1a4c6f9e0b1a2c3d4e5f60",
"model": "picassoia/picassoia-image-editor-pro",
"input": {
"prompt": "put the cat from image 1 on the sofa from image 2",
"images": [
"https://example.com/cat.png",
"https://example.com/sofa.jpg"
],
"aspect_ratio": "match_input_image",
"num_outputs": 1,
"output_format": "webp",
"output_quality": 95
},
"output": null,
"error": null,
"status": "starting",
"created_at": "2026-10-01T09:30:00.000Z",
"started_at": null,
"completed_at": null,
"metrics": {
"predict_time": null
},
"urls": {
"get": "https://api.picassoia.com/v1/predictions/api_3f9c2b7e8d1a4c6f9e0b1a2c3d4e5f60",
"cancel": "https://api.picassoia.com/v1/predictions/api_3f9c2b7e8d1a4c6f9e0b1a2c3d4e5f60/cancel"
},
"eta": {
"seconds": 12,
"next_poll_in_seconds": 6
}
}Poll
Poll urls.get again after eta.next_poll_in_seconds: at half the estimate, when the estimate is reached, then every 2 seconds.
# Repeat after eta.next_poll_in_seconds until status is
# succeeded, failed or canceled.
curl -s https://api.picassoia.com/v1/predictions/api_3f9c2b7e8d1a4c6f9e0b1a2c3d4e5f60 \
-H "Authorization: Bearer $PICASSOIA_API_KEY"const sleep = (s) => new Promise((resolve) => setTimeout(resolve, s * 1000))
while (!['succeeded', 'failed', 'canceled'].includes(prediction.status)) {
await sleep(prediction.eta?.next_poll_in_seconds ?? 2)
prediction = await (await fetch(prediction.urls.get, { headers })).json()
}import time
while prediction["status"] not in ("succeeded", "failed", "canceled"):
time.sleep((prediction.get("eta") or {}).get("next_poll_in_seconds", 2))
prediction = requests.get(prediction["urls"]["get"], headers=HEADERS).json(){
"id": "api_3f9c2b7e8d1a4c6f9e0b1a2c3d4e5f60",
"status": "processing",
"output": null,
"eta": {
"seconds": 6,
"next_poll_in_seconds": 6
}
}Result
When status is succeeded, output holds the result. A failed prediction carries its reason in error, and a canceled one was stopped before it finished. All three statuses are final.
# The last poll holds the result: "output" when "status" is succeeded,
# "error" when it is failed. With jq, just those three fields:
curl -s https://api.picassoia.com/v1/predictions/api_3f9c2b7e8d1a4c6f9e0b1a2c3d4e5f60 \
-H "Authorization: Bearer $PICASSOIA_API_KEY" | jq '.status, .output, .error'if (prediction.status !== 'succeeded') throw new Error(prediction.error ?? prediction.status)
console.log(prediction.output)if prediction["status"] != "succeeded":
raise RuntimeError(prediction["error"] or prediction["status"])
print(prediction["output"]){
"id": "api_3f9c2b7e8d1a4c6f9e0b1a2c3d4e5f60",
"status": "succeeded",
"output": [
"https://…/output-1.webp"
],
"error": null,
"completed_at": "2026-10-01T09:30:08.000Z",
"metrics": {
"predict_time": 6.4
},
"eta": null
}Cancel
POST urls.cancel stops a prediction that is still queued (any model) or an image that is still generating; a video already generating answers 409 not_cancelable.
The IDs, times and URLs in these responses are examples: yours will have their own.
Output
When the prediction succeeds, output is a list of 1 or 2 image URLs.
Real Outputs
Errors and Limits
Errors are JSON problem details with a stable code, and a 422 names every field at fault.
Limits
- 5 predictions queued or running at once per account
- Request body up to 10 MB; images as data URLs up to 5 MB each
- Prompt up to 4000 characters
- Unknown fields are refused with 422 invalid_input
Pricing
$0 per request with an active Infinite plan: API predictions use no credits, whatever the settings.
Other providers charge $0.03/MP for a comparable model (as of October 1, 2026).
API PricingJSON Schema
The same schema the API serves at GET /v1/models/picassoia/picassoia-image-editor-pro.
{
"input_schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"prompt": {
"type": "string",
"minLength": 1,
"maxLength": 4000,
"description": "The edit to make. Refer to the images as \"image 1\", \"image 2\"…"
},
"images": {
"minItems": 1,
"maxItems": 4,
"type": "array",
"items": {
"type": "string",
"description": "An image: an https URL or a data URL."
},
"description": "1 to 4 images to edit or combine; the first one is the main image."
},
"aspect_ratio": {
"default": "match_input_image",
"description": "Aspect ratio of the result; match_input_image keeps the first image's.",
"type": "string",
"enum": [
"match_input_image",
"1:1",
"16:9",
"9:16",
"4:3",
"3:4",
"3:2",
"2:3"
]
},
"num_outputs": {
"default": 1,
"description": "How many images to create: 1 or 2.",
"type": "integer",
"minimum": 1,
"maximum": 2
},
"output_format": {
"default": "webp",
"description": "File format of the images.",
"type": "string",
"enum": [
"webp",
"jpg",
"png"
]
},
"output_quality": {
"default": 95,
"description": "Quality of JPG and WebP images, from 0 to 100.",
"type": "integer",
"minimum": 0,
"maximum": 100
},
"seed": {
"description": "Random seed. Set it to reproduce a result.",
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"required": [
"prompt",
"images"
],
"additionalProperties": false
},
"output_schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"items": {
"type": "string",
"format": "uri"
},
"minItems": 1,
"maxItems": 2,
"description": "URLs of the edited images, in the requested output_format."
}
}