Start Free

PicassoIA API reference

Run PicassoIA's image and video models from your own code with a simple HTTP API: create a prediction, poll it until it finishes and download the result.

  • Base URLhttps://api.picassoia.com/v1
  • Free with the Infinite plan
  • Up to 5 predictions in progress at once

The PicassoIA API lets you run PicassoIA's image and video models from your own code:

  1. You create a prediction for a model.
  2. You poll the prediction until it finishes.
  3. You read the output URLs.
  • Base URL: https://api.picassoia.com/v1
  • Format: JSON requests and responses, UTF-8.
  • Price: API predictions are currently free. They use no credits.
  • Who can use it: accounts on the Infinite plan.

Contents

Authentication

Every request needs an API key in the Authorization header:

Authorization: Bearer pia_sk_…
  • Creating a key. Create keys on picassoia.com, in your account's API keys section.
    • The full key is shown once, when you create it. Store it somewhere safe.
    • An account can hold 2 keys at a time. To make room, revoke one.
  • Revoking a key. A revoked key stops working at once.
  • Keep keys server-side. Anyone with a key can run predictions on your account, so never put one in a web page or a mobile app. The API sends no CORS headers, so browsers cannot call it directly.
  • Plan changes. Keys keep existing if your plan changes. Without an Infinite plan, creating predictions answers 403 plan_required. Reading, listing and canceling your predictions keeps working.

Quick start

1. Create a prediction:

bash
curl -s -X POST https://api.picassoia.com/v1/models/picassoia/picassoia-image/predictions \
  -H "Authorization: Bearer $PICASSOIA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input": {"prompt": "a lighthouse at sunset, oil painting", "aspect_ratio": "16:9"}}'

The answer (201 Created) is a prediction in the starting state:

json
{
  "id": "api_3f9c2b7e8d1a4c6f9e0b1a2c3d4e5f60",
  "model": "picassoia/picassoia-image",
  "input": {
    "prompt": "a lighthouse at sunset, oil painting",
    "aspect_ratio": "16:9",
    "num_outputs": 1,
    "output_format": "jpg",
    "output_quality": 80
  },
  "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 }
}

2. Poll it (wait eta.next_poll_in_seconds between calls) until status is succeeded, failed or canceled:

bash
curl -s https://api.picassoia.com/v1/predictions/api_3f9c2b7e8d1a4c6f9e0b1a2c3d4e5f60 \
  -H "Authorization: Bearer $PICASSOIA_API_KEY"

3. Read the output:

json
{
  "id": "api_3f9c2b7e8d1a4c6f9e0b1a2c3d4e5f60",
  "status": "succeeded",
  "output": ["https://…/lighthouse.jpg"],
  "metrics": { "predict_time": 6.4 },
  "eta": null
}

(Other fields are left out here.)

Node.js (fetch)

js
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))

async function run(model, input) {
  const created = await fetch(`${API}/models/${model}/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)
  return prediction.output
}

const video = await run('picassoia/seedance-2.5-lite', {
  prompt: 'a paper boat sailing down a rainy street',
  duration: 5,
})
console.log(video) // "https://…/video.mp4"

Python (requests)

python
import os, time, requests

API = "https://api.picassoia.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PICASSOIA_API_KEY']}"}

def run(model, input):
    response = requests.post(f"{API}/models/{model}/predictions", json={"input": input}, 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"])
    return prediction["output"]

images = run("picassoia/picassoia-image-editor-pro", {
    "prompt": "put the cat from image 1 on the sofa from image 2",
    "images": ["https://example.com/cat.png", "https://example.com/sofa.jpg"],
})
print(images)  # ["https://…/result.webp"]

Models

ModelWhat it doesOutput
picassoia/picassoia-imageText to image: 1 or 2 images from a promptlist of image URLs
picassoia/picassoia-image-editor-proEdits or combines 1 to 4 images following a promptlist of image URLs
picassoia/picassoia-videoText or image to video: 480p up to 20 s, 720p up to 10 s, 1080p up to 5 sone MP4 URL
picassoia/seedance-2.5-liteText or image to video with synchronized audio: 5, 10 or 15 s; optional closing frameone MP4 URL

The exact schema of every model is also served as JSON Schema: GET /v1/models/{owner}/{name} returns input_schema (with every field, its limits and defaults) and output_schema.

Images as input. Image fields take either kind of value:

  • an https URL that is publicly reachable;
  • a data URL: data:image/png;base64,…, also image/jpeg or image/webp, up to 5 MB each.

The images must be PNG, JPEG or WebP. PicassoIA stores its own copy of each one, so input in the prediction shows that copy's URL. Use URLs for anything larger than a few hundred KB: the whole request body is limited to 10 MB.

Unknown fields are refused, so a typo does not silently fall back to a default. The answer is 422 invalid_input.

picassoia/picassoia-image

FieldTypeDefaultNotes
promptstring, 1–4000 charactersrequiredWhat the image should show.
aspect_ratio1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:31:1
num_outputsinteger, 1–21How many images.
output_formatwebp, jpg, pngjpg
output_qualityinteger, 0–10080JPG and WebP only.
seedintegerrandomSet it to reproduce a result.

Output: a list of 1 or 2 image URLs.

picassoia/picassoia-image-editor-pro

FieldTypeDefaultNotes
promptstring, 1–4000 charactersrequiredThe edit to make. Refer to the images as "image 1", "image 2"…
imageslist of 1–4 images (https or data URLs)requiredThe first one is the main image.
aspect_ratiomatch_input_image, 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3match_input_imagematch_input_image keeps the first image's proportions.
num_outputsinteger, 1–21
output_formatwebp, jpg, pngwebp
output_qualityinteger, 0–10095JPG and WebP only.
seedintegerrandom

Output: a list of 1 or 2 image URLs.

picassoia/picassoia-video

FieldTypeDefaultNotes
promptstring, 1–4000 charactersrequiredWhat happens in the video.
imageimage (https or data URL)—Opening frame (image to video).
resolution480p, 720p, 1080p480p
durationinteger, seconds51–20 at 480p, up to 10 at 720p, up to 5 at 1080p.
aspect_ratiomatch_input_image, 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3match_input_image with an image, 16:9 without
save_audiobooleantrueKeep the generated audio track.
seedintegerrandom
enhance_promptbooleantruePicassoIA rewrites the prompt for a better video before generating it. Your own prompt stays in input.

Output: one MP4 URL (a string).

picassoia/seedance-2.5-lite

FieldTypeDefaultNotes
promptstring, 1–4000 charactersrequiredWhat happens in the video.
imageimage (https or data URL)—Opening frame.
last_frame_imageimage (https or data URL)—Closing frame. Needs image as well.
resolution480p, 720p480p
duration5, 10 or 15 (seconds)5Up to 15 at 480p, up to 10 at 720p.
aspect_ratiomatch_input_image, 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3match_input_image with an image, 16:9 without
save_audiobooleantrueKeep the synchronized audio.
seedintegerrandom
enhance_promptbooleantrueAs in picassoia/picassoia-video.

Output: one MP4 URL (a string).

Endpoints

Method and pathWhat it doesSuccess
GET /v1/modelsLists the models, with their schemas200 { "next": null, "previous": null, "results": [Model] }
GET /v1/models/{owner}/{name}One model200 Model
POST /v1/models/{owner}/{name}/predictionsCreates a prediction201 Prediction
GET /v1/predictions/{id}Gets a prediction200 Prediction
POST /v1/predictions/{id}/cancelCancels a prediction200 Prediction
GET /v1/predictionsLists your predictions, newest first200 { "next", "previous", "results": [Prediction] }

Model

json
{
  "id": "picassoia/seedance-2.5-lite",
  "owner": "picassoia",
  "name": "seedance-2.5-lite",
  "title": "Seedance 2.5 Lite",
  "description": "Text or image to video with synchronized audio: …",
  "input_schema": { "type": "object", "properties": { "…": {} }, "required": ["prompt"], "additionalProperties": false },
  "output_schema": { "type": "string", "format": "uri" }
}

Creating a prediction

POST /v1/models/{owner}/{name}/predictions with the body { "input": { … } }.

  • The API answers once the model has accepted the job, usually within seconds, with the prediction in the starting state. It never waits for the result.
  • If the prediction fails right away, the answer is still 201, with the prediction in the failed state and its error. This happens, for example, when the input is refused by the safety filter.
  • If no prediction was created, the answer is an error: invalid input, plan, too many predictions in progress, service unavailable.

Listing predictions

GET /v1/predictions returns your API predictions, 50 per page, newest first, across all your keys.

  • next is the URL of the next page (…/v1/predictions?cursor=…), or null on the last page.
  • previous is always null.
  • Predictions you deleted on picassoia.com are left out.

A prediction is visible through any of your keys, and never to anyone else.

The prediction object

FieldTypeNotes
idstringapi_ followed by 32 hex characters.
modelstringThe model it runs.
inputobjectYour input, with the defaults applied. Image fields point to PicassoIA's copy.
statusstringstarting, processing, succeeded, failed or canceled.
outputlist of URLs, one URL, or nullSet once it succeeded; its shape is the model's output_schema.
errorstring or nullWhy it failed.
created_atISO 8601 or null
started_atISO 8601 or nullUsually null: see status below.
completed_atISO 8601 or nullWhen it finished.
metrics.predict_timenumber or nullSeconds of generation, once it succeeded.
urls.get, urls.cancelURLsWhere to poll it and cancel it.
etaobject or nullWhile it runs: seconds, the estimated seconds left, and next_poll_in_seconds, when to ask again. null once it has finished.

Statuses.

StatusMeaning
startingAccepted and waiting in the queue.
processingGenerating.
succeededFinished. output has the result.
failedFinished without a result. error says why.
canceledCanceled before it finished.

starting versus processing is an estimate, made from the queue position at creation. A new prediction is always starting. The last three statuses are final: a prediction in one of them never changes again.

Errors of a failed prediction. The error of a failed prediction is one of these messages:

  • The input or the output was refused by the safety filter. The message ends in (SAFETY_CHECKER_PICASSO_FILTER_S4).
  • The prediction failed. You can try again.
  • The prediction timed out. You can try again. Nothing finished it within 3 hours.

Outputs. Output URLs are hosted by PicassoIA. Download the files you want to keep.

Polling and ETA

eta.next_poll_in_seconds tells you when the next poll is worth making:

  1. At half the estimated time.
  2. When the estimate is reached.
  3. Then every 2 seconds.

Example with an estimate of 10 s: poll after 5 s, then after 10 s, then every 2 s. Polling faster gets you nothing sooner.

Canceling

POST /v1/predictions/{id}/cancel:

The prediction is…What happens
an image, queued or generatingCanceled. Answers 200 with status: "canceled".
a video, still queuedCanceled. Answers 200 with status: "canceled".
a video, already generatingNot cancelable: 409 not_cancelable. Poll it for its result.
already finishedAnswers 200 with the prediction as it is.

A canceled prediction frees its place in your 5 predictions in progress at once.

Errors

Errors are JSON problem details, plus a stable code for programs:

json
{
  "title": "Too many predictions in progress",
  "detail": "This account can have up to 5 predictions queued or running at once, shared by its API keys and MCP connections, and 5 are. Wait for one to finish and try again.",
  "status": 429,
  "code": "concurrency_limit",
  "retry_after": 10,
  "limit": 5,
  "running": 5
}
HTTPcodeWhen
400invalid_jsonThe body is not valid JSON.
400invalid_cursorThe cursor of a list is not one the API gave you.
401missing_api_keyNo Authorization: Bearer … header.
401invalid_api_keyThe key is wrong or was revoked.
403plan_requiredCreating predictions needs an Infinite plan.
404not_foundNo such endpoint.
404model_not_foundThe API does not serve that model.
404prediction_not_foundYou have no prediction with that id.
405method_not_allowedWrong method for the endpoint (see the Allow header).
409not_cancelableA video that is already generating.
413payload_too_largeThe body is over 10 MB.
422invalid_inputThe input is not valid. See invalid_fields, below.
429concurrency_limit5 predictions are already queued or running, counting the generations the account runs through MCP. Also limit, running, retry_after and the Retry-After header.
500internal_errorSomething went wrong on our side. Try again.
502bad_gatewayThe API could not be reached. Try again.
503service_unavailable, server_misconfiguredTemporarily unavailable. Try again later, after Retry-After seconds when the header is there.

invalid_fields. A 422 lists every field at fault:

json
{
  "title": "Input validation failed",
  "status": 422,
  "code": "invalid_input",
  "detail": "input.duration: At 720p the longest clip is 10 seconds.",
  "invalid_fields": [
    { "type": "invalid_value", "field": "input.duration", "description": "At 720p the longest clip is 10 seconds." }
  ]
}

Limits

LimitValue
Predictions queued or running at once5 per account, across all its keys, shared with the generations it runs through MCP (ChatGPT, Claude, Supercomputer). The 6th answers 429.
API keys per account2
Request body10 MB
Image sent as a data URL5 MB each
Prompt4000 characters
Images for picassoia-image-editor-pro1 to 4
PriceFree: API predictions use no credits