POST /v1/generations
Create a new music generation
Start generating an AI music track with the specified parameters. Deducts one credit and submits to the generation queue.
Want vocal-free instrumentals without specifying BPM, key, or scale? See POST /v1/instrumental.
Request
POST https://pub.finetuning.ai/v1/generationsHeaders
| Header | Type | Required | Description |
|---|---|---|---|
X-API-Key | string | Yes | Your API key |
Content-Type | string | Yes | application/json |
Body parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
tags | string | Yes | — | Music description (max 700 chars) |
lyrics | string | No | — | Lyrics text (max 2000 chars) |
duration | number | No | 60 | Track duration in seconds (5–210; free tier capped at 120) |
bpm | number | No | 120 | Beats per minute (60–200) |
language | string | No | — | Language code (en, ja, de, fr, es, zh, ko, pt, it, ru) |
key | string | No | auto | Musical key (C, C#, D, ... B) |
scale | string | No | auto | Scale type (major, minor) |
timesignature | string | No | "4" | Time signature (2–7) |
vocalGender | string | No | "auto" | Steers the singing voice: auto, female, or male. Only meaningful when lyrics are supplied. See Vocal gender below. |
seed | number | No | — | Seed for reproducibility |
webhook | string | No | — | HTTPS URL we'll POST the completed (or failed) track to. Skips polling. Max 2048 chars. Identified by User-Agent: finetuning-webhook/1.0. See the Webhooks guide for payload shape and how to secure your endpoint. |
certificate | boolean | No | false | true issues a generation certificate for this track the moment it completes, and returns a download URL on your webhook. See Certificates below. |
Example request
curl -X POST https://pub.finetuning.ai/v1/generations \
-H "X-API-Key: ft_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"tags": "lofi chill piano night",
"duration": 120,
"bpm": 85,
"key": "C",
"scale": "minor",
"language": "en",
"timesignature": "4",
"seed": 1234567,
"webhook": "https://your-app.com/finetuning-callback?token=YOUR_SECRET"
}'Response
{
"data": {
"id": "gen_xyz789",
"status": "processing",
"prompt": "lofi chill piano night",
"parameters": {
"bpm": 85,
"duration": 120,
"timesignature": "4",
"language": "en",
"keyscale": "C minor",
"seed": 1234567
},
"webhook": "https://your-app.com/finetuning-callback?token=YOUR_SECRET",
"certificate": false,
"creditsRemaining": 1952,
"createdAt": "2025-01-15T10:30:00Z"
}
}If webhook was not provided, the field is returned as null.
Response fields
| Field | Type | Description |
|---|---|---|
data.id | string | Unique generation ID |
data.status | string | processing, pending, completed, failed |
data.prompt | string | The tags/prompt actually used — includes the vocal-gender phrasing when vocalGender was set |
data.parameters.bpm | number | BPM of the track |
data.parameters.duration | number | Requested duration in seconds |
data.parameters.timesignature | string | Time signature |
data.parameters.language | string | Language code |
data.parameters.keyscale | string | Combined key and scale (e.g., "C minor") |
data.parameters.seed | number | Seed used for generation |
data.webhook | string | null | Echo of the webhook URL we accepted, or null if not provided |
data.certificate | boolean | Echo of the certificate flag we accepted |
data.creditsRemaining | number | Credits remaining after this generation |
data.createdAt | string | ISO 8601 timestamp |
Certificates
Set certificate: true to have a generation certificate issued the moment the
track completes. The completion webhook then carries the certificate's number, its public
verification URL and a download URL for the PDF.
{
"tags": "lofi chill piano night",
"certificate": true,
"webhook": "https://your-app.com/finetuning-callback?token=YOUR_SECRET"
}You only need this if you delete tracks after archiving them: a certificate is rendered from
the generation, so it can't be produced once the generation is gone. If you keep your tracks, leave
the flag off and call GET /v1/generations/:id/certificate
whenever you need the PDF.
Two things worth knowing:
- It must be a real JSON boolean. We accept
trueand nothing else — the string"true"is read as "no certificate". The response'sdata.certificateis how you confirm what we accepted. - Certificates are free. Requesting one never costs a credit, and a certificate that fails to issue never stops the track being delivered.
See Certificates for the webhook payload and the archiving pattern.
Vocal gender
Set vocalGender to female or male to steer the singing voice. It only affects
tracks that have lyrics — an instrumental has no voice to steer.
{
"tags": "modern pop, clean production, catchy melody",
"lyrics": "[verse]\nCity lights are fading out tonight",
"vocalGender": "female"
}Three things are worth knowing before you rely on it:
- It is a strong steer, not a guarantee. In our own blind testing the requested
gender was never inverted, but a small share of takes come out with the vocal too
indistinct to call. If a take is wrong, generate again — a different
seedusually resolves it. data.promptcomes back rewritten. We prepend the vocal phrasing to yourtags, and the response echoes what was actually used. Storedata.promptif you want to reproduce a track exactly.- Re-sending a returned
promptis safe. If you feeddata.promptback in astagswith a differentvocalGender, the previous phrasing is replaced rather than stacked, so the two never conflict.
Omitting the field, or sending auto, leaves generation exactly as it was before this
option existed.
Errors
| Code | Status | Description |
|---|---|---|
VALIDATION_ERROR | 400 | Invalid parameters (check message field for details) |
MISSING_API_KEY | 401 | X-API-Key header was not provided |
INVALID_API_KEY | 401 | API key is malformed, revoked, or does not exist |
MONTHLY_LIMIT_REACHED | 402 | No generations remaining this month |
PRO_PLAN_REQUIRED | 403 | Your subscription does not include API access |
RATE_LIMITED | 429 | Too many requests on a read endpoint — wait and retry (60/min/user) |
GENERATION_RATE_LIMITED | 429 | Too many POST /v1/generations calls — wait and retry (10/min/user) |
QUEUE_FULL | 429 | Too many generations in progress |
GENERATION_FAILED | 500 | Generation queue submission failed |