---
title: Infinite Studio API
description: Make videos and songs from your own tools with a personal key; every video or song counts on the key owner's week.
canonical: https://studio.infinitepeople.ai/api/
openapi: https://api.infinitepeople.ai/v1/studio/openapi.json
---

# Infinite Studio API

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](/#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 "file=@mia.jpg"
# → {"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](https://api.infinitepeople.ai/v1/studio/openapi.json). This page in Markdown: [/api/index.md](/api/index.md). A summary for language models: [/llms.txt](/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.

Videos are generated by MiniMax H3 and songs by ACE-Step 1.5 on our machines; the direction help and the lyrics use OpenAI models. Every request is moderated before it runs. The [terms of use](/#terms) apply to what you make with a key as much as in the app.

Base URL: `https://api.infinitepeople.ai/v1`. Authentication: `Authorization: Bearer <your key>` (a key made on https://studio.infinitepeople.ai/#keys). OpenAPI: https://api.infinitepeople.ai/v1/studio/openapi.json. The HTML version of this page: https://studio.infinitepeople.ai/api/ — this Markdown file is for readers and language models.
