POST /v1/reimagine
A new song from your song's lyrics, in a style you choose — 2 credits in total
Write and perform a brand-new track from an upload's lyrics and length, in a new style. Only the words and the length carry over: the melody, instruments and voice are new. Returns immediately with processing; the track arrives on your webhook or by polling GET /v1/generations/:id.
The first Reimagine from an upload costs 1 credit, on top of the 1 credit its transcription cost — 2 in total. Every later Reimagine from the same upload costs 2. (So does one from an upload whose transcription was refunded, if you send your own lyrics.) Credits are refunded automatically if the job fails; a failed first Reimagine hands the transcription credit back, so the next try costs 1 again. creditsCharged in the response and the upload's pricing.nextReimagineCredits tell you exactly.
The track is private to your account — it can't be made public, shared or certified.
Request
POST https://pub.finetuning.ai/v1/reimagineHeaders
| 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 |
|---|---|---|---|---|
uploadId | string | Yes | — | From POST /v1/songs/uploads |
lyrics | string | No | the transcript | What the new song sings, one line per sung line, with optional section tags ([verse], [chorus], [bridge]…). Max: the upload's lyricsCap (18 characters per second of song, 600–3,500; 3,240 for 3:00). Omit to use the lyrics we heard |
templateId | string | One of templateId / prompt | — | A curated style with vocals (isInstrumental: false) from GET /v1/templates |
prompt | string | One of templateId / prompt | — | Your own style description, like tags on POST /v1/generations. Max 700 characters. Ignored when templateId is set |
extraPrompt | string | No | — | With templateId: extra words added to the style (max 300 characters) |
vocalGender | string | No | "auto" | "auto", "female" or "male" |
webhook | string | No | — | HTTPS URL we POST the finished (or failed) track to. Max 2,048 characters. See Webhooks |
The new track is as long as the upload's window (durationSeconds, at most 3:00).
Example request
curl -X POST https://pub.finetuning.ai/v1/reimagine \
-H "X-API-Key: ft_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"uploadId": "22970e5b-c403-494c-b10d-846df6dde7c5",
"templateId": "template-open-roads",
"vocalGender": "female",
"webhook": "https://your-app.com/finetuning-callback?token=YOUR_SECRET"
}'const res = await fetch('https://pub.finetuning.ai/v1/reimagine', {
method: 'POST',
headers: { 'X-API-Key': process.env.FINETUNING_API_KEY!, 'Content-Type': 'application/json' },
body: JSON.stringify({
uploadId: upload.id,
prompt: 'acoustic folk, fingerpicked guitar, warm',
// Fix a misheard line before rendering, or leave `lyrics` out to sing the transcript as heard:
lyrics: upload.lyrics.replace('Neon humming', 'Neon hums along'),
}),
})
if (res.status === 409) {
// TRANSCRIPT_NOT_READY: still listening. Poll GET /v1/songs/uploads/:id, or send your own lyrics.
}
const { data } = await res.json()Response
{
"data": {
"id": "959ef62f-45c1-4cb6-a4db-fa1da227f5fe",
"status": "processing",
"type": "reimagine",
"title": "my-song (Open Roads)",
"uploadId": "22970e5b-c403-494c-b10d-846df6dde7c5",
"duration": 180,
"isPublic": false,
"webhook": "https://your-app.com/finetuning-callback?token=YOUR_SECRET",
"creditsCharged": 1,
"creditsRemaining": 2044,
"createdAt": "2026-09-30T13:53:47.108Z"
}
}The track usually takes about a minute. Poll GET /v1/generations/{id} until status is completed (it then has an audioUrl) or failed (its credits are already back). With a curated style, the generation's prompt is empty: template prompts stay private, as they do everywhere else.
Errors
| Code | Status | Description |
|---|---|---|
VALIDATION_ERROR | 400 | uploadId missing; no templateId or prompt; unknown or instrumental templateId; prompt over 700; bad webhook |
LYRICS_REQUIRED | 400 | No lyrics sent and none heard (e.g. status: "no_vocals") |
LYRICS_TOO_LONG | 400 | Over the cap; details.maxLength is the cap for this upload |
INSUFFICIENT_CREDITS | 402 | Fewer credits left than this render costs (1 or 2). details.cost says which |
PAID_PLAN_REQUIRED | 403 | Not on a paid plan |
NOT_FOUND | 404 | No upload with that id on your account |
TRANSCRIPT_NOT_READY | 409 | lyrics omitted while we're still listening |
TRANSCRIPT_REQUIRED | 409 | A Remove vocals upload that hasn't been transcribed yet |
CATALOGUE_MATCH | 409 | The upload was blocked |
CREDITS_CHANGED | 409 | Your balance changed mid-request; retry |
UPLOAD_EXPIRED | 410 | The upload's 24 hours are up |
QUEUE_FULL | 429 | Too many generations in progress |
GENERATION_RATE_LIMITED | 429 | More than 10 Reimagine / Remove vocals requests in a minute |
GENERATION_FAILED | 502 | We couldn't start it; your credits are back |