PicassoIA Reference To Video uses the model ID picassoia/picassoia-reference-to-video. Reference-guided video with audio, clips of 1 to 15 seconds and 480p or 720p output.
Calling the API
Send requests to https://api.picassoia.com/v1/models/picassoia/picassoia-reference-to-video/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 8000 characters.
imagesarray- DefaultNone
- The images to edit or combine. Refer to them in the prompt as image 1, image 2 and so on. 1 to 9 images. At least one of images or video is required. A PNG, JPEG or WebP image, as a public https URL or as a data URL of up to 5 MB.
videostring- DefaultNone
- The video to work from. At least one of images or video is required. An MP4, MOV or WebM video of up to 50 MB, as a public https URL.
audiostring- DefaultNone
- The audio to work from. An MP3, WAV, M4A, AAC, OGG or FLAC file of up to 20 MB, as a public https URL.
resolutionstring- Default
"480p" - The size of the output. Options: 480p and 720p.
durationinteger- Default
5 - The length of the clip, in seconds. From 1 to 15.
aspect_ratiostring- Default
"match_input_image" / "16:9" - 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, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 and 1:1. If you leave it out: match_input_image when video is sent, 16:9 when it is not.
video_strengthstring- Default
"high" - How closely the clip follows the reference video. Options: high, medium and low. Requires the video field.
save_audioboolean- Default
true - Keep the generated audio track.
seedinteger- DefaultNone
- Set it to reproduce a result; leave it out for a random one.
enhance_promptboolean- Default
true - Picasso IA rewrites your prompt for a better result. Your own prompt stays in the input.
Modes
The mode depends on which files, if any, you send with the prompt. Every mode uses the same endpoint.
Example Request
Reference to Video
curl -s -X POST https://api.picassoia.com/v1/models/picassoia/picassoia-reference-to-video/predictions \
-H "Authorization: Bearer $PICASSOIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":{"prompt":"Image 1, the woman with red hair, walks into Image 2, the cafe, and sits by the window. Soft cafe ambience.","images":["https://example.com/woman.jpg","https://example.com/cafe.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: "Image 1, the woman with red hair, walks into Image 2, the cafe, and sits by the window. Soft cafe ambience.",
images: [
"https://example.com/woman.jpg",
"https://example.com/cafe.jpg"
]
}
const created = await fetch(`${API}/models/picassoia/picassoia-reference-to-video/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": "Image 1, the woman with red hair, walks into Image 2, the cafe, and sits by the window. Soft cafe ambience.",
"images": [
"https://example.com/woman.jpg",
"https://example.com/cafe.jpg",
],
}}
response = requests.post(f"{API}/models/picassoia/picassoia-reference-to-video/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-reference-to-video/predictions \
-H "Authorization: Bearer $PICASSOIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"input":{"prompt":"Image 1, the woman with red hair, walks into Image 2, the cafe, and sits by the window. Soft cafe ambience.","images":["https://example.com/woman.jpg","https://example.com/cafe.jpg"]}}'const API = 'https://api.picassoia.com/v1'
const headers = {
Authorization: `Bearer ${process.env.PICASSOIA_API_KEY}`,
'Content-Type': 'application/json',
}
const input = {
prompt: "Image 1, the woman with red hair, walks into Image 2, the cafe, and sits by the window. Soft cafe ambience.",
images: [
"https://example.com/woman.jpg",
"https://example.com/cafe.jpg"
]
}
const created = await fetch(`${API}/models/picassoia/picassoia-reference-to-video/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": "Image 1, the woman with red hair, walks into Image 2, the cafe, and sits by the window. Soft cafe ambience.",
"images": [
"https://example.com/woman.jpg",
"https://example.com/cafe.jpg",
],
}}
response = requests.post(f"{API}/models/picassoia/picassoia-reference-to-video/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-reference-to-video",
"input": {
"prompt": "Image 1, the woman with red hair, walks into Image 2, the cafe, and sits by the window. Soft cafe ambience.",
"images": [
"https://example.com/woman.jpg",
"https://example.com/cafe.jpg"
],
"resolution": "480p",
"duration": 5,
"aspect_ratio": "16:9",
"save_audio": true,
"enhance_prompt": true
},
"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": 60,
"next_poll_in_seconds": 30
}
}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": 30,
"next_poll_in_seconds": 30
}
}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.mp4",
"error": null,
"completed_at": "2026-10-01T09:30:54.000Z",
"metrics": {
"predict_time": 52.8
},
"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 the URL of an MP4 video.
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 8000 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.
API PricingJSON Schema
The same schema the API serves at GET /v1/models/picassoia/picassoia-reference-to-video.
{
"input_schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"prompt": {
"type": "string",
"minLength": 1,
"maxLength": 8000,
"description": "What happens in the video and what is heard. Name the references: \"Image 1\", \"Video 1\", \"Audio 1\"."
},
"images": {
"description": "Up to 9 reference images (characters, objects, places), in the order of \"Image 1\", \"Image 2\"… At least one image or a video is required.",
"maxItems": 9,
"type": "array",
"items": {
"type": "string",
"description": "A reference image: an https URL or a data URL."
}
},
"video": {
"type": "string",
"description": "Reference video: an https URL (MP4, MOV or WebM, up to 50 MB; its first 15 s are used). The clip follows its movement, camera and timing, keeps its sound unless an audio is sent, and lasts as long as the video, or duration if that is shorter. To put your own character in it, say who replaces whom (\"Image 1 replaces the woman in Video 1\") and what they do."
},
"audio": {
"type": "string",
"description": "Reference audio: an https URL (MP3, WAV, M4A, AAC, OGG or FLAC, up to 20 MB). A voice a character says with lip sync (\"Image 1 says Audio 1\"), or music. It is heard as it is. Lip sync is most exact in clips of up to 5 s."
},
"resolution": {
"default": "480p",
"description": "480p or 720p, both up to 15 s.",
"type": "string",
"enum": [
"480p",
"720p"
]
},
"duration": {
"default": 5,
"description": "Length in seconds.",
"type": "integer",
"minimum": 1,
"maximum": 15
},
"aspect_ratio": {
"description": "Default: 16:9, or the reference video's when there is one. match_input_image follows the video if there is one, otherwise the first image.",
"type": "string",
"enum": [
"match_input_image",
"16:9",
"9:16",
"4:3",
"3:4",
"3:2",
"2:3",
"1:1"
]
},
"video_strength": {
"description": "Only with a video: how closely the clip follows it. high (the default) follows its camera, composition and timing most closely; medium and low follow it more loosely and render faster.",
"type": "string",
"enum": [
"high",
"medium",
"low"
]
},
"save_audio": {
"default": true,
"description": "Keep the generated audio track.",
"type": "boolean"
},
"seed": {
"description": "Random seed. Set it to reproduce a result.",
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"enhance_prompt": {
"default": true,
"description": "Let PicassoIA rewrite the prompt for a better video before generating it.",
"type": "boolean"
}
},
"required": [
"prompt"
],
"additionalProperties": false
},
"output_schema": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"format": "uri",
"description": "URL of the generated video (MP4)."
}
}