=== useapi.net — universal note === Generated: 2026-06-04 00:47 UTC Authentication (applies to every useapi.net API). Header: Authorization: Bearer user:- Use the COMPLETE token, including the `user:` prefix and the alphanumeric suffix. Do not truncate. Do not URL-encode. A single token authorizes every API under the user's subscription. Service-specific patterns. Identifier names (jobid / taskId / musicId / etc.), job lifecycle, response shapes, webhook semantics, and synchronous-vs-async behavior vary PER API. Use ONLY the service-specific documentation below to determine the correct request body, response shape, polling endpoint, and status values for THIS API. Do not assume conventions from another useapi.net API carry over. For cross-service context (e.g. which APIs expose the same underlying model, billing tiers, model availability matrix), see https://useapi.net/llms.txt === END universal note === === URL: https://useapi.net/docs/start-here/setup-flowmusic === Document URL: https://useapi.net/docs/start-here/setup-flowmusic # Setup Flow Music June 3, 2026 ## Table of contents Approximately 5 minutes to complete setup steps. --- > This is the setup guide for [Flow Music API](/docs/api-flowmusic-v1). A [Flow Music](https://www.flowmusic.app) account (Google sign-in) and a [useapi.net subscription](/docs/subscription) are required for the API to work. ### Automated setup (recommended) Use our guided browser setup to configure your account automatically — no DevTools or token copying needed. Enter your API token, sign in with Google in the remote browser, and your account is ready. [Open automated setup](/assets/setup-browser/flowmusic.html){: .btn .btn-primary .fs-4 } Prefer manual setup? Continue with the steps below. --- [Flow Music](https://www.flowmusic.app) is [Google Labs](https://labs.google)' AI music generator, powered by the [Lyria 3 Pro](https://deepmind.google/models/lyria/) model (formerly Producer AI, originally [Riffusion](https://www.riffusion.com)). It creates complete, fully-produced songs — vocals, melody, and arrangement — from a text prompt, and can also cover/restyle songs, adjust lyrics in place, extend or replace sections, and apply audio effects. Flow Music signs you in with your Google account and keeps the session — including a **refresh token** — in your browser. To use the API you copy that refresh token and register it with [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts); useapi.net then calls Flow Music on your behalf and refreshes the token automatically. You can configure up to 50 Flow Music accounts per single [useapi.net subscription](/docs/subscription). ### Sign in to Flow Music * Navigate to [https://www.flowmusic.app](https://www.flowmusic.app) * Sign in with your Google account. We **strongly recommend** using a dedicated Google account for the API — not your personal Gmail. * Generate at least one song, and upload at least one image **and** one audio file. Doing each once in the browser dismisses Flow Music's first-use consent dialogs. ### Copy your refresh token 1. Open [https://www.flowmusic.app](https://www.flowmusic.app) `1` and make sure you're **signed out** (sign out if you're already signed in). 2. Open browser DevTools (**F12**) and select the **Network** tab `2`. 3. Click **Login** `3` and sign in with Google — this triggers a fresh token exchange. (The screenshot shows that button as **New session** because it changes to that once sign-in completes.) 4. In the Network filter box, type `sb.flowmusic.app/auth/v1/token` `4`. 5. Click the `token?grant_type=pkce` request that appears `5`. 6. Open the **Response** tab and copy the value of `refresh_token` `6`. ![](/assets/images/flowmusic-setup-1.jpg) ⚠️ **Important — manual setup only:** once the account is added to the API, **clear Flow Music's cookies** in your browser (or just close it and stop using Flow Music there). **Do not sign out** — signing out revokes the refresh token and disables the API account. Also don't keep browsing Flow Music there, or it will refresh the token itself and rotate it away from the API. Let the API fully manage the session from here on. The [automated setup](#automated-setup-recommended) handles all of this for you — it captures the token in a throwaway remote browser with no lingering session, so it's strongly preferred. If an account later stops working, repeat these steps and re-run [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts). ### Verify and add account Paste your API token and the `refresh_token` below to register the account — you should receive a `201` (new) or `200` (updated) response. You can also call [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts) directly.
=== URL: https://useapi.net/docs/api-flowmusic-v1/delete-flowmusic-accounts-email === Document URL: https://useapi.net/docs/api-flowmusic-v1/delete-flowmusic-accounts-email ## Remove an account June 3, 2026 --- Remove a configured Flow Music account from your useapi.net account. > **https://api.useapi.net/v1/flowmusic/accounts/`email`** - `email` is URL-encoded in the path. ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Responses **204** **204 No Content** — the account was deleted. The response body is empty. **401** **401 Unauthorized** — invalid API token. **404** **404** — account not configured. ### Examples **Curl** ``` bash curl -X DELETE -H "Authorization: Bearer YOUR_API_TOKEN" \ "https://api.useapi.net/v1/flowmusic/accounts/user@email.com" ``` **Python** ``` python import requests, urllib.parse email = 'user@email.com' r = requests.delete('https://api.useapi.net/v1/flowmusic/accounts/' + urllib.parse.quote(email, safe=''), headers={'Authorization': 'Bearer YOUR_API_TOKEN'}) print(r.status_code) # 204 No Content (empty body) ``` ### Try It
=== URL: https://useapi.net/docs/api-flowmusic-v1/delete-flowmusic-jobs-jobid === Document URL: https://useapi.net/docs/api-flowmusic-v1/delete-flowmusic-jobs-jobid ## Cancel a job June 3, 2026 --- Cancel a running async job and free its concurrency slot. The job record is marked `failed` with error code `user_cancelled` (terminal records are simply removed from the running set). > **https://api.useapi.net/v1/flowmusic/jobs/`jobid`** - `jobid` is URL-encoded in the path. ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Responses **204** **204 No Content** — cancelled (or slot already freed). No body. **401** **401 Unauthorized** — invalid API token. **403** **403** — the jobid does not belong to your account. **404** **404** — no job with that id. ### Model A successful cancel returns `204 No Content` with no body. Only the error responses (`403` / `404`) carry a JSON `{ error, code }`. ### Examples **Curl** ``` bash JOBID="job:56ef7890-...-bot:flowmusic" curl -X DELETE -H "Authorization: Bearer YOUR_API_TOKEN" \ "https://api.useapi.net/v1/flowmusic/jobs/$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=''))" "$JOBID")" ``` **Python** ``` python import requests, urllib.parse jobid = 'job:56ef7890-...-bot:flowmusic' r = requests.delete('https://api.useapi.net/v1/flowmusic/jobs/' + urllib.parse.quote(jobid, safe=''), headers={'Authorization': 'Bearer YOUR_API_TOKEN'}) print(r.status_code) # 204 ``` ### Try It
=== URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-accounts-email === Document URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-accounts-email ## Get one account June 3, 2026 --- Get the detail for a single configured account. > **https://api.useapi.net/v1/flowmusic/accounts/`email`** - `email` is the account's email address (URL-encoded in the path). ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Responses **200** **200 OK** ```json { "email": "user@email.com", "tier": "plus", "subscription_plan": "plus-monthly", "provider": "g1_entitlement", "user_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "maxJobs": 12, "added": "2026-06-01T12:00:00.000Z", "refreshed_at": "2026-06-01T12:00:00.000Z", "refreshed_count": 0, "access_token": "eyJ…redacted…zmQ", "access_token_expires_at": "2026-06-01T13:00:00.000Z", "refresh_token": "l6e…redacted…z4v", "session_id": "f0e1d2c3-b4a5-4768-9a0b-1c2d3e4f5a6b", "credits_remaining": 5000, "tokens_remaining": 60000 } ``` - `credits_remaining` and `tokens_remaining` are live-pulled from Flow Music on every request, so they are always current. They are returned only for a healthy account — an account in an error state (`error` present) omits them because its balance can't be queried. **401** **401 Unauthorized** — invalid API token. **404** **404** — account not configured. ### Model ```typescript { email: string tier: 'free' | 'starter' | 'plus' | 'member' | 'unknown' subscription_plan?: string // paid tiers only provider?: string // paid tiers only user_id: string // the account's user UUID maxJobs: number added: string // ISO 8601 timestamp refreshed_at: string // ISO 8601 timestamp refreshed_count: number access_token: string // redacted access_token_expires_at: string // ISO 8601 timestamp refresh_token: string // redacted session_id: string error?: string // present only when the account needs re-authentication credits_remaining?: number | null // live-pulled per request; present only for a healthy account tokens_remaining?: number | null // live-pulled per request; present only for a healthy account } ``` ### Examples **Curl** ``` bash curl -H "Authorization: Bearer YOUR_API_TOKEN" \ "https://api.useapi.net/v1/flowmusic/accounts/user@email.com" ``` **Python** ``` python import requests, urllib.parse email = 'user@email.com' r = requests.get('https://api.useapi.net/v1/flowmusic/accounts/' + urllib.parse.quote(email, safe=''), headers={'Authorization': 'Bearer YOUR_API_TOKEN'}) print(r.status_code, r.json()) ``` ### Try It
=== URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-accounts-usage === Document URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-accounts-usage ## Account usage June 3, 2026 --- Token and credit usage for an account — raw billing events plus per-type and per-song summaries, aggregated per UTC day. > **https://api.useapi.net/v1/flowmusic/accounts/usage?email=`email`** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Query Parameters - `email` (required) is which configured account to report on. ### Responses **200** **200 OK** ```json { "email": "user@email.com", "events": [ { "type": "producer-usage", "amount": 60, "conversation_id": "11112222-3333-4444-8555-666677778888", "conversation_title": "Sample Song", "event_at": "2026-06-01T00:00:00", "max_created_at": "2026-06-01T18:42:55.123456Z", "_account_email": "user@email.com" } ], "summary": { "by_type_tokens": { "daily-free": 1440, "riffusion": 60, "producer-usage": 1200, "grant": 7200 }, "by_type_credits": { "daily-free": 120, "riffusion": 5, "producer-usage": 100, "grant": 600 }, "total_charged_credits": 105, "total_received_tokens": 8640, "per_conversation": [ { "conversation_id": "11112222-3333-4444-8555-666677778888", "title": "Sample Song", "email": "user@email.com", "tokens": 60, "credits": 5 } ] } } ``` - `events` lists per-day usage rows, aggregated per UTC day. Each row carries a `type`, an `amount` in tokens, the `conversation_id` / `conversation_title` it belongs to, `event_at` (start of the UTC day), and `max_created_at` (the latest actual event time in that day). - `summary.by_type_tokens` and `by_type_credits` total the amounts per event type (credits are tokens / 12). - `total_charged_credits` sums the charged types (`producer-usage`, `riffusion`). `total_received_tokens` sums grants and free allowances. - `per_conversation` breaks the spend down per song. See [Flow Music pricing](https://www.flowmusic.app/pricing?plan=yearly) for plan allowances and per-generation costs. **401** **401 Unauthorized** — invalid API token. **404** **404** — account not configured. ### Model ```typescript { email: string events: Array<{ type: string // e.g. 'producer-usage' | 'riffusion' | 'daily-free' | 'grant' amount: number // tokens conversation_id: string | null conversation_title: string | null event_at: string // start of the UTC day this row is bucketed into max_created_at: string // latest actual event time in that day _account_email: string }> summary: { by_type_tokens: Record by_type_credits: Record // tokens / 12 total_charged_credits: number total_received_tokens: number per_conversation: Array<{ conversation_id: string title: string | null email: string | null tokens: number credits: number }> } } ``` ### Examples **Curl** ``` bash curl -H "Authorization: Bearer YOUR_API_TOKEN" \ "https://api.useapi.net/v1/flowmusic/accounts/usage?email=user@email.com" ``` **Python** ``` python import requests r = requests.get('https://api.useapi.net/v1/flowmusic/accounts/usage', headers={'Authorization': 'Bearer YOUR_API_TOKEN'}, params={'email': 'user@email.com'}) print(r.status_code, r.json()) ``` ### Try It
=== URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-accounts === Document URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-accounts ## List accounts June 3, 2026 --- List all Flow Music accounts configured under your useapi.net account. > **https://api.useapi.net/v1/flowmusic/accounts** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Responses **200** **200 OK** ```json { "user@email.com": { "email": "user@email.com", "tier": "plus", "subscription_plan": "plus-monthly", "provider": "g1_entitlement", "user_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "maxJobs": 12, "added": "2026-06-01T12:00:00.000Z", "refreshed_at": "2026-06-01T12:00:00.000Z", "refreshed_count": 0, "access_token": "eyJ…redacted…zmQ", "access_token_expires_at": "2026-06-01T13:00:00.000Z", "refresh_token": "l6e…redacted…z4v", "session_id": "f0e1d2c3-b4a5-4768-9a0b-1c2d3e4f5a6b" } } ``` - The response is an object keyed by account email (`{}` when no accounts are configured). - `error` is present only when an account needs re-authentication (re-run [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts)) — healthy accounts omit it. - Credit balances are omitted from the list — fetch them live per account via [GET /accounts/`email`](/docs/api-flowmusic-v1/get-flowmusic-accounts-email). **401** **401 Unauthorized** — invalid API token. ### Model An object keyed by account email (`{}` when none are configured). ```typescript { [email: string]: { email: string tier: 'free' | 'starter' | 'plus' | 'member' | 'unknown' subscription_plan?: string // paid tiers only provider?: string // paid tiers only user_id: string // the account's user UUID maxJobs: number added: string // ISO 8601 timestamp refreshed_at: string // ISO 8601 timestamp refreshed_count: number access_token: string // redacted access_token_expires_at: string // ISO 8601 timestamp refresh_token: string // redacted session_id: string error?: string // present only when the account needs re-authentication } } ``` ### Examples **Curl** ``` bash curl -H "Authorization: Bearer YOUR_API_TOKEN" \ "https://api.useapi.net/v1/flowmusic/accounts" ``` **Python** ``` python import requests r = requests.get('https://api.useapi.net/v1/flowmusic/accounts', headers={'Authorization': 'Bearer YOUR_API_TOKEN'}) print(r.status_code, r.json()) ``` ### Try It
=== URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-jobs-jobid === Document URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-jobs-jobid ## Poll a job June 3, 2026 --- Fetch one job record by its `jobid`. Use this to poll an [async](/docs/api-flowmusic-v1/post-flowmusic-music) generation until `status` is `completed` or `failed`. > **https://api.useapi.net/v1/flowmusic/jobs/`jobid`** - `jobid` is URL-encoded in the path (it contains `:` and `@`). ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Responses **200** **200 OK** ```json { "jobid": "job:56ef7890-...-bot:flowmusic", "email": "user@email.com", "request": { "prompt": "...", "mode": "async" }, "created_at": "2026-05-31T19:57:23.954Z", "completed_at": "2026-05-31T19:59:43.118Z", "duration_ms": 139164, "status": "completed", "clips": [ { "clip": "user:1234-...-clip:...", "duration_s": 173.03, "image_url": "..." } ] } ``` While still running, only `status: "pending"` and the dispatch fields are present (no `clips`). **401** **401 Unauthorized** — invalid API token. **403** **403** — the jobid does not belong to your account. **404** **404** — no job with that id (expired or never existed). ### Model The job record — the same shape returned by [POST /music](/docs/api-flowmusic-v1/post-flowmusic-music) and [POST /music/edit](/docs/api-flowmusic-v1/post-flowmusic-music-edit). `operation` is present only for edit jobs, `clips` appears once `completed`, and `error` appears when `failed`. ```typescript { jobid: string // job:-user:--bot:flowmusic email: string operation?: 'effect' | 'extend' | 'replace' | 'cover' | 'remix' // edit jobs only status: 'completed' | 'pending' | 'failed' created_at: string // ISO 8601 timestamp completed_at?: string // present when terminal duration_ms?: number // wall-clock generation time clips?: Array<{ // present once completed clip: string // encoded clip asset id — use with /music/download and /music/edit title: string | null duration_s: number | null lyrics: string | null seed: number | null lyrics_timing: Array<[number, number]> | null // timing markers, [start, end] seconds (vocal clips) created_at: string audio_url: string wav_url: string image_url: string | null }> error?: { // present when status is 'failed' code: string message: string } request: object // the original request body, echoed back verbatim } ``` ### Examples **Curl** ``` bash JOBID="job:56ef7890-...-bot:flowmusic" curl -H "Authorization: Bearer YOUR_API_TOKEN" \ "https://api.useapi.net/v1/flowmusic/jobs/$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=''))" "$JOBID")" ``` **Python** ``` python import requests, urllib.parse jobid = 'job:56ef7890-...-bot:flowmusic' r = requests.get('https://api.useapi.net/v1/flowmusic/jobs/' + urllib.parse.quote(jobid, safe=''), headers={'Authorization': 'Bearer YOUR_API_TOKEN'}) print(r.status_code, r.json()) ``` ### Try It
=== URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-jobs === Document URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-jobs ## List running jobs June 3, 2026 --- List the currently-running async jobs, grouped by account email. Useful for tracking concurrency against each account's `maxJobs` limit. > **https://api.useapi.net/v1/flowmusic/jobs** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Responses **200** **200 OK** ```json { "user@email.com": { "running": 1, "maxJobs": 12, "jobs": [ { "jobid": "job:56ef7890-...-bot:flowmusic", "status": "pending", "created_at": "2026-05-31T19:57:23.954Z" } ] } } ``` An empty object `{}` means nothing is running. **401** **401 Unauthorized** — invalid API token. ### Model An object keyed by account email (`{}` when nothing is running). ```typescript { [email: string]: { running: number maxJobs: number jobs: Array<{ jobid: string status: 'pending' | 'completed' | 'failed' operation?: 'effect' | 'extend' | 'replace' | 'cover' | 'remix' // edit jobs only created_at: string // ISO 8601 timestamp }> } } ``` ### Examples **Curl** ``` bash curl -H "Authorization: Bearer YOUR_API_TOKEN" \ "https://api.useapi.net/v1/flowmusic/jobs" ``` **Python** ``` python import requests r = requests.get('https://api.useapi.net/v1/flowmusic/jobs', headers={'Authorization': 'Bearer YOUR_API_TOKEN'}) print(r.status_code, r.json()) ``` ### Try It
=== URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-music-download === Document URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-music-download ## Download raw mp3 audio June 3, 2026 --- Download a generated clip as raw `mp3` audio — the response body is the audio bytes. The full-quality **m4a** and **wav** are public URLs already on every clip, so they aren't re-served here: read `audio_url` / `wav_url` from [GET /music](/docs/api-flowmusic-v1/get-flowmusic-music) (or the job record) and download those directly. > **https://api.useapi.net/v1/flowmusic/music/download** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Query Parameters - `id` is **required**, an encoded clip asset ID (`user:--clip:`) or a completed jobid (resolves to the job's first clip). - `format` is optional, `mp3` (the default and only value). > **Stems are separate clips.** When you separate a song's stems in Flow Music it creates one clip per track — they show up in [GET /music](/docs/api-flowmusic-v1/get-flowmusic-music) as `" - drums"`, `"- bass"`, `"- vocals"`, `"- other"`. Download each one here with its own `id` (or grab its `audio_url`/`wav_url`). ### Responses **200** **200 OK** — the raw `audio/mpeg` bytes, with a `Content-Disposition` filename (`<clip>.mp3`). **400** **400** — `id` is not a valid clip ID or jobid, or `format` is unsupported. ```json { "error": "Parameter id not a valid clip ID or jobid", "code": 400 } ``` **401** **401 Unauthorized** — invalid API token. **409** **409** — a jobid was given but the job is not completed yet. **596** **596 Account Error** The account that owns the requested clip is in an error state and can't be used — re-add it via [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts). The `error` field returns whatever reason was recorded for the account. ```json { "error": "Account user@email.com in error state: refresh token rejected", "code": 596 } ``` ### Model This endpoint streams raw `audio/mpeg` bytes (not JSON), with a `Content-Disposition` filename. Only the error responses (`400` / `409`) return JSON `{ error, code }`. ### Examples **Curl** ``` bash # Any clip — including a separated stem clip — downloads as mp3 by its id curl -H "Authorization: Bearer YOUR_API_TOKEN" \ "https://api.useapi.net/v1/flowmusic/music/download?id=user:1234-user@email.com-clip:12ab34cd-...&format=mp3" \ --output song.mp3 ``` **Python** ``` python import requests, urllib.parse clip = 'user:1234-user@email.com-clip:12ab34cd-...' url = 'https://api.useapi.net/v1/flowmusic/music/download?id=' + urllib.parse.quote(clip) + '&format=mp3' r = requests.get(url, headers={'Authorization': 'Bearer YOUR_API_TOKEN'}) open('song.mp3', 'wb').write(r.content) ``` ### Try It This endpoint returns raw audio bytes (not JSON), so the form below fetches the file with your token and plays it inline. <script> async function apiTestDownloadFlowMusic() { const id = document.getElementById('get-music-download-FlowMusic-id')?.value; const data = { method: 'GET', headers: { 'Authorization': `Bearer ${document.getElementById('get-music-download-FlowMusic-token').value}` } }; const url = `https://api.useapi.net/v1/flowmusic/music/download?id=${encodeURIComponent(id)}&format=mp3`; await apiExecute(url, 'get-music-download-FlowMusic', data, { expectMedia: true }); } </script> <form id="get-music-download-FlowMusic" onsubmit="return false;" class="input-form"> <div class="input-field"> <div class="input-field-row"> <label for="get-music-download-FlowMusic-token"><span>API Token<small> (see <a href="/docs/start-here/setup-useapi">Setup useapi.net</a>)</small></span> <input id="get-music-download-FlowMusic-token" type="text" class="input-element" required aria-required="true" oninput="this.size = this.value.length"> </label> <label for="get-music-download-FlowMusic-id"><span>id<small> (encoded clip id or jobid)</small></span> <input id="get-music-download-FlowMusic-id" type="text" class="input-element" required aria-required="true" oninput="this.size = this.value.length"> </label> </div> <button id="get-music-download-FlowMusic-button" class="btn btn-primary fs-3" type="submit" onclick="apiTestDownloadFlowMusic()">Go</button> </div> </form> <div id="get-music-download-FlowMusic-response" class="response-placeholder visible-false"></div> === URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-music === Document URL: https://useapi.net/docs/api-flowmusic-v1/get-flowmusic-music ## List clips <small>June 3, 2026</small> --- List the clips generated on a Flow Music account, most recent first. Paginated by `offset`. > **https://api.useapi.net/v1/flowmusic/music** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Query Parameters - `email` is optional, which configured account to list. Defaults to an available account. - `limit` is optional, the page size, 1–100 (default 20). - `offset` is optional, the page offset, ≥ 0 (default 0). ### Responses **200** **200 OK** ```json { "email": "user@email.com", "clips": [ { "clip": "user:1234-user@email.com-clip:12ab34cd-...", "title": "Morning Sun", "lyrics": "[Verse 1]\n...", "duration_s": 173.03, "image_url": "https://storage.googleapis.com/..." } ], "limit": 20, "offset": 0, "next": 20 } ``` - `next` is the `offset` to request for the following page, or `null` when this was the last page. **401** **401 Unauthorized** — invalid API token. **596** **596 Account Error** The account targeted by `email` is in an error state and can't be used — re-add it via [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts). The `error` field returns whatever reason was recorded for the account. ```json { "error": "Account user@email.com in error state: refresh token rejected", "code": 596 } ``` ### Model ```typescript { email: string clips: Array<{ clip: string // encoded clip asset id — use with /music/download and /music/edit title: string | null lyrics: string | null duration_s: number | null image_url: string | null }> limit: number offset: number next: number | null // offset for the next page, or null when this was the last page } ``` ### Examples **Curl** ``` bash curl -H "Authorization: Bearer YOUR_API_TOKEN" \ "https://api.useapi.net/v1/flowmusic/music?limit=10&offset=0" ``` **Python** ``` python import requests response = requests.get( 'https://api.useapi.net/v1/flowmusic/music', headers={'Authorization': 'Bearer YOUR_API_TOKEN'}, params={'limit': 10, 'offset': 0} ) print(response.status_code, response.json()) ``` ### Try It <script> async function apiTestGetMusicFlowMusic() { const data = { method: 'GET', headers: { 'Authorization': `Bearer ${document.getElementById('get-music-FlowMusic-token').value}` } }; let email = document.getElementById(`get-music-FlowMusic-email`)?.value; let limit = document.getElementById(`get-music-FlowMusic-limit`)?.value; let offset = document.getElementById(`get-music-FlowMusic-offset`)?.value; const qs = new URLSearchParams(); if (email) qs.set('email', email); if (limit) qs.set('limit', limit); if (offset) qs.set('offset', offset); const url = `https://api.useapi.net/v1/flowmusic/music` + (qs.toString() ? `?${qs.toString()}` : ''); await apiExecute(url, 'get-music-FlowMusic', data); } </script> <form id="get-music-FlowMusic" onsubmit="return false;" class="input-form"> <div class="input-field"> <div class="input-field-row"> <label for="get-music-FlowMusic-token"><span>API Token<small> (see <a href="/docs/start-here/setup-useapi">Setup useapi.net</a>)</small></span> <input id="get-music-FlowMusic-token" type="text" class="input-element" required aria-required="true" oninput="this.size = this.value.length"> </label> <label for="get-music-FlowMusic-email"><span>email<small> (optional)</small></span> <input id="get-music-FlowMusic-email" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="get-music-FlowMusic-limit"><span>limit<small> (optional, 1-100)</small></span> <input id="get-music-FlowMusic-limit" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="get-music-FlowMusic-offset"><span>offset<small> (optional, >=0)</small></span> <input id="get-music-FlowMusic-offset" type="text" class="input-element" oninput="this.size = this.value.length"> </label> </div> <button id="get-music-FlowMusic-button" class="btn btn-primary fs-3" type="submit" onclick="apiTestGetMusicFlowMusic()">Go</button> </div> </form> <div id="get-music-FlowMusic-response" class="response-placeholder visible-false"></div> === URL: https://useapi.net/docs/api-flowmusic-v1 === Document URL: https://useapi.net/docs/api-flowmusic-v1 # <img style="height:1em;vertical-align: middle;" src="/assets/images/google-flow-music-logo.jpg"> FlowMusic API v1 <small>June 3, 2026</small> This is the [experimental](/docs/legal) API for [Google Flow Music](https://www.flowmusic.app) by [Google Labs](https://labs.google), powered by the [Lyria 3 Pro](https://deepmind.google/models/lyria/) music model. Flow Music was formerly Producer AI, and originally [Riffusion](https://www.riffusion.com). [Flow Music](https://www.flowmusic.app) turns a text prompt, mood, or lyric idea into a complete, fully-produced song — vocals, melody, and arrangement — using [Lyria 3 Pro](https://deepmind.google/models/lyria/). It also covers (restyles) existing songs, adjusts lyrics in place, extends or replaces sections, and applies audio effects. Each generation returns up to two clips (an A/B pair). Sign in to [flowmusic.app](https://www.flowmusic.app) with your Google account. See the [Flow Music pricing page](https://www.flowmusic.app/pricing) for plans and credit allowances. <details markdown="block"> <summary><b>💲 Cost calculator</b></summary> Each generation costs **~5 credits** and returns up to two clips (an A/B pair). All plans include daily top-up credits, so even Free generates a little each day. **Max concurrent** is how many generations the API runs at once per account (the `maxJobs` cap); monthly credits don't roll over. | Plan | Price | Credits / mo | ~Songs / mo | ~Cost / song | Max concurrent | |---|---|---|---|---|---| | Free | $0 | daily top-up only | — | free daily credits | 2 | | Starter | $8 / mo | 3,000 | ~600 | ~$0.013 | 8 | | Plus | $24 / mo | 10,000 | ~2,000 | ~$0.012 | 12 | | Member | $64 / mo | 30,000 | ~6,000 | ~$0.011 | 16 | </details> You can configure up to 50 Flow Music accounts per single [useapi.net account](/docs/subscription). ⚙️ [Setup Flow Music](/docs/start-here/setup-flowmusic) 📦 [Postman collection](https://www.postman.com/useapinet/useapi-net/collection) 🤖 [LLM-friendly API spec](https://useapi.net/assets/aibot/api-flowmusic-v1.txt) <small>Feed this to your LLM to build integrations</small> Developer Community: * <a href="https://discord.gg/w28uK3cnmF"><img style="height:1em;vertical-align: middle;" src="/assets/images/discord.svg"> Discord Server</a> * <a href="https://t.me/use_api"><img style="height:1.3em;vertical-align: middle;" src="/assets/images/telegram.svg"> Telegram Channel</a> === URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-accounts === Document URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-accounts ## Configure a Flow Music account <small>June 3, 2026</small> --- Configure a [Flow Music](https://www.flowmusic.app) account for API access using its refresh token. See [Setup Flow Music](/docs/start-here/setup-flowmusic) for how to obtain the token. You can configure up to 50 Flow Music accounts per single [useapi.net account](/docs/subscription). > **https://api.useapi.net/v1/flowmusic/accounts** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json # Alternatively you can use multipart/form-data # Content-Type: multipart/form-data ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Request Body ```json { "refresh_token": "bdykxtrr5eaf...", "maxJobs": 12 } ``` - `refresh_token` is **required**, the flowmusic.app refresh token (≥10 chars). See [Setup Flow Music](/docs/start-here/setup-flowmusic). - `maxJobs` is optional, the max concurrent jobs for this account, 1–16. ### Responses **200** **200 OK** — existing account updated. ```json { "email": "user@email.com", "tier": "plus", "subscription_plan": "plus-monthly", "provider": "g1_entitlement", "user_id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "maxJobs": 12, "added": "2026-06-01T12:00:00.000Z", "refreshed_at": "2026-06-01T12:00:00.000Z", "refreshed_count": 0, "access_token": "eyJ…redacted…zmQ", "access_token_expires_at": "2026-06-01T13:00:00.000Z", "refresh_token": "l6e…redacted…z4v", "session_id": "f0e1d2c3-b4a5-4768-9a0b-1c2d3e4f5a6b" } ``` - Credit balances are not included here — fetch them live from [GET /accounts/`email`](/docs/api-flowmusic-v1/get-flowmusic-accounts-email). **201** **201 Created** — new account configured. Same body as `200`. **400** **400** — missing/invalid `refresh_token`, or the token failed to refresh. ```json { "error": "Parameter refresh_token is required", "code": 400 } ``` **401** **401 Unauthorized** — invalid API token. ### Model `200` (updated) and `201` (created) return the same account record. ```typescript { email: string tier: 'free' | 'starter' | 'plus' | 'member' | 'unknown' subscription_plan?: string // paid tiers only provider?: string // paid tiers only user_id: string // the account's user UUID maxJobs: number added: string // ISO 8601 timestamp refreshed_at: string // ISO 8601 timestamp refreshed_count: number access_token: string // redacted access_token_expires_at: string // ISO 8601 timestamp refresh_token: string // redacted session_id: string error?: string // present only when the account needs re-authentication } ``` ### Examples **Curl** ``` bash curl -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -X POST "https://api.useapi.net/v1/flowmusic/accounts" \ -d '{ "refresh_token": "bdykxtrr5eaf...", "maxJobs": 12 }' ``` **Python** ``` python import requests response = requests.post( 'https://api.useapi.net/v1/flowmusic/accounts', headers={'Authorization': 'Bearer YOUR_API_TOKEN'}, json={'refresh_token': 'bdykxtrr5eaf...', 'maxJobs': 12} ) print(response.status_code, response.json()) ``` ### Try It <script> async function apiTestPostAccountsFlowMusic() { const refresh_token = document.getElementById('post-accounts-FlowMusic-refresh_token')?.value; const maxJobs = document.getElementById('post-accounts-FlowMusic-maxJobs')?.value; const payload = {}; if (refresh_token) payload.refresh_token = refresh_token; if (maxJobs) payload.maxJobs = parseInt(maxJobs); const data = { method: 'POST', headers: { 'Authorization': `Bearer ${document.getElementById('post-accounts-FlowMusic-token').value}`, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }; await apiExecute(`https://api.useapi.net/v1/flowmusic/accounts`, 'post-accounts-FlowMusic', data); } </script> <form id="post-accounts-FlowMusic" onsubmit="return false;" class="input-form"> <div class="input-field"> <div class="input-field-row"> <label for="post-accounts-FlowMusic-token"><span>API Token<small> (see <a href="/docs/start-here/setup-useapi">Setup useapi.net</a>)</small></span> <input id="post-accounts-FlowMusic-token" type="text" class="input-element" required aria-required="true" oninput="this.size = this.value.length"> </label> <label for="post-accounts-FlowMusic-refresh_token"><span>refresh_token<small> (see <a href="/docs/start-here/setup-flowmusic">Setup Flow Music</a>)</small></span> <input id="post-accounts-FlowMusic-refresh_token" type="text" class="input-element" required aria-required="true" oninput="this.size = this.value.length"> </label> <label for="post-accounts-FlowMusic-maxJobs"><span>maxJobs<small> (optional, 1–16)</small></span> <input id="post-accounts-FlowMusic-maxJobs" type="number" class="input-element" min="1" max="16" oninput="this.size = this.value.length"> </label> </div> <button id="post-accounts-FlowMusic-button" class="btn btn-primary fs-3" type="submit" onclick="apiTestPostAccountsFlowMusic()">Go</button> </div> </form> <div id="post-accounts-FlowMusic-response" class="response-placeholder visible-false"></div> === URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-files-generate === Document URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-files-generate ## Generate an AI image <small>June 3, 2026</small> --- Generate an AI image from a text prompt. Returns an encoded image asset ID — the same shape [POST /files](/docs/api-flowmusic-v1/post-flowmusic-files) returns for an uploaded image — that you can pass as a reference image (`refImage1…`) to [POST /music](/docs/api-flowmusic-v1/post-flowmusic-music). > **https://api.useapi.net/v1/flowmusic/files/generate** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json # Alternatively you can use multipart/form-data # Content-Type: multipart/form-data ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Request Body ```json { "prompt": "abstract neon city skyline at night" } ``` - `email` is optional, which configured account to use. - `prompt` is **required**, the image description. 1–2,000 characters. ### Responses **200** **200 OK** ```json { "id": "user:1234-user@email.com-image:...", "url": "https://storage.googleapis.com/producer-app-public/assets/....jpg", "kind": "image", "assetType": "image", "email": "user@email.com", "width": 1024, "height": 1024, "model": "google-generative-ai" } ``` **400** **400** — missing/empty prompt. **401** **401 Unauthorized** — invalid API token. **500** **500** — Flow Music declined the request. The most common cause is a moderated or otherwise disallowed prompt: Flow Music returns a generic `Internal Server Error` (it does not say why) and we pass it through verbatim. Retry with a different prompt — if a clearly benign prompt still fails, it is likely a transient upstream error. ```json { "error": "FlowMusic image generation failed (500): \"Internal Server Error\"", "code": 500 } ``` **596** **596 Account Error** The account targeted by `email` is in an error state and can't be used — re-add it via [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts). The `error` field returns whatever reason was recorded for the account. ```json { "error": "Account user@email.com in error state: refresh token rejected", "code": 596 } ``` ### Model ```typescript { id: string // encoded image asset id — usable as refImage1… in POST /music url: string kind: 'image' assetType: 'image' email: string width: number height: number model: string // the image model, e.g. 'google-generative-ai' } ``` ### Examples **Curl** ``` bash curl -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -X POST "https://api.useapi.net/v1/flowmusic/files/generate" \ -d '{ "prompt": "abstract neon city skyline at night" }' ``` **Python** ``` python import requests response = requests.post( 'https://api.useapi.net/v1/flowmusic/files/generate', headers={'Authorization': 'Bearer YOUR_API_TOKEN'}, json={'prompt': 'abstract neon city skyline at night'} ) print(response.status_code, response.json()) ``` ### Try It <script> function displayFlowMusicImage(response, elementId) { const container = document.getElementById(`${elementId}-media-links`); if (!container) return; container.innerHTML = ''; if (!response.url) { container.style.display = 'none'; return; } const u = response.url; container.innerHTML = `<div style="margin:6px 0;"><a href="${u}" target="_blank" rel="noopener noreferrer" title="Open full image in a new tab"><img src="${u}" alt="generated image" style="max-width:256px;border-radius:6px;cursor:zoom-in;"></a></div>`; container.style.display = 'block'; } async function apiTestFilesGenerateFlowMusic() { const elementId = 'post-files-generate-FlowMusic'; const data = { method: 'POST', headers: { 'Authorization': `Bearer ${document.getElementById('post-files-generate-FlowMusic-token').value}`, 'Content-Type': 'application/json' } }; let email = document.getElementById(`post-files-generate-FlowMusic-email`)?.value; let prompt = document.getElementById(`post-files-generate-FlowMusic-prompt`)?.value; const url = `https://api.useapi.net/v1/flowmusic/files/generate`; data.body = JSON.stringify({ ...(email ? { email } : {}), prompt }); const mediaLinks = document.getElementById(`${elementId}-media-links`); if (mediaLinks) mediaLinks.style.display = 'none'; const status = await apiExecute(url, elementId, data, { includeTimer: true }); if (status === 200) { try { const response = JSON.parse(document.getElementById(`${elementId}-response-json`).innerText); displayFlowMusicImage(response, elementId); } catch (e) { console.error('Failed to parse response for image display:', e); } } } </script> <form id="post-files-generate-FlowMusic" onsubmit="return false;" class="input-form"> <div class="input-field"> <div class="input-field-row"> <label for="post-files-generate-FlowMusic-token"><span>API Token<small> (see <a href="/docs/start-here/setup-useapi">Setup useapi.net</a>)</small></span> <input id="post-files-generate-FlowMusic-token" type="text" class="input-element" required aria-required="true" oninput="this.size = this.value.length"> </label> <label for="post-files-generate-FlowMusic-email"><span>email<small> (optional)</small></span> <input id="post-files-generate-FlowMusic-email" type="text" class="input-element" placeholder="user@email.com"> </label> <label for="post-files-generate-FlowMusic-prompt"><span>prompt</span> <textarea id="post-files-generate-FlowMusic-prompt" class="input-element" rows="3" required aria-required="true" style="resize: both;"></textarea> </label> </div> <button id="post-files-generate-FlowMusic-button" class="btn btn-primary fs-3" type="submit" onclick="apiTestFilesGenerateFlowMusic()">Go</button> </div> </form> <div id="post-files-generate-FlowMusic-media-links" style="display:none;"></div> <div id="post-files-generate-FlowMusic-response" class="response-placeholder visible-false"></div> === URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-files === Document URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-files ## Upload a file <small>June 3, 2026</small> --- Upload a reference image or audio file. The file is sent as the **raw request body**, with its MIME type in the `Content-Type` header (no multipart, no form fields). Returns an encoded asset ID you can pass to [POST /music](/docs/api-flowmusic-v1/post-flowmusic-music) (`refImage1…`/`refAudio1…`). > **https://api.useapi.net/v1/flowmusic/files** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: {file MIME type} # see supported types below ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. The request body **is** the file's raw bytes. | Content-Type | File Extension | Max size | | ------------ | -------------- | -------- | | image/jpeg | jpg | 20 MB | | image/png | png | 20 MB | | image/webp | webp | 20 MB | | audio/mpeg | mp3 | 40 MB | | audio/wav | wav | 40 MB | | audio/mp4 | m4a | 40 MB | | audio/x-m4a | m4a | 40 MB | | audio/aac | m4a | 40 MB | ### Query Parameters - `email` is optional, which configured account to use. - `stripMetadata` is optional, default `true` — strips AI-provenance / EXIF metadata from images before upload (Flow Music's edge rejects images that declare AI-generated provenance). Set `false` to keep the original bytes. ### Responses **200** **200 OK** ```json { "id": "user:1234-user@email.com-image:34cd56ef-...", "url": "https://storage.googleapis.com/...", "kind": "image", "assetType": "image", "email": "user@email.com", "metadata_stripped": true } ``` Audio uploads also include `clip_id`, `operation_id`, `is_flagged`, and `duration_s` when available. **400** **400** — unsupported Content-Type, empty body, or magic-byte mismatch (declared type ≠ actual bytes). ```json { "error": "Content-Type (application/octet-stream) not supported. Valid values: image/jpeg, image/png, ...", "code": 400 } ``` **401** **401 Unauthorized** — invalid API token. **413** **413** — file exceeds the size cap. **422** **422** — image declares AI-generated provenance metadata and `stripMetadata=false` was set. Remove the flag (default strips it) or re-save the image without metadata. **500** **500** — FlowMusic rejected the upload or hit an internal error. Image moderation (e.g. NSFW) surfaces here as a `flowmusic_error`. The `upstream` block preserves the evidence — quote `cf_ray` to support. ```json { "error": "Upload failed (HTTP 500): FlowMusic: \"Internal Server Error\"", "code": 500, "upstream": { "status": 500, "cause": "flowmusic_error", "cf_ray": "a05b815ef02cf9cc-SJC", "server": "cloudflare", "content_type": "application/json" } } ``` **596** **596 Account Error** The account targeted by `email` is in an error state and can't be used — re-add it via [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts). The `error` field returns whatever reason was recorded for the account. ```json { "error": "Account user@email.com in error state: refresh token rejected", "code": 596 } ``` ### Model ```typescript { id: string // encoded asset id — user:<id>-<email>-<image|audio>:<uuid> url: string // Flow Music storage URL kind: 'image' | 'audio' assetType: 'image' | 'audio' email: string metadata_stripped?: boolean // images only — whether AI-provenance metadata was stripped clip_id?: string | null // audio only operation_id?: string | null // audio only is_flagged?: boolean | null // audio only duration_s?: number | null // audio only } ``` ### Examples **Curl** ``` bash curl -H "Authorization: Bearer YOUR_API_TOKEN" \ -H "Content-Type: image/png" \ -X POST "https://api.useapi.net/v1/flowmusic/files" \ --data-binary "@image.png" ``` **JavaScript** ``` javascript const bytes = await (await fetch('image.png')).arrayBuffer(); const response = await fetch('https://api.useapi.net/v1/flowmusic/files', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_TOKEN', 'Content-Type': 'image/png' }, body: bytes }); console.log(await response.json()); ``` **Python** ``` python import requests data = open('image.png', 'rb').read() response = requests.post( 'https://api.useapi.net/v1/flowmusic/files', headers={'Authorization': 'Bearer YOUR_API_TOKEN', 'Content-Type': 'image/png'}, data=data ) print(response.status_code, response.json()) ``` ### Try It <script> async function apiTestFilesFlowMusic() { const responseEl = document.getElementById('post-files-FlowMusic-response'); const file = document.getElementById('post-files-FlowMusic-file')?.files?.[0]; if (!file) { responseEl.classList.remove('visible-false'); responseEl.classList.add('visible-true'); responseEl.innerHTML = '<div class="response-status http-status-bad">Choose a file first</div>'; return; } const email = document.getElementById('post-files-FlowMusic-email')?.value; const stripMetadata = document.getElementById('post-files-FlowMusic-stripMetadata')?.value; const qs = new URLSearchParams(); if (email) qs.set('email', email); if (stripMetadata) qs.set('stripMetadata', stripMetadata); const url = `https://api.useapi.net/v1/flowmusic/files` + (qs.toString() ? `?${qs.toString()}` : ''); const data = { method: 'POST', headers: { 'Authorization': `Bearer ${document.getElementById('post-files-FlowMusic-token').value}`, 'Content-Type': file.type }, body: file }; await apiExecute(url, 'post-files-FlowMusic', data); } </script> <form id="post-files-FlowMusic" onsubmit="return false;" class="input-form"> <div class="input-field"> <div class="input-field-row"> <label for="post-files-FlowMusic-token"><span>API Token<small> (see <a href="/docs/start-here/setup-useapi">Setup useapi.net</a>)</small></span> <input id="post-files-FlowMusic-token" type="text" class="input-element" required aria-required="true" oninput="this.size = this.value.length"> </label> <label for="post-files-FlowMusic-email"><span>email<small> (optional)</small></span> <input id="post-files-FlowMusic-email" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-files-FlowMusic-stripMetadata"><span>stripMetadata<small> (optional)</small></span> <select id="post-files-FlowMusic-stripMetadata" class="input-element"> <option value="">default (true)</option> <option value="false">false — keep original bytes</option> </select> </label> <label for="post-files-FlowMusic-file"><span>file<small> (image or audio)</small></span> <input id="post-files-FlowMusic-file" type="file" class="input-element" accept="image/jpeg,image/png,image/webp,audio/mpeg,audio/wav,audio/mp4,audio/aac" required aria-required="true"> </label> </div> <button id="post-files-FlowMusic-button" class="btn btn-primary fs-3" type="submit" onclick="apiTestFilesFlowMusic()">Go</button> </div> </form> <div id="post-files-FlowMusic-response" class="response-placeholder visible-false"></div> === URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-music-edit === Document URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-music-edit ## Edit a song <small>June 3, 2026</small> --- Edit an existing clip — the `operation` field selects one of six edit kinds, each taking a `clip` (an encoded clip asset ID from a previous generation or [GET /music](/docs/api-flowmusic-v1/get-flowmusic-music)). `operation` is **required** — one of: | `operation` | What it does | Required params | Optional params | | ----------- | ------------ | --------------- | --------------- | | `effect` | Apply one audio effect | `effect` + that effect's scalar param(s) | — | | `extend` | Add new music to the end, or from a point | `instruction` | `extend_s`, `extend_from_s` | | `replace` | Regenerate a time section | `instruction`, `mask_start_s`, `mask_end_s` | — | | `cover` | Restyle the whole song (a Lyria 3 Pro cover) | — | `instruction`, `strength` | | `remix` | Re-sing with edited lyrics | `lyrics` | `prompt` | | `stems` | Separate into 4 stem clips (drums/bass/vocals/other) | — | — | > **https://api.useapi.net/v1/flowmusic/music/edit** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json # Alternatively you can use multipart/form-data # Content-Type: multipart/form-data ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Request Body Every operation accepts these common fields (there is no `email` — the `clip` already identifies its account, and the edit runs there): - `clip` is **required**, the encoded clip asset ID to edit. - `seed` is optional, an integer ≥ 0 — pins the result for reproducibility. - `title` is optional, a title for the result clip. - `mode` is optional, `sync` (default — blocks until ready) or `async` (returns `201` with a `jobid`; poll [GET /jobs/`jobid`](/docs/api-flowmusic-v1/get-flowmusic-jobs-jobid)). Ignored by `stems`, which is always sync. - `replyUrl` is optional, a webhook URL — the job record is POSTed to it when the edit completes or fails. Ignored by `stems`. - `replyRef` is optional, a custom reference string echoed back in the webhook. Plus the chosen operation's own fields, below. #### operation: `effect` `effect` is one of `Trim`, `Fade`, `Gain`, `Reverb`, `Pan`, `Equalizer`. Each effect reads only its own flat scalar params (sending a param for a different effect is ignored): - `Gain` — `gain_db` (−30 … 30). - `Reverb` — `mix`, `room_size` (0 … 1 each). - `Pan` — `pan` (−1 left … 1 right). - `Equalizer` — `bass_db`, `mid_db`, `treble_db` (−12 … 12 each). - `Fade` — `fade_in_s`, `fade_out_s` (≥ 0, at least one > 0). - `Trim` — `start_s`, `end_s` (`end_s` > `start_s`, ≤ duration). ```json { "operation": "effect", "clip": "user:1234-user@email.com-clip:...", "effect": "Reverb", "mix": 0.4, "room_size": 0.7 } ``` #### operation: `extend` ```json { "operation": "extend", "clip": "user:1234-...-clip:...", "instruction": "add an instrumental outro", "extend_s": 30 } ``` - `instruction` is **required**, what to add. - `extend_s` is optional, seconds to add, 1–300 (default 150). - `extend_from_s` is optional, the point to extend from (default = end of clip). #### operation: `replace` ```json { "operation": "replace", "clip": "user:1234-...-clip:...", "instruction": "softer piano in this section", "mask_start_s": 0, "mask_end_s": 8 } ``` - `instruction` is **required**, how to regenerate the section. - `mask_start_s` is **required**, the section start (seconds). - `mask_end_s` is **required**, the section end (seconds), > `mask_start_s`, ≤ duration. #### operation: `cover` Restyle the entire song. `instruction` is **optional** (a free-text style direction); `strength` is the cover-strength slider. ```json { "operation": "cover", "clip": "user:1234-...-clip:...", "instruction": "lo-fi chillhop reinterpretation", "strength": 0.7 } ``` - `instruction` is optional, a free-text style direction. - `strength` is optional, 0 … 1 (default 1). #### operation: `remix` Re-sing the song with **edited lyrics**. By default it reuses the source song's sound prompt; override with `prompt`. The edited lyrics round-trip into the result clip's `lyrics`. ```json { "operation": "remix", "clip": "user:1234-...-clip:...", "lyrics": "[Verse 1]\nNeon rain on the avenue\n[Chorus]\nWe rise again", "prompt": "dreamy synthwave, 100 bpm" } ``` - `lyrics` is **required**, the edited lyrics, `[Verse]`/`[Chorus]`-tagged. - `prompt` is optional, a sound-prompt override (defaults to the source clip's). #### operation: `stems` Separate the song into its individual tracks. Flow Music runs the split server-side and creates **4 new clips** — `drums`, `bass`, `vocals`, `other` — each a normal clip with its own id and a public `audio_url`/`wav_url`. Only `clip` is needed. ```json { "operation": "stems", "clip": "user:1234-...-clip:..." } ``` - `stems` is **synchronous** — the request blocks ~15–60 s while the split runs, then returns all four stems in one reply (it ignores `mode`/`replyUrl`/`replyRef`). Re-running on an already-split clip returns its stems immediately. - Download any stem with [GET /music/download](/docs/api-flowmusic-v1/get-flowmusic-music-download) (mp3) or its `audio_url`/`wav_url`. Its response differs from the other edits — it's a flat manifest of the stem clips, not a job record: ```json { "email": "user@email.com", "operation": "stems", "source_clip": "user:1234-...-clip:...", "status": "completed", "clips": [ { "stem_type": "drums", "clip": "user:1234-...-clip:...", "title": "Song - drums", "duration_s": 173.0, "audio_url": "https://storage.googleapis.com/...1a2b3c4d.m4a", "wav_url": "https://storage.googleapis.com/...1a2b3c4d.wav", "image_url": null } ] } ``` (four entries — `drums`, `bass`, `vocals`, `other`). ### Responses Same job-record shape as [POST /music](/docs/api-flowmusic-v1/post-flowmusic-music) — `operation` echoes the requested edit kind. (`operation: stems` is the exception — it returns the flat stem manifest shown above, not a job record.) **200** **200 OK** **Sync mode** (default) — the edit is rendered and the full record is returned inline. ```json { "jobid": "job:aaaabbbb-cccc-4ddd-8eee-ffff99990000-user:1234-user@email.com-bot:flowmusic", "email": "user@email.com", "operation": "cover", "status": "completed", "created_at": "2026-06-01T18:40:42.412Z", "completed_at": "2026-06-01T18:41:37.432Z", "duration_ms": 55020, "clips": [ { "clip": "user:1234-user@email.com-clip:c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f", "title": "Morning Sun (Cover)", "duration_s": 171.02, "lyrics": "[Verse 1]\nCity lights blur in the rain\n[Chorus]\nAnd I will wait for the morning sun", "seed": 123456, "lyrics_timing": [ [0.0, 2.4], [2.4, 5.1] ], "created_at": "2026-06-01T18:41:30.118Z", "audio_url": "https://storage.googleapis.com/...c3d4e5f6.m4a", "wav_url": "https://storage.googleapis.com/...c3d4e5f6.wav", "image_url": "https://storage.googleapis.com/...d4e5f6a7.jpg" } ], "request": { "operation": "cover", "clip": "user:1234-user@email.com-clip:...", "strength": 0.7 } } ``` **201** **201 Created** **Async mode** (`"mode": "async"`) — the edit is queued and returned immediately as `pending`. Poll [GET /jobs/`jobid`](/docs/api-flowmusic-v1/get-flowmusic-jobs-jobid) until `status` is `completed` or `failed`. ```json { "jobid": "job:aaaabbbb-cccc-4ddd-8eee-ffff99990000-user:1234-user@email.com-bot:flowmusic", "email": "user@email.com", "operation": "remix", "status": "pending", "created_at": "2026-06-01T18:40:42.412Z", "request": { "operation": "remix", "clip": "user:1234-...-clip:...", "lyrics": "[Verse 1]\n...", "mode": "async" } } ``` **400** **400 Bad Request** Validation error — an unknown `operation`, a missing required field for the chosen operation (e.g. `replace` without `mask_start_s`/`mask_end_s`, or `effect` without `effect`), or a `clip` that isn't a clip-type asset ID. ```json { "error": "instruction required for operation=extend", "code": 400 } ``` **401** **401 Unauthorized** Invalid API token. ```json { "error": "Unauthorized", "code": 401 } ``` **402** **402 Payment Required** The targeted Flow Music account has insufficient credits to complete the edit. Top up its credit balance (or target a funded account with `email`), then retry. ```json { "error": "recipes/create failed (402): {\"detail\":\"User has insufficient credits\"}", "code": 402 } ``` **404** **404** The `clip`'s account isn't configured under your useapi.net account, or the source clip no longer exists on Flow Music. ```json { "error": "source clip not found on FlowMusic", "code": 404 } ``` **422** **422** The edit produced no audio (e.g. a content-moderation refusal — a terminal `status: "failed"` record), or the source clip can't be edited (an uploaded clip has no `conversation_id`). ```json { "jobid": "job:aaaabbbb-cccc-4ddd-8eee-ffff99990000-user:1234-user@email.com-bot:flowmusic", "email": "user@email.com", "operation": "cover", "status": "failed", "created_at": "2026-06-01T18:40:42.412Z", "completed_at": "2026-06-01T18:41:10.512Z", "error": { "code": "moderation_reject", "message": "the model declined to generate audio for this edit" }, "request": { "operation": "cover", "clip": "user:1234-...-clip:..." } } ``` **429** **429 Too Many Requests** Passed through from Flow Music when the `clip`'s account is already running too many concurrent jobs — retry once one finishes. ```json { "error": "producer/tool-call failed (429): {\"detail\":\"Too Many Requests\"}", "code": 429 } ``` **596** **596 Account Error** The `clip`'s account is in an error state and can't be used — re-add it via [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts). ```json { "error": "Account user@email.com in error state: refresh token rejected", "code": 596 } ``` ### Model Same job-record shape as [POST /music](/docs/api-flowmusic-v1/post-flowmusic-music), plus a top-level `operation`. The `request` echoes the edit body you submitted. (`operation: stems` returns a flat manifest of the 4 stem clips instead — see [operation: stems](#operation-stems).) **200 Completed** ```typescript { jobid: string // job:<uuid>-user:<id>-<email>-bot:flowmusic email: string operation: 'effect' | 'extend' | 'replace' | 'cover' | 'remix' | 'stems' status: 'completed' | 'pending' | 'failed' created_at: string // ISO 8601 timestamp completed_at?: string // present when terminal duration_ms?: number // wall-clock render time clips: Array<{ clip: string // encoded clip asset id — use with /music/download and /music/edit title: string | null duration_s: number | null lyrics: string | null // [Verse]/[Chorus]-tagged; "[Instrumental]" for instrumentals seed: number | null lyrics_timing: Array<[number, number]> | null // timing markers, [start, end] seconds (vocal clips) created_at: string // ISO 8601 timestamp audio_url: string // m4a download URL wav_url: string // wav download URL image_url: string | null // auto-generated artwork URL }> request: { // the edit body you submitted, echoed back verbatim operation: 'effect' | 'extend' | 'replace' | 'cover' | 'remix' | 'stems' clip: string seed?: number // common — pins the result title?: string // common mode?: 'sync' | 'async' // common replyUrl?: string // common — webhook replyRef?: string // common — webhook reference effect?: 'Trim' | 'Fade' | 'Gain' | 'Reverb' | 'Pan' | 'Equalizer' // effect gain_db?: number // effect=Gain (-30..30) mix?: number // effect=Reverb (0..1) room_size?: number // effect=Reverb (0..1) pan?: number // effect=Pan (-1..1) bass_db?: number // effect=Equalizer (-12..12) mid_db?: number // effect=Equalizer (-12..12) treble_db?: number // effect=Equalizer (-12..12) fade_in_s?: number // effect=Fade fade_out_s?: number // effect=Fade start_s?: number // effect=Trim end_s?: number // effect=Trim instruction?: string // extend / replace / cover extend_s?: number // extend (1..300) extend_from_s?: number // extend mask_start_s?: number // replace mask_end_s?: number // replace strength?: number // cover (0..1) lyrics?: string // remix prompt?: string // remix } } ``` **201 Pending** ```typescript { jobid: string // job:<uuid>-user:<id>-<email>-bot:flowmusic email: string operation: 'effect' | 'extend' | 'replace' | 'cover' | 'remix' | 'stems' status: 'pending' created_at: string // ISO 8601 timestamp request: { } // the edit body you submitted — same shape as the 200 (Completed) model above } ``` ### Examples **Curl** ``` bash # Cover (restyle) — instruction optional curl -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -X POST "https://api.useapi.net/v1/flowmusic/music/edit" \ -d '{ "operation": "cover", "clip": "user:1234-user@email.com-clip:...", "strength": 0.7 }' ``` **JavaScript** ``` javascript const response = await fetch('https://api.useapi.net/v1/flowmusic/music/edit', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_TOKEN', 'Content-Type': 'application/json' }, body: JSON.stringify({ operation: 'remix', clip: 'user:1234-user@email.com-clip:...', lyrics: '[Verse 1]\nNeon rain on the avenue\n[Chorus]\nWe rise again' }) }); console.log(await response.json()); ``` **Python** ``` python import requests response = requests.post( 'https://api.useapi.net/v1/flowmusic/music/edit', headers={'Authorization': 'Bearer YOUR_API_TOKEN'}, json={'operation': 'effect', 'clip': 'user:1234-user@email.com-clip:...', 'effect': 'Gain', 'gain_db': 6} ) print(response.status_code, response.json()) ``` ### Try It <script> function displayFlowMusicClips(response, elementId) { const container = document.getElementById(`${elementId}-media-links`); if (!container) return; container.innerHTML = ''; const clips = response.clips || []; if (clips.length === 0) { container.style.display = 'none'; return; } container.innerHTML = '<div class="media-links-container"><strong>Result clips:</strong></div>' + clips.map((c, i) => { const title = c.stem_type ? `${c.title || ('Stem ' + (i + 1))} (${c.stem_type})` : (c.title || `Clip ${i + 1}`); const cover = c.image_url ? `<a href="${c.image_url}" target="_blank" rel="noopener noreferrer" title="Open full image in a new tab"><img src="${c.image_url}" alt="cover" style="max-width:96px;border-radius:6px;vertical-align:middle;margin-right:8px;cursor:zoom-in;"></a>` : ''; const audio = c.audio_url ? `<audio src="${c.audio_url}" controls style="vertical-align:middle;"></audio>` : ''; return `<div style="margin:6px 0;">${cover}${audio}<div><small>${title}</small></div></div>`; }).join(''); container.style.display = 'block'; } async function apiTestMusicEditFlowMusic() { const data = { method: 'POST', headers: { 'Authorization': `Bearer ${document.getElementById('post-music-edit-FlowMusic-token').value}`, 'Content-Type': 'application/json' } }; const v = (id) => document.getElementById(`post-music-edit-FlowMusic-${id}`)?.value?.trim(); const str = (id) => { const x = v(id); return x ? x : undefined; }; const num = (id) => { const x = v(id); return x ? parseFloat(x) : undefined; }; const seedVal = v('seed'); const body = { operation: v('operation'), clip: v('clip'), seed: seedVal ? parseInt(seedVal) : undefined, title: str('title'), mode: str('mode'), replyUrl: str('replyUrl'), replyRef: str('replyRef'), effect: str('effect'), gain_db: num('gain_db'), mix: num('mix'), room_size: num('room_size'), pan: num('pan'), bass_db: num('bass_db'), mid_db: num('mid_db'), treble_db: num('treble_db'), fade_in_s: num('fade_in_s'), fade_out_s: num('fade_out_s'), start_s: num('start_s'), end_s: num('end_s'), instruction: str('instruction'), extend_s: num('extend_s'), extend_from_s: num('extend_from_s'), mask_start_s: num('mask_start_s'), mask_end_s: num('mask_end_s'), strength: num('strength'), lyrics: str('lyrics'), prompt: str('prompt') }; data.body = JSON.stringify(body); const mediaLinks = document.getElementById('post-music-edit-FlowMusic-media-links'); if (mediaLinks) mediaLinks.style.display = 'none'; const status = await apiExecute(`https://api.useapi.net/v1/flowmusic/music/edit`, 'post-music-edit-FlowMusic', data); if (status === 200) { try { const response = JSON.parse(document.getElementById('post-music-edit-FlowMusic-response-json').innerText); displayFlowMusicClips(response, 'post-music-edit-FlowMusic'); } catch (e) { console.error('clip display failed', e); } } } </script> <form id="post-music-edit-FlowMusic" onsubmit="return false;" class="input-form"> <div class="input-field"> <div class="input-field-row"> <label for="post-music-edit-FlowMusic-token"><span>API Token<small> (see <a href="/docs/start-here/setup-useapi">Setup useapi.net</a>)</small></span> <input id="post-music-edit-FlowMusic-token" type="text" class="input-element" required aria-required="true" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-operation"><span>operation<small> (required)</small></span> <select id="post-music-edit-FlowMusic-operation" class="input-element" required aria-required="true"> <option value="effect">effect</option> <option value="extend">extend</option> <option value="replace">replace</option> <option value="cover">cover</option> <option value="remix">remix</option> <option value="stems">stems</option> </select> </label> <label for="post-music-edit-FlowMusic-clip"><span>clip<small> (required, encoded clip asset id)</small></span> <input id="post-music-edit-FlowMusic-clip" type="text" class="input-element" required aria-required="true" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-seed"><span>seed<small> (optional, integer ≥ 0)</small></span> <input id="post-music-edit-FlowMusic-seed" type="number" class="input-element" min="0" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-title"><span>title<small> (optional, result clip title)</small></span> <input id="post-music-edit-FlowMusic-title" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-instruction"><span>instruction<small> (extend/replace required, cover optional)</small></span> <textarea id="post-music-edit-FlowMusic-instruction" class="input-element" rows="3" style="resize: both;"></textarea> </label> <label for="post-music-edit-FlowMusic-lyrics"><span>lyrics<small> (remix, required)</small></span> <textarea id="post-music-edit-FlowMusic-lyrics" class="input-element" rows="3" style="resize: both;"></textarea> </label> <label for="post-music-edit-FlowMusic-prompt"><span>prompt<small> (remix, optional sound override)</small></span> <input id="post-music-edit-FlowMusic-prompt" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-effect"><span>effect<small> (effect, required — Trim/Fade/Gain/Reverb/Pan/Equalizer)</small></span> <select id="post-music-edit-FlowMusic-effect" class="input-element"> <option value=""></option> <option value="Trim">Trim</option> <option value="Fade">Fade</option> <option value="Gain">Gain</option> <option value="Reverb">Reverb</option> <option value="Pan">Pan</option> <option value="Equalizer">Equalizer</option> </select> </label> <label for="post-music-edit-FlowMusic-gain_db"><span>gain_db<small> (effect=Gain, −30…30)</small></span> <input id="post-music-edit-FlowMusic-gain_db" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-mix"><span>mix<small> (effect=Reverb, 0…1)</small></span> <input id="post-music-edit-FlowMusic-mix" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-room_size"><span>room_size<small> (effect=Reverb, 0…1)</small></span> <input id="post-music-edit-FlowMusic-room_size" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-pan"><span>pan<small> (effect=Pan, −1…1)</small></span> <input id="post-music-edit-FlowMusic-pan" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-bass_db"><span>bass_db<small> (effect=Equalizer, −12…12)</small></span> <input id="post-music-edit-FlowMusic-bass_db" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-mid_db"><span>mid_db<small> (effect=Equalizer, −12…12)</small></span> <input id="post-music-edit-FlowMusic-mid_db" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-treble_db"><span>treble_db<small> (effect=Equalizer, −12…12)</small></span> <input id="post-music-edit-FlowMusic-treble_db" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-fade_in_s"><span>fade_in_s<small> (effect=Fade, seconds)</small></span> <input id="post-music-edit-FlowMusic-fade_in_s" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-fade_out_s"><span>fade_out_s<small> (effect=Fade, seconds)</small></span> <input id="post-music-edit-FlowMusic-fade_out_s" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-start_s"><span>start_s<small> (effect=Trim, seconds)</small></span> <input id="post-music-edit-FlowMusic-start_s" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-end_s"><span>end_s<small> (effect=Trim, seconds)</small></span> <input id="post-music-edit-FlowMusic-end_s" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-extend_s"><span>extend_s<small> (extend, seconds 1–300)</small></span> <input id="post-music-edit-FlowMusic-extend_s" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-extend_from_s"><span>extend_from_s<small> (extend, seconds)</small></span> <input id="post-music-edit-FlowMusic-extend_from_s" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-mask_start_s"><span>mask_start_s<small> (replace, required, seconds)</small></span> <input id="post-music-edit-FlowMusic-mask_start_s" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-mask_end_s"><span>mask_end_s<small> (replace, required, seconds)</small></span> <input id="post-music-edit-FlowMusic-mask_end_s" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-strength"><span>strength<small> (cover, 0…1)</small></span> <input id="post-music-edit-FlowMusic-strength" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-mode"><span>mode<small> (optional — ignored by stems)</small></span> <select id="post-music-edit-FlowMusic-mode" class="input-element"> <option value="">sync (default)</option> <option value="async">async</option> </select> </label> <label for="post-music-edit-FlowMusic-replyUrl"><span>replyUrl<small> (optional, webhook URL)</small></span> <input id="post-music-edit-FlowMusic-replyUrl" type="text" class="input-element" oninput="this.size = this.value.length"> </label> <label for="post-music-edit-FlowMusic-replyRef"><span>replyRef<small> (optional, webhook reference)</small></span> <input id="post-music-edit-FlowMusic-replyRef" type="text" class="input-element" oninput="this.size = this.value.length"> </label> </div> <button id="post-music-edit-FlowMusic-button" class="btn btn-primary fs-3" type="submit" onclick="apiTestMusicEditFlowMusic()">Go</button> </div> </form> <div id="post-music-edit-FlowMusic-media-links" style="display:none;"></div> <div id="post-music-edit-FlowMusic-response" class="response-placeholder visible-false"></div> === URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-music-lyrics === Document URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-music-lyrics ## Generate lyrics (free) <small>June 3, 2026</small> --- Generate `[Verse]`/`[Chorus]`-structured lyrics from a prompt, **without** producing audio (no credits burned). > Flow Music has no dedicated lyrics-only model — this endpoint drives the song agent to write lyrics (and a matching `sound_prompt`) **without** generating audio. It reads the agent's structured `lyrics__create` result, so lyrics come back reliably even when the agent's chat reply is conversational; a `422` only happens when the agent produced no lyrics at all. For lyrics bound to an actual track, use [POST /music](/docs/api-flowmusic-v1/post-flowmusic-music) — every generated clip returns `clip.lyrics`. > **https://api.useapi.net/v1/flowmusic/music/lyrics** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json # Alternatively you can use multipart/form-data # Content-Type: multipart/form-data ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Request Body ```json { "prompt": "a nostalgic song about a long road trip home", "structure": "Verse 1 / Chorus / Verse 2 / Chorus / Bridge / Outro" } ``` - `email` is optional, which configured account to use. - `prompt` is **required**, the song concept. 1–2,000 characters. - `structure` is optional, a free-text section scaffold to shape the layout. ≤2,000 characters. ### Responses **200** **200 OK** ```json { "email": "user@email.com", "title": "Road Home", "lyrics": "[Verse 1]\nHighway humming under the wheels\n[Chorus]\nTaking the long way home", "sound_prompt": "Indie folk, warm acoustic guitar, 80 BPM, intimate male vocal", "source": "lyrics-create", "audio_create_song_fired": false, "credits_burned": 0 } ``` **401** **401 Unauthorized** — invalid API token. **422** **422** — rare: the agent produced no lyrics at all (no structured result and no tagged text block). Retry, or use [POST /music](/docs/api-flowmusic-v1/post-flowmusic-music) and read `clip.lyrics`. ```json { "email": "user@email.com", "error": { "code": "no_lyrics", "message": "..." }, "agent_text": "..." } ``` **596** **596 Account Error** The account targeted by `email` is in an error state and can't be used — re-add it via [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts). The `error` field returns whatever reason was recorded for the account. ```json { "error": "Account user@email.com in error state: refresh token rejected", "code": 596 } ``` ### Model ```typescript { email: string title: string | null lyrics: string // [Verse]/[Chorus]-tagged sound_prompt: string | null // a matching style/production prompt, when the agent wrote one source: string // 'lyrics-create' (agent's structured result) | 'text-part-fallback' audio_create_song_fired: boolean // true only if the agent also kicked off a render (normally false) credits_burned: number | null // 0 (free); null if a song render unexpectedly fired } ``` ### Examples **Curl** ``` bash curl -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -X POST "https://api.useapi.net/v1/flowmusic/music/lyrics" \ -d '{ "prompt": "a nostalgic song about a long road trip home" }' ``` **Python** ``` python import requests response = requests.post( 'https://api.useapi.net/v1/flowmusic/music/lyrics', headers={'Authorization': 'Bearer YOUR_API_TOKEN'}, json={'prompt': 'a nostalgic song about a long road trip home'} ) print(response.status_code, response.json()) ``` ### Try It <script> async function apiTestMusicLyricsFlowMusic() { const data = { method: 'POST', headers: { 'Authorization': `Bearer ${document.getElementById('post-music-lyrics-FlowMusic-token').value}`, 'Content-Type': 'application/json' } }; let prompt = document.getElementById(`post-music-lyrics-FlowMusic-prompt`)?.value; let structure = document.getElementById(`post-music-lyrics-FlowMusic-structure`)?.value; const url = `https://api.useapi.net/v1/flowmusic/music/lyrics`; data.body = JSON.stringify({ prompt, structure: structure || undefined, }); await apiExecute(url, 'post-music-lyrics-FlowMusic', data); } </script> <form id="post-music-lyrics-FlowMusic" onsubmit="return false;" class="input-form"> <div class="input-field"> <div class="input-field-row"> <label for="post-music-lyrics-FlowMusic-token"><span>API Token<small> (see <a href="/docs/start-here/setup-useapi">Setup useapi.net</a>)</small></span> <input id="post-music-lyrics-FlowMusic-token" type="text" class="input-element" required aria-required="true" oninput="this.size = this.value.length"> </label> <label for="post-music-lyrics-FlowMusic-prompt"><span>prompt</span> <textarea id="post-music-lyrics-FlowMusic-prompt" class="input-element" rows="3" required aria-required="true" style="resize: both;"></textarea> </label> <label for="post-music-lyrics-FlowMusic-structure"><span>structure<small> (optional, section scaffold)</small></span> <input id="post-music-lyrics-FlowMusic-structure" type="text" class="input-element" oninput="this.size = this.value.length"> </label> </div> <button id="post-music-lyrics-FlowMusic-button" class="btn btn-primary fs-3" type="submit" onclick="apiTestMusicLyricsFlowMusic()">Go</button> </div> </form> <div id="post-music-lyrics-FlowMusic-response" class="response-placeholder visible-false"></div> === URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-music === Document URL: https://useapi.net/docs/api-flowmusic-v1/post-flowmusic-music ## Generate a song <small>June 3, 2026</small> --- Generate a complete song from a text prompt using [Lyria 3 Pro](https://deepmind.google/models/lyria/). Whether the result is vocal or instrumental is decided by the model from your prompt — describe vocals (or pass your own `lyrics`) to get a sung track with model-written lyrics, or set `instrumental: true` to force an instrumental. Each generation returns up to two clips (an A/B pair) and consumes credits from your account balance. > **https://api.useapi.net/v1/flowmusic/music** ### Request Headers ``` yaml Authorization: Bearer {API token} Content-Type: application/json # Alternatively you can use multipart/form-data # Content-Type: multipart/form-data ``` - `API token` is **required**, see [Setup useapi.net](/docs/start-here/setup-useapi) for details. ### Request Body ```json { "prompt": "lo-fi hip hop, mellow piano, 80 bpm", "instrumental": false, "ghostwriter": "pro" } ``` - `email` is optional. The Flow Music account to use. See [GET /accounts](/docs/api-flowmusic-v1/get-flowmusic-accounts) for configured accounts. If not provided, the API automatically selects an account with available capacity. Specify when you want to use a specific configured account. - `prompt` is **required**, the song description — style, mood, genre, instruments, BPM, and any vocal direction. 1–10,000 characters. - `instrumental` is optional. `true` forces an instrumental (prepends an `[Instrumental]` tag). `false` (default) lets the model decide whether to sing based on your prompt. - `lyrics` is optional, your own lyrics, ideally `[Verse]`/`[Chorus]`-tagged (≤10,000 chars). Providing them forces a vocal render of those words. Omit them and the model writes lyrics only if the prompt implies vocals. - `ghostwriter` is optional, the lyrics-writer version used when the model writes the lyrics. It has no effect if you supply your own `lyrics`. Supported values: `standard`, `pro`. - `refImage1` to `refImage10` are optional, encoded image asset IDs from [POST /files](/docs/api-flowmusic-v1/post-flowmusic-files) or [POST /files/generate](/docs/api-flowmusic-v1/post-flowmusic-files-generate). Passed to the model only as uploaded-file hints (it may loosely nudge style) — not a literal reference. - `refAudio1` to `refAudio10` are optional, encoded audio asset IDs from [POST /files](/docs/api-flowmusic-v1/post-flowmusic-files). Experimental, limited effect — the current Lyria 3 Pro model does not truly reference an uploaded song. The upload is passed only as a hint and may not influence the result. - `mode` is optional, `sync` (default — the request blocks until the song is ready, ~40–150 s) or `async` (returns `201` immediately with a `jobid`). Poll [GET /jobs/`jobid`](/docs/api-flowmusic-v1/get-flowmusic-jobs-jobid) for completion. - `replyUrl` is optional, webhook URL. When the job completes or fails the API POSTs the job record to it — the same shape as [GET /jobs/`jobid`](/docs/api-flowmusic-v1/get-flowmusic-jobs-jobid). - `replyRef` is optional, custom reference string echoed back in the webhook. > **References must come from one account.** A `refImage`/`refAudio` ID is tied to the account it was uploaded to. If you pass any references, they must all be from the same account, and the song is generated on that account. If you also pass `email`, it must be that same account — otherwise the request fails with `400`. ### Responses **200** **200 OK** **Sync mode** (default, or `"mode": "sync"`) — the song is generated and the full record is returned inline. All audio lives in the `clips[]` array. ```json { "jobid": "job:aaaabbbb-cccc-4ddd-8eee-ffff99990000-user:1234-user@email.com-bot:flowmusic", "email": "user@email.com", "status": "completed", "created_at": "2026-06-01T18:40:42.412Z", "completed_at": "2026-06-01T18:42:55.123Z", "duration_ms": 132711, "clips": [ { "clip": "user:1234-user@email.com-clip:a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d", "title": "Morning Sun", "duration_s": 173.04, "lyrics": "[Verse 1]\nCity lights blur in the rain\n[Chorus]\nAnd I will wait for the morning sun", "seed": null, "lyrics_timing": [ [0.0, 2.4], [2.4, 5.1] ], "created_at": "2026-06-01T18:41:04.283Z", "audio_url": "https://storage.googleapis.com/...a1b2c3d4.m4a", "wav_url": "https://storage.googleapis.com/...a1b2c3d4.wav", "image_url": "https://storage.googleapis.com/...b2c3d4e5.jpg" } ], "request": { "prompt": "lo-fi hip hop, mellow piano, 80 bpm", "instrumental": false } } ``` **201** **201 Created** **Async mode** (`"mode": "async"`) — the job is queued and returned immediately as `pending`. Poll [GET /jobs/`jobid`](/docs/api-flowmusic-v1/get-flowmusic-jobs-jobid) until `status` is `completed` or `failed`. ```json { "jobid": "job:aaaabbbb-cccc-4ddd-8eee-ffff99990000-user:1234-user@email.com-bot:flowmusic", "email": "user@email.com", "status": "pending", "created_at": "2026-06-01T18:40:42.412Z", "request": { "prompt": "upbeat funk groove, 110 bpm", "instrumental": true, "mode": "async" } } ``` **400** **400 Bad Request** Validation error (missing/invalid parameter, or no account configured). ```json { "error": "Parameter prompt is required", "code": 400 } ``` **401** **401 Unauthorized** Invalid API token. ```json { "error": "Unauthorized", "code": 401 } ``` **402** **402 Payment Required** The targeted Flow Music account has insufficient credits to complete the generation. Top up its credit balance (or target a funded account with `email`), then retry. ```json { "error": "FlowMusic conversation failed (402): {\"detail\":\"User has insufficient credits\"}", "code": 402 } ``` **422** **422** The model produced no audio (e.g. a content-moderation refusal). A terminal job record with `status: "failed"`. ```json { "jobid": "job:aaaabbbb-cccc-4ddd-8eee-ffff99990000-user:1234-user@email.com-bot:flowmusic", "email": "user@email.com", "status": "failed", "created_at": "2026-06-01T18:40:42.412Z", "completed_at": "2026-06-01T18:41:10.512Z", "error": { "code": "moderation_reject", "message": "the model declined to generate audio for this prompt" }, "request": { "prompt": "...", "instrumental": false } } ``` **429** **429 Too Many Requests** Returned in two cases. `email` omitted and **every** configured account is already at its `maxJobs` concurrency limit (load-balancing has nowhere to place the job) — retry once a running job finishes: ```json { "error": "All 3 FlowMusic accounts are at their maxJobs concurrency limit — retry shortly", "code": 429 } ``` Or passed through from FlowMusic when a chosen account exceeds its own per-account limit (a single or `email`-targeted account running too many generations at once): ```json { "error": "FlowMusic conversation failed (429): {\"detail\":\"Too Many Requests\"}", "code": 429 } ``` **596** **596 Account Error** The account targeted by `email` is in an error state and can't be used — re-add it via [POST /accounts](/docs/api-flowmusic-v1/post-flowmusic-accounts). The `error` field returns whatever reason was recorded for the account. ```json { "error": "Account user@email.com in error state: refresh token rejected", "code": 596 } ``` ### Model **200 Completed** ```typescript { jobid: string // job:<uuid>-user:<id>-<email>-bot:flowmusic email: string status: 'completed' | 'pending' | 'failed' created_at: string // ISO 8601 timestamp completed_at?: string // present when terminal duration_ms?: number // wall-clock generation time clips: Array<{ clip: string // encoded clip asset id — use with /music/download and /music/edit title: string | null duration_s: number | null lyrics: string | null // [Verse]/[Chorus]-tagged; "[Instrumental]" for instrumentals seed: number | null lyrics_timing: Array<[number, number]> | null // timing markers, [start, end] seconds (vocal clips) created_at: string // ISO 8601 timestamp audio_url: string // signed m4a download URL wav_url: string // signed wav download URL image_url: string | null // auto-generated artwork URL }> request: { // the request body you submitted, echoed back verbatim prompt: string email?: string instrumental?: boolean lyrics?: string ghostwriter?: 'standard' | 'pro' mode?: 'sync' | 'async' replyUrl?: string replyRef?: string refImage1?: string // …up to refImage10 — encoded image asset IDs refAudio1?: string // …up to refAudio10 — encoded audio asset IDs } } ``` **201 Pending** ```typescript { jobid: string // job:<uuid>-user:<id>-<email>-bot:flowmusic email: string status: 'pending' created_at: string // ISO 8601 timestamp request: { // the request body you submitted, echoed back verbatim prompt: string email?: string instrumental?: boolean lyrics?: string ghostwriter?: 'standard' | 'pro' mode?: 'sync' | 'async' replyUrl?: string replyRef?: string refImage1?: string // …up to refImage10 — encoded image asset IDs refAudio1?: string // …up to refAudio10 — encoded audio asset IDs } } ``` ### Examples **Curl** ``` bash curl -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_API_TOKEN" \ -X POST "https://api.useapi.net/v1/flowmusic/music" \ -d '{ "prompt": "lo-fi hip hop, mellow piano, 80 bpm" }' ``` **JavaScript** ``` javascript const response = await fetch('https://api.useapi.net/v1/flowmusic/music', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_TOKEN', 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt: 'lo-fi hip hop, mellow piano, 80 bpm' }) }); const result = await response.json(); console.log(result.clips?.[0]?.audio_url); ``` **Python** ``` python import requests response = requests.post( 'https://api.useapi.net/v1/flowmusic/music', headers={'Authorization': 'Bearer YOUR_API_TOKEN'}, json={'prompt': 'lo-fi hip hop, mellow piano, 80 bpm'} ) print(response.status_code, response.json()) ``` ### Try It <script> function displayFlowMusicClips(response, elementId) { const container = document.getElementById(`${elementId}-media-links`); if (!container) return; container.innerHTML = ''; const clips = response.clips || []; if (clips.length === 0) { container.style.display = 'none'; return; } container.innerHTML = '<div class="media-links-container"><strong>Generated clips:</strong></div>' + clips.map((c, i) => { const title = c.title || `Clip ${i + 1}`; const cover = c.image_url ? `<a href="${c.image_url}" target="_blank" rel="noopener noreferrer" title="Open full image in a new tab"><img src="${c.image_url}" alt="cover" style="max-width:96px;border-radius:6px;vertical-align:middle;margin-right:8px;cursor:zoom-in;"></a>` : ''; const audio = c.audio_url ? `<audio src="${c.audio_url}" controls style="vertical-align:middle;"></audio>` : ''; return `<div style="margin:6px 0;">${cover}${audio}<div><small>${title}</small></div></div>`; }).join(''); container.style.display = 'block'; } async function apiExecutePostFlowMusicMusic() { const elementId = 'input'; const payload = {}; document.querySelectorAll('.input-query-param').forEach(input => { const value = input.value?.trim(); if (input.id.startsWith('input-') && value !== '' && value !== undefined) { const key = input.id.replace('input-', ''); if (key === 'seed') payload[key] = parseInt(value); else if (key === 'instrumental') payload[key] = value === 'true'; else payload[key] = value; } }); const data = { method: 'POST', headers: { 'Authorization': `Bearer ${document.getElementById('input-token').value}`, 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }; const mediaLinks = document.getElementById(`${elementId}-media-links`); if (mediaLinks) mediaLinks.style.display = 'none'; const status = await apiExecute('https://api.useapi.net/v1/flowmusic/music', elementId, data, { includeTimer: true }); if (status === 200) { try { const response = JSON.parse(document.getElementById(`${elementId}-response-json`).innerText); displayFlowMusicClips(response, elementId); } catch (e) { console.error('Failed to parse response for clip display:', e); } } } </script> <form id="input" class="input-form" onsubmit="return false;"> <div class="input-field"> <div class="input-field-row"> <label for="input-token"><span>API Token<small> (see <a href="/docs/start-here/setup-useapi">Setup useapi.net</a>)</small></span> <input id="input-token" type="text" class="input-element" required aria-required="true"> </label> <label for="input-prompt"><span>prompt<small> (required)</small></span> <input id="input-prompt" type="text" class="input-element input-query-param" required aria-required="true" placeholder="lo-fi hip hop, mellow piano, 80 bpm"> </label> <label for="input-instrumental"><span>instrumental</span> <select id="input-instrumental" class="input-element input-query-param"> <option value="">false (default)</option> <option value="true">true (instrumental — no vocals)</option> </select> </label> <label for="input-lyrics"><span>lyrics<small> (optional, [Verse]/[Chorus]-tagged)</small></span> <input id="input-lyrics" type="text" class="input-element input-query-param" placeholder="[Verse 1] ... [Chorus] ..."> </label> <label for="input-ghostwriter"><span>ghostwriter<small> (lyrics-writer version)</small></span> <select id="input-ghostwriter" class="input-element input-query-param"> <option value=""></option> <option value="standard">standard</option> <option value="pro">pro</option> </select> </label> <label for="input-email"><span>email<small> (optional)</small></span> <input id="input-email" type="text" class="input-element input-query-param" placeholder="user@email.com"> </label> <label for="input-refImage1"><span>refImage1<small> (optional, encoded image asset id from <a href="/docs/api-flowmusic-v1/post-flowmusic-files" target="_blank">POST /files</a>)</small></span> <input id="input-refImage1" type="text" class="input-element input-query-param" placeholder="user:1234-user@email.com-image:..."> </label> <label for="input-refAudio1"><span>refAudio1<small> (optional, encoded audio asset id from <a href="/docs/api-flowmusic-v1/post-flowmusic-files" target="_blank">POST /files</a>)</small></span> <input id="input-refAudio1" type="text" class="input-element input-query-param" placeholder="user:1234-user@email.com-audio:..."> </label> <label for="input-mode"><span>mode</span> <select id="input-mode" class="input-element input-query-param"> <option value="">sync (default)</option> <option value="async">async</option> </select> </label> <label for="input-replyUrl"><span>replyUrl<small> (webhook URL)</small></span> <input id="input-replyUrl" type="text" class="input-element input-query-param" placeholder="https://your-domain.com/webhook"> </label> <label for="input-replyRef"><span>replyRef<small> (custom reference)</small></span> <input id="input-replyRef" type="text" class="input-element input-query-param" placeholder="my-custom-id-123"> </label> </div> <button id="input-button" class="btn btn-primary fs-3" type="submit" onclick="apiExecutePostFlowMusicMusic()">Go</button> </div> </form> <div id="input-media-links" style="display:none;"></div> <div id="input-response" class="response-placeholder visible-false"></div> === Cross-reference: General Q&A === For cross-cutting tips (rate limiting, prompt moderation, common patterns) see https://useapi.net/assets/aibot/qa.txt WARNING: Q&A items are general-purpose and may reference deprecated APIs, sunsetted services, or scenarios not applicable to the API documented above. Cross-check against this service's endpoint documentation before applying any Q&A guidance.