Enterprise API

Soundverse Enterprise API

Generate licensed music, vocals, stems, and audio insights from your own product. This page covers auth, the request/response contract, and the two ways to call the API — start here before jumping to a capability page.

What changed

The Enterprise API moved off the old per-capability, per-version REST surface (POST /v6/generate/music, GET /v5/recognize, etc.) onto a single generic pipeline: every generation — songs, music, vocals, stems, copyright checks — goes through one endpoint, POST /v1/generations, with a tool_id that identifies which capability you want.

If you integrated against the old API, you don’t need to rewrite anything yet: most of the old URLs and field names still work, unchanged, via a compatibility layer that resolves the right tool for you server-side. See Compat Routes for the full list, or jump straight to your capability’s page in the sidebar — each one shows the compat route first, if it has one.

Authentication

Every request (except GET /health) requires a bearer API key in the Authorization header:

Authorization: Bearer sksoundverse_<your_api_key> Content-Type: application/json

A missing or malformed header returns 401 {"error": "UNAUTHENTICATED"}; an invalid or revoked key returns 401 {"error": "INVALID_API_KEY"}.

Two ways to call this API

There are two ways to create a generation, and you can mix both across your integration:

  1. Compat routes — old-style URLs and field names (e.g. POST /v7/generate/song). The gateway resolves the right tool for you; you never need a tool_id. Covers most of the commonly-used capabilities. See Compat Routes.
  2. Native pipelinePOST /v1/generations with an explicit tool_id. Always available, and the only option for capabilities the compat layer doesn’t cover yet (e.g. audio transcription, sound effects, audio effects, similar-music, MIDI-to-song). Look up the right tool_id on the Tool Catalog page.

Endpoints

POST/v1/generationscreate a generation

GET/v1/generations/{id}fetch one task

GET/v1/generationslist your tasks, cursor-paginated

GET/v1/generations/{id}/streamlive SSE progress

GET/v1/files/{file_id}/downloadshort-lived signed download URL

GET/v1/toolslist the tools this account can bill against

GET/v1/account/balanceyour remaining token balance

POST /v1/generations body: tool_id (string, required), license (integer, optional — see below), payload_json (string, a JSON-encoded object of the tool’s own input fields — see each capability page).

It always returns immediately — 201 {"task_id": "...", "status": "queued"} — it never blocks until the generation finishes. From there, poll GET /v1/generations/{id} or open the SSE stream; see Polling vs. streaming below.

License tiers

license is an integer on the request body. Omit it (or send 0) to default to Royalty-Free.

FieldTypeRequiredDefaultDescription
0LICENSE_UNSPECIFIEDNoNot a valid choice — treated as unset.
1LICENSE_ROYALTY_FREENoDefault if omitted.
2LICENSE_STANDARDNo
3LICENSE_DISTRIBUTIONNo
4LICENSE_SYNCNo
5LICENSE_MASTERNo

Pricing is per license tier and varies by capability and version — see the pricing table on each capability’s page, or the consolidated Tool Catalog.

Idempotency-Key

Set an Idempotency-Key header on any create call (native or compat) to make retries safe:

Idempotency-Key: my-client-generated-key-123

Up to 200 characters, [A-Za-z0-9_.:-] only. A repeat call with the same key (scoped to your account) returns the original task instead of creating a new one and billing you again — the right thing to set on every create call your client might retry. A missing or malformed key is not an error; the request just proceeds without dedup.

Errors

Two response shapes exist, depending on where the request fails:

Pipeline errors (creating a generation)

Raised anywhere in the enforcement pipeline — tool resolution, rate limiting, billing, input validation. The error field is the exact class name below (use it for programmatic branching, not the HTTP status alone — two of these share a status code):

{"error": "RateLimited", "message": "...", "retryable": true}
FieldTypeRequiredDefaultDescription
400UnknownToolNoUnknown or inactive tool_id, or (model, operation) pair. Not retryable — fix the request.
402InsufficientFundsNoToken reservation failed. message names the shortfall reason; top up your balance and retry.
422ToolNotPricedNoThe tool has no platform pricing configured for the given license tier — a config gap on our side, not your request.
422InvalidGenerationInputNoThe payload failed capability-specific validation (e.g. a file field isn't a resolvable upload, an inpaint time range is invalid, a consent id is malformed). message names the offending field.
429RateLimitedNoPer-tool/per-model rate limit exceeded for your account. retryable: true.
503DependencyUnavailableNoA required internal dependency (uploaded-file service, voice-clone consent service, etc.) is unavailable or not yet compatible. retryable: true.

Other errors

Raised outside the pipeline — auth and resource-ownership checks. These do not carry a retryable field, and FORBIDDEN/NOT_FOUND carry no message either; treat them as non-retryable regardless.

FieldTypeRequiredDefaultDescription
401UNAUTHENTICATEDNoMissing or non-Bearer Authorization header.
401INVALID_API_KEYNoBearer token present but invalid or revoked.
403FORBIDDENNoThe task exists but belongs to a different account. Body is just {"error": "FORBIDDEN"} — no message.
404NOT_FOUNDNoNo task with that id. Body is just {"error": "NOT_FOUND"} — no message.

There are no rate-limit response headers (no X-RateLimit-*, no Retry-After) — the 429 body above is the only signal. Back off and retry with the same Idempotency-Key. Only errors marked retryable: true above (RateLimited, DependencyUnavailable) are safe to retry as-is; retrying any other error without changing the request will fail the same way again.

Polling vs. streaming

Every generation is created async. Once you have a task id, choose either:

  • PollGET /v1/generations/{id} for a point-in-time snapshot (status: queued / processing / completed / failed).
  • StreamGET /v1/generations/{id}/stream, an SSE stream of progress / streaming_url / partial_output events, ending in a terminal event. Resume a dropped connection with ?after=<seq> from the last event you saw; a timeout or error event also carries a resume cursor.

Both are backstopped server-side — billing settles even if you never poll or stream to completion.

Results, playback, and downloads

A completed audio generation returns one or more output.assets. Each asset includes a durable file_id and blob_hash, and is saved to the authenticated account’s Soundverse Library. Enterprise API outputs are hidden from public/profile surfaces by default. Keep the file_id when you want to download the result or use it as an input to a later Soundverse generation.

To download from your own backend, call GET /v1/files/{file_id}/download. It returns a short-lived signed URL scoped to the API-key owner.

Play completed audio from Library

Open the Soundverse Library while signed in to the same account as the API key. The Library mints an authorized, short-lived playback or download link for the generated file.

Do not treat output.assets[].url as a public browser URL. It is a private storage locator and can return ResourceNotFound when opened directly.

Rate limits

Limits are enforced per tool and per model, per hour and per day, scoped to your account. There are no response headers exposing remaining quota — a 429 on create is the only signal (see Errors above).

Providing audio/file input

Capabilities that take an audio or MIDI input (extend, remix, singing with a reference, stem separation, copyright check, transcription, audio effects) accept the field in any of three shapes:

{"song": {"url": "https://example.com/track.mp3"}} // or, as shorthand, a bare string is coerced the same way: {"song": "https://example.com/track.mp3"}

A direct URL or inline base64 is usually the right choice for a server-to-server integration — you don’t need to upload anything to a Soundverse-hosted library first. Each capability page documents the exact field name (song, audio, vocal_reference, etc.) and any size/format limits for that field.

Account balance

GET/v1/account/balance

{ "total_effective": 12500, "base_effective": 10000, "extra_tokens": 2500, "base_expires_at": "2026-08-01T00:00:00Z" }