Music Generation API
Create music tracks with POST /api/v1/music, including options, pricing, and errors.
Create a music task
POST /api/v1/music generates an original track from a text prompt. The track renders before the call returns, so the response usually already has status: "success" and the track URL in data.taskUrls; keep the connection open and use a generous client timeout. The returned data.id also works with GET /api/v1/tasks/{taskId} if you prefer one polling flow.
POST https://viewmax.studio/api/v1/music
Authorization: Bearer sk-your-api-key
Content-Type: application/json
Idempotency-Key: lofi-001Request body
| Field | Required | Type | Description |
|---|---|---|---|
prompt | Yes | string | Non-empty description of the track |
options | No | object | Generation controls |
options
| Field | Type | Description |
|---|---|---|
duration_seconds | number | Track length in seconds: a JSON number from 3 to 300. Default 60 |
instrumental | boolean | true for no vocals |
style | string | Genre or mood. Changes how prompt is read; see below |
title | string | Song title. Changes how prompt is read; see below |
lyrics | string | Lyrics to sing |
options.duration and options.music_length_ms are rejected; use duration_seconds.
When style or title is set, the model receives prompt as lyrics. To describe a track, put the whole description in prompt and leave style and title unset. To give a song a style, put the lyrics in prompt and do not also send lyrics.
Cost
Music costs max(60, ceil(duration_seconds)) credits: 60 credits up to 60 seconds, then 1 credit per second (300 seconds cost 300). Omitting duration_seconds renders 60 seconds for 60 credits. Plan allowances do not apply to music on the API. GET /api/v1/models?type=music lists the base price as credits.
Example
curl -X POST https://viewmax.studio/api/v1/music \
-H "Authorization: Bearer $VIEWMAX_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: lofi-001" \
-d '{
"prompt": "Warm lo-fi hip-hop with a mellow piano loop",
"options": { "duration_seconds": 30, "instrumental": true }
}'This costs 60 credits.
Successful response
{
"code": 0,
"message": "ok",
"data": {
"id": "5c7e9a1b-3d5f-4b6c-8e0a-2f4b6d8e0a12",
"userId": "usr_123",
"mediaType": "music",
"model": "music_v2",
"prompt": "Warm lo-fi hip-hop with a mellow piano loop",
"options": "{\"duration_seconds\":30,\"instrumental\":true}",
"status": "success",
"createdAt": "2026-09-29T08:00:00.000Z",
"updatedAt": "2026-09-29T08:00:42.000Z",
"deletedAt": null,
"taskId": "sync:5c7e9a1b-3d5f-4b6c-8e0a-2f4b6d8e0a12",
"taskInfo": null,
"taskResult": "{\"output\":[\"https://cdn.example.com/music/track.mp3\"]}",
"taskUrls": ["https://cdn.example.com/music/track.mp3"],
"costCredits": 60,
"scene": "text-to-music",
"visibility": "user",
"creditId": "cr_789",
"waivedCredits": 0,
"fairUseKey": null
}
}The fields are described under Task fields in the Video Generation API. taskUrls is always an array.
Idempotency and refunds
- Send
Idempotency-Key(1–200 visible ASCII characters, no spaces). Repeating a key returns the original task and itscostCreditswithout charging again; the body is not compared. Retry a timed-out request with the same key; use a new key for a deliberate new attempt. Without the header, every POST creates and charges a new task. - Credits are deducted when the task is created. If the balance is lower than the cost, the call returns 402 and creates no task. A task that ends
failedorcanceledis refunded automatically.
Errors
All errors use the error envelope.
| HTTP | error.type | Example message | What to do |
|---|---|---|---|
| 400 | invalid_request | prompt is required | Send a non-empty prompt |
| 400 | invalid_request | options.duration_seconds must be a number from 3 to 300 | Send a JSON number in range |
| 400 | invalid_request | options.duration is not supported; use options.duration_seconds | Rename the option |
| 400 | invalid_request | the generation request uses unsupported settings, with error.task_id | The provider rejected the settings; the task failed and was refunded. Read its taskInfo.errorMessage, adjust, and retry with a new key |
| 400 | content_rejected | input content was rejected by safety filters | Change the prompt or lyrics; do not retry unchanged |
| 402 | insufficient_credits | insufficient credits | No task was created. Add credits; do not retry until then |
| 500 | internal_error | the generation service is temporarily unavailable | With error.task_id the task failed and was refunded: retry once. If the hint says the outcome is unknown, poll that task instead of retrying |
| 503 | internal_error | — | The content-safety check is temporarily unavailable; retry after a short delay |