Extend
Add up to 32 seconds before or after a song from a completed Udio job.
POST /v2/music/udio/action — action: "extend"
Equivalent to Udio's Extend button: adds a new section to one of the parent job's songs, either before it or after it. The result is a fresh pair of songs — same shape as a generation — and each can be extended again, which is how you build a track longer than Udio's 130-second maximum.
One extension per call, and at most 32 seconds of new audio, because Udio renders one 32-second sample at a time. Chaining is deliberate: you hear each section before deciding what comes next.
Select the song with index (1 or 2) or with the trackId from the parent's
result.tracks[].id — exactly one of the two.
Request
| Parameter | Type | Required | Description |
|---|---|---|---|
parentJobId | string (uuid) | Yes | ID of the completed udio job to act on |
action | string | Yes | "extend" |
index | number | One of index/trackId | Which of the parent's songs to extend: 1 or 2 |
trackId | string | One of index/trackId | Song ID from the parent's result.tracks[].id |
placement | string | No | Where the new section goes: "after-add-section", "after-add-outro", "before-add-section", "before-add-intro" |
output_length | number | No | Seconds of new audio, 1–32. Defaults to Udio's own choice |
prompt | string | No | Description of the new material (max 5,000 characters). Defaults to the source song's |
lyrics | string | No | Your own words for the new section (max 5,000 characters) |
lyrics_prompt | string | No | What the new section should be about — Udio writes the lyrics from it (max 2,000 characters) |
lyrics_type | string | No | "generate", "user" or "instrumental" |
negative_tags | string | No | Styles to avoid (max 500 characters) |
model | string | No | Model version for the extension. Defaults to the source song's |
voice | object | No | A reference voice |
style_ref | object | No | A reference style |
bpm | number | No | Target tempo, 60–200 |
seed | integer | No | Seed for reproducibility |
prompt_strength | number | No | 0–1 |
lyrics_strength | number | No | 0–1 |
clarity_strength | number | No | 0–1 |
generation_quality | number | No | 0–1 |
webhookUrl | string | No | HTTPS URL that receives a JSON POST when the job reaches a subscribed event — see Webhooks for payload formats |
webhookEvents | string[] | No | Events to be notified about: "progress", "completed", "failed". Defaults to ["completed", "failed"] when webhookUrl is set |
Everything you leave out is inherited from the source song, so a bare request just continues what is already there.
Two limits are Udio's, not ours, and both are rejected before any credits are deducted:
- Songs generated with a
udio130-*model cannot be extended. Overridemodelwith audio32-*one, or remix instead (400). - Songs longer than 15 minutes cannot be extended (
409).
Credits
3 credits per extend — one 32-second sample, whatever the length of the source song.
Example result
Once the job is COMPLETED, the result object on
GET /v2/jobs/:id contains a fresh pair of songs:
{
"tracks": [
{
"id": "2c9a7e51-8f4b-4d3a-b6e0-1f5d8c2a9b74",
"audioUrl": "https://cdn2.apiframe.ai/audio/d4e5f6a7-b8c9-0123-def4-567890123456-0.mp3",
"imageUrl": "https://cdn2.apiframe.ai/audio/d4e5f6a7-b8c9-0123-def4-567890123456-0.jpeg",
"title": "Midnight Frequencies",
"tags": "lo-fi, hip hop, chill",
"duration": 48.6
},
{
"id": "7e3b1d96-2a5c-4f8e-9c07-4b6a1e8d3f25",
"audioUrl": "https://cdn2.apiframe.ai/audio/d4e5f6a7-b8c9-0123-def4-567890123456-1.mp3",
"imageUrl": "https://cdn2.apiframe.ai/audio/d4e5f6a7-b8c9-0123-def4-567890123456-1.jpeg",
"title": "Midnight Frequencies",
"tags": "lo-fi, hip hop, chill",
"duration": 48.2
}
]
}Each of these songs can be the parent of another extend.
Code examples
curl -X POST https://api.apiframe.ai/v2/music/udio/action \
-H "X-API-Key: afk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"parentJobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"action": "extend",
"index": 1
}'import requests
response = requests.post(
"https://api.apiframe.ai/v2/music/udio/action",
headers={
"X-API-Key": "afk_your_api_key_here",
"Content-Type": "application/json",
},
json={
"parentJobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"action": "extend",
"index": 1,
},
)
print(response.json())const response = await fetch("https://api.apiframe.ai/v2/music/udio/action", {
method: "POST",
headers: {
"X-API-Key": "afk_your_api_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
parentJobId: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
action: "extend",
index: 1,
}),
});
console.log(await response.json());body := `{
"parentJobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"action": "extend",
"index": 1
}`
req, _ := http.NewRequest("POST", "https://api.apiframe.ai/v2/music/udio/action",
strings.NewReader(body))
req.Header.Set("X-API-Key", "afk_your_api_key_here")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)Add a 16-second outro with your own lyrics
curl -X POST https://api.apiframe.ai/v2/music/udio/action \
-H "X-API-Key: afk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"parentJobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"action": "extend",
"trackId": "8b2f64d1-3c5e-4a7f-9d2b-6e1a0c4f8b3d",
"placement": "after-add-outro",
"output_length": 16,
"lyrics": "[Outro]\nCity fades into the dawn"
}'Add an intro instead
curl -X POST https://api.apiframe.ai/v2/music/udio/action \
-H "X-API-Key: afk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"parentJobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"action": "extend",
"index": 1,
"placement": "before-add-intro",
"output_length": 8,
"lyrics_type": "instrumental"
}'Try it
/v2/music/udio/actionTry it