Make videos and songs from your own tools
Everything the app does, from a script or a server: describe a scene, add pictures, get a short video with sound; say what a song is about, get it written and sung. Everything made with a key counts on the key owner's week, exactly like in the app.
Your key
Create one on the app's Account screen (“API access”). It is shown once. Send it as a bearer token on every call:
Authorization: Bearer ip_xxxxxxxxxxxxxxxxxxxxxxxx
A key reaches the studio's trades only (https://api.infinitepeople.ai/v1/video/… and /v1/song/… today, the other trades as they come); it cannot change your subscription or make other keys. Revoke it from the same screen if it leaks — or let the key burn itself: DELETE /v1/studio/key with that key revokes it at once. The app itself runs on these very routes: whatever it can do, a key can do, with the same account, the same week and the same plan.
Make a video
curl -X POST https://api.infinitepeople.ai/v1/video/generate \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"model": "minimax-h3-ref2va",
"prompt": "Mia hails a taxi in the rain, shouting \"Taxi!\"",
"reference_urls": ["https://example.com/mia.jpg"],
"duration_s": 5,
"enhance": "low",
"review": false
}'
The answer is the creation: {"id": "…", "status": "enhancing", "ready": false, "keep_polling": true, "poll": "https://api.infinitepeople.ai/v1/video/jobs/…", "next": "Fetch 'poll' right now…", …}. Do not wait or sleep: fetch poll right away — it holds the connection up to 25 seconds and answers as soon as something changes (?wait=0 answers at once; GET or POST alike):
curl https://api.infinitepeople.ai/v1/video/jobs/$JOB_ID -H "Authorization: Bearer $KEY"
One stop signal: ready: true — result.url is the mp4 (kept 7 days; a video kept in a project stays), result.poster_url its first frame, result.last_poster_url its last one — or keep_polling: false with a code (generation_failed, cancelled). next always says what to do; page_for_your_human is the creation in the app. The job's own fields (status, request, result_url…) stay as they were.
Two models
| model | pictures | use it for |
|---|---|---|
minimax-h3-ref2va | reference_urls (up to 4) | people, places and styles the video draws on; cite them in the prompt as <Picture 1>, <Picture 2>… A voice_url (mp3, wav, m4a) gives the voice, cited as <Audio 1>. |
minimax-h3-fl2va | first_frame_url, optional last_frame_url | the video starts on the first picture and lands on the second; no picture at all works too (text only). Pictures are brought to the clip's shape (see Shape). |
Fields
| field | values | |
|---|---|---|
prompt | text | What happens: who, where, what moves. One sentence is enough. Written in any language; a line of dialogue in quotes is spoken as is. |
duration_s | 5, 10, 15 | 5 s on the free plan, up to 15 s with a subscription. |
enhance | off, low, medium, high | The direction help: off sends your prompt as is; low (default) rewrites it for the model; medium and high also paint the first image from your pictures or your text (high: twice as detailed). What your plan allows: GET /v1/video/me → limits.enhance_levels. |
definition | low, standard, high | 864×480, 1024×576, 1344×768 at 16:9, the same pixel count at another shape (high is capped at 10 s). |
aspect | 16:9, 9:16, 1:1, 4:3, 3:4, 21:9, auto | The clip's shape; auto follows the first frame given, 16:9 without one. |
confidentiality | low, medium, high | Where the work may run (default: the account's setting, low). low: the best tool wherever it is. medium: our machines and contracted providers that never train on your content, results stored in Europe. high: our own machines only — not open yet; GET /v1/studio/me → confidentiality.levels says what each level allows today. |
audio | true / false | The soundtrack, on by default. |
review | true / false | With a painted first image, the app shows it for 10 s before rendering. From a script, send false: the job goes straight to rendering. |
seed | integer | Fix it to reproduce a take. |
negative_prompt | text | What to steer away from. |
Your own pictures
A picture must be reachable by URL. If it is not hosted anywhere, send it first (JPEG, PNG or WebP, 8 MB max); the URL lives 7 days:
curl -X POST https://api.infinitepeople.ai/v1/video/inputs \
-H "Authorization: Bearer $KEY" -F "[email protected]"
# → {"url": "https://media.infinitepeople.ai/ephemeral/inputs/…jpg"}
Pictures kept in a project (GET /v1/video/projects) have permanent URLs you can reuse directly.
Shape
aspect: 16:9 (the default), 9:16, 1:1, 4:3, 3:4, 21:9, or auto to follow the first frame you give. A definition keeps its pixel count whatever the shape (low 864×480 or 480×864 or 640×640…, high 1344×768 or 768×1344 or 1008×1008). 24 frames per second, 5 to 15 seconds. A start or end frame of another shape is cropped around its centre when it is close, set on black bars when it is the other orientation; with the medium or high direction, the picture is extended to the shape instead.
The first image under review
With enhance: medium or high and review: true, the job stops in status review for ten seconds with its painted first image (request.enhanced.painted_urls[0]), then renders on its own. Act on it meanwhile:
curl -X POST https://api.infinitepeople.ai/v1/video/jobs/$JOB_ID/review \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"action": "approve"}' # or "pause", "resume", or "regen" with "panels": [1] to repaint it
Start again from a video
A finished video gives pictures to start from: poster_url (its first frame), last_poster_url (its last), or any moment:
curl -X POST https://api.infinitepeople.ai/v1/video/jobs/$JOB_ID/frame \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -d '{"t": 2.5}'
# → {"url": "https://media.infinitepeople.ai/ephemeral/inputs/…jpg", "t": 2.5}
Use that URL as first_frame_url (a sequel), last_frame_url, or in reference_urls; add "edited_from": "$JOB_ID" to keep the lineage, and the same seed for the same take. A cancelled job: DELETE /v1/video/jobs/$JOB_ID (free before the render; counted once a node has it, the answer says charged). The same call deletes a finished video.
Projects
Folders of pictures kept for good, the same as the app's: GET /v1/video/projects lists them with their pictures; POST /v1/video/projects {"name": "Mia"} makes one; POST /v1/video/projects/{id}/references (multipart file) or …/references/from-url {"url": …} (one of our URLs, a frame for instance) keeps a picture; DELETE on a project or a reference removes it, files included.
A finished video kept in a project does not expire while the project exists: POST /v1/video/projects/{id}/videos {"job_id": …} copies it for good and answers the kept video with a job object shaped like GET /v1/video/jobs/{id} (its result_url is the permanent copy); the project then lists it under videos; DELETE /v1/video/projects/{id}/videos/{video_id} takes it out. A frame (…/jobs/{id}/frame) of a kept video still works after the job record is gone.
Make a song
A song is made in two calls: the director writes the words, you send them (edited or not) to be sung. One take per call; songs are kept 7 days like videos.
1. The words
curl -X POST https://api.infinitepeople.ai/v1/song/write \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"request": "une chanson pop pour les 30 ans de Léa, qui adore voyager, de Lisbonne à Tokyo"}'
# → {"title": "…", "language": "fr", "duration_s": 170, "caption": "pop, festive, female vocal, …",
# "lyrics": "[Intro - quiet]\n…\n\n[Chorus - anthemic]\n…", "words": 210}
Nothing is queued: the draft is yours to keep, edit or send back. Name an artist in the request to point at a style (the words stay original). To rework a draft, send it again with notes:
-d '{"request": "…", "previous": {…the draft…}, "notes": "one more verse about Lisbon, a sadder chorus"}'
2. The music
curl -X POST https://api.infinitepeople.ai/v1/song/generate \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"title": "Trente escales pour Léa",
"lyrics": "[Verse - intimate]\nElle a des cartes au fond des poches\n…",
"caption": "pop, festive, female vocal, acoustic guitar, synthesizer, warm, catchy chorus, french",
"language": "fr",
"duration_s": 170,
"mode": "fast"
}'
The answer is the job (status: queued); poll GET /v1/song/jobs/{job_id}. When done, result_url is the mp3, recipe_url a JSON next to it with the timed lyrics (lrc) and what the song was made of, duration_s the measured length.
| field | values | |
|---|---|---|
lyrics | text, 4096 chars max | Section tags on their own lines ([Verse], [Chorus], [Bridge]…), with one or two performance hints after a dash ([Chorus - anthemic], [Bridge - whispered]). [Instrumental] alone for no vocals. |
caption | tags, 512 chars max | The style as comma-separated tags: genre, mood, voice and its timbre, instruments, era and production, texture, structure. Not a sentence; reminiscent of <artist> points at a style. |
duration_s | 30 to 240 | 2 min on the free plan, 3 min on Starter, 4 min from Standard. The words are paced to fit. |
mode | fast, normal | fast (default) writes the song's blueprint with the light planner, about a minute; normal with the large one, richer composition, about twice the time (with a subscription). |
language | fr, en, es, it, de, pt, nl, ja, ko, zh | The language of the vocals. |
title | text | Kept on the job for your library. |
instrumental | true / false | No vocals, whatever the lyrics say. |
bpm, keyscale | 82, "D major" | Left out, the planner chooses them (usually well when the caption is rich). |
seed | integer | Fix it to reproduce a take. |
Statuses are queued, in_progress, done, failed, cancelled; DELETE /v1/song/jobs/{job_id} cancels or deletes like a video. Five songs in progress at most per account; thirty drafts an hour. Your week and what your plan allows in the songs: GET /v1/song/me → limits.modes, limits.max_duration_s. The catalogue: GET /v1/song/models.
Statuses
| status | |
|---|---|
enhancing | waiting for, or under, the direction help |
review | the painted first image awaits your say (review: true); it goes on by itself after review_deadline |
queued | in line for a rendering node |
in_progress | rendering; estimate_seconds is the expected render time |
done | result_url is ready |
failed, cancelled | the week gets the video back (unless cancelled while rendering) |
DELETE /v1/video/jobs/{job_id} cancels a job (free before the render, counted once a node has it) or deletes a finished video.
Refusals
Every refusal is a status and a JSON body: detail is a sentence that says what to do and whether retrying makes sense, code a stable word, retry a boolean (with retry_after_s when waiting helps): {"detail": "This account's week is used up…", "code": "week_over", "retry": false, "resets_at": "…"}. A 4xx without a JSON body is not ours.
| code | |
|---|---|
402 not_on_plan | the length or the direction level is not on your plan |
402 week_over | your week is spent; resets_at says when it starts again |
422 | the request or a picture was refused by moderation, or a field is wrong (invalid_request: the detail says which) |
422 not_available_at_confidentiality | the confidentiality asked (low by default, medium, high) cannot do a step of the request; refused names the option to change (the direction help needs a cloud service) — high is not open yet |
429 | ten videos already in progress on the account |
503 | the service is paused for maintenance; try again later |
Tell us
A bug, a wish, a word on a creation: POST /v1/studio/feedback with {"subject": "api" | "video" | "song" | "app" | "other", "message": "…", "job_id": "…"} (the job is optional). We read every one. The rating of a video (1 to 5 and a comment) stays on POST /v1/video/jobs/{job_id}/feedback.
For machines
The OpenAPI document of every route a key can reach: api.infinitepeople.ai/v1/studio/openapi.json. This page in Markdown: /api/index.md. A summary for language models: /llms.txt.
Your week
curl https://api.infinitepeople.ai/v1/video/me -H "Authorization: Bearer $KEY"
week.left_pct is the share of your week that is left, week.resets_at when it starts again, limits what your plan allows. The catalogue with every field: GET /v1/video/models.