Skip to content

ElevenLabs Music

ElevenLabs Music is exposed through @effect-uai/elevenlabs. Sync generation and real chunked HTTP streaming; streamGenerationFrom (bidi session updates) is a compile-time error against this Layer (no bidi endpoint).

For ElevenLabs TTS see Speech / ElevenLabs. This page covers music only.

Install

Terminal window
pnpm add @effect-uai/core @effect-uai/elevenlabs effect

Layer

import { Config, Effect, Layer } from "effect"
import { FetchHttpClient } from "effect/unstable/http"
import { layer as elevenlabsMusicLayer } from "@effect-uai/elevenlabs/ElevenLabsMusicGenerator"
const music = Layer.unwrap(
Effect.gen(function* () {
const apiKey = yield* Config.redacted("ELEVENLABS_API_KEY")
return elevenlabsMusicLayer({ apiKey })
}),
)
const mainLayer = music.pipe(Layer.provide(FetchHttpClient.layer))

elevenlabsMusicLayer registers two service tags from one underlying implementation:

  • ElevenLabsMusicGenerator — the typed tag. Yield this for the full provider surface (composition plan, forceInstrumental, signWithC2pa, createCompositionPlan).
  • MusicGenerator — the generic tag. Yield this in provider-portable code.

Does not register MusicInteractiveSession — calling streamGenerationFrom is a compile error against this Layer alone.

Models

ModelStatusNotes
music_v1API default (May 2026)The model the API exposes today.
music_v2UI default; API in flightElevenLabs Music v2 launched 2026-05-26; model_id plumbing pending.

ElevenLabsMusicModel is a literal union with (string & {}) tail so newer model IDs work as soon as ElevenLabs ships them.

Two modes: prompt and composition plan

ElevenLabs Music has two mutually-exclusive input modes on the same endpoints.

Prompt mode

import { Duration } from "effect"
import * as MusicGenerator from "@effect-uai/core/MusicGenerator"
const result =
yield *
MusicGenerator.generate({
model: "music_v1",
prompt: "Lo-fi piano with brushed drums, 70 BPM, melancholic",
duration: Duration.seconds(60),
outputFormat: { container: "mp3", encoding: "mp3", sampleRate: 44100, bitRate: 128 },
})
yield * writeFile("out.mp3", result.primary.audio.bytes)

Bucket-2 fields the Common request carries but ElevenLabs cannot honor in prompt mode (lyrics) are dropped with a structured Effect.logWarning — embed lyrics in your prompt yourself, or use the composition plan below.

Composition-plan mode

A MusicPrompt plan structures the song into named sections with per-section lyrics, positive / negative style hints, and duration. Reach for it when the prompt mode’s single-string surface isn’t expressive enough.

import { Duration } from "effect"
import * as ElevenLabsMusicGenerator from "@effect-uai/elevenlabs/ElevenLabsMusicGenerator"
const result =
yield *
ElevenLabsMusicGenerator.ElevenLabsMusicGenerator.use((s) =>
s.generate({
model: "music_v1",
prompt: "", // must be empty when compositionPlan is set
compositionPlan: {
positiveGlobalStyles: ["lo-fi", "warm", "vintage tape"],
negativeGlobalStyles: ["distorted", "harsh"],
sections: [
{
sectionName: "Intro",
positiveLocalStyles: ["solo piano", "soft"],
negativeLocalStyles: [],
duration: Duration.seconds(12),
lines: [],
},
{
sectionName: "Verse",
positiveLocalStyles: ["brushed drums enter", "upright bass"],
negativeLocalStyles: [],
duration: Duration.seconds(24),
lines: ["A late train hums beneath the city"],
},
],
},
forceInstrumental: false,
signWithC2pa: true,
}),
)

prompt and compositionPlan are mutually exclusive on the wire; the adapter rejects with AiError.InvalidRequest if both are set. duration is rejected the same way when compositionPlan is set — each section carries its own duration.

Free composition-plan generator

Turn a prompt into a structured plan you can then edit and feed back into generate:

const plan =
yield *
ElevenLabsMusicGenerator.ElevenLabsMusicGenerator.use((s) =>
s.createCompositionPlan({
prompt: "Lo-fi piano with brushed drums, intro then verse, 60 s",
duration: Duration.seconds(60),
}),
)
// Mutate or augment plan, then:
const result =
yield *
MusicGenerator.generate({
/* … */ compositionPlan: plan,
})

The plan endpoint is free (rate-limited only, no credit cost). Use it as a starting point when prompt-only output isn’t structured enough.

Streaming output

ElevenLabs ships native chunked HTTP streaming via POST /v1/music/stream.

import * as MusicGenerator from "@effect-uai/core/MusicGenerator"
import { Stream } from "effect"
const chunks = MusicGenerator.streamGeneration({
model: "music_v1",
prompt: "uplifting orchestral build, 90 BPM",
duration: Duration.seconds(45),
})
// Stream<AudioChunk> straight to a sink:
yield * Stream.run(chunks, fileSink)

Each AudioChunk is raw bytes in your requested output_format (default mp3 44.1 kHz 128 kbps). No fake single-chunk fallback — the adapter forwards the SSE-encoded byte stream as-is.

Request shape (provider-typed)

type ElevenLabsMusicGenerateRequest = Omit<CommonGenerateMusicRequest, "model"> & {
readonly model?: ElevenLabsMusicModel // default "music_v1"
readonly compositionPlan?: ElevenLabsCompositionPlan
readonly forceInstrumental?: boolean
readonly signWithC2pa?: boolean // MP3 output only
readonly respectSectionsDurations?: boolean // composition-plan mode only
}

Output

type MusicResult = {
readonly audio: AudioBlob
readonly provider?: "elevenlabs-music"
readonly songId?: string // ElevenLabs song_id, when returned in headers
readonly watermark?: Watermark // "c2pa" when signWithC2pa: true
}

Returned as GenerateResult (one variant; primary === variants[0]). C2PA Content Credentials are opt-in via signWithC2pa: true and apply to MP3 output only. Surfaced as result.primary.watermark === "c2pa".

Output formats

outputFormat is encoded as ElevenLabs’s ?output_format= slug:

ContainerEncodingSample ratesBitrates (mp3 / opus)
mp3mp322050, 24000, 4410032, 48, 64, 96, 128, 192
opusopus4800032, 64, 96, 128, 192
wavpcm_s16le48000n/a
rawpcm_s16leany (8000 / 16000 / 22050 / 24000 / 32000 / 44100 / 48000)n/a
rawpcm_mulaw8000n/a
rawpcm_alaw8000n/a

Unencodable formats fail AiError.Unsupported at the adapter, matching the rest of the ElevenLabs codec surface. Tier gates apply (e.g. mp3_44100_192 requires Creator+).

Wire / auth notes

EndpointUse
POST /v1/musicSync. Binary audio response in requested format.
POST /v1/music/streamChunked HTTP stream. Same body as sync.
POST /v1/music/planFree composition-plan generator. Returns JSON.

Auth: xi-api-key header — same key as TTS / STT. Regional bases honored via the region field on Config (default / eu / in), shared with the rest of @effect-uai/elevenlabs.

Errors

Standard HTTP → AiError mapping. ElevenLabs-music-specific:

Request shapeError
prompt non-empty AND compositionPlanAiError.InvalidRequest (mutually excl.)
duration set AND compositionPlan setAiError.InvalidRequest
Output format the codec can’t encodeAiError.Unsupported
422 validation error from the APIAiError.InvalidRequest (with raw body)
streamGenerationFrom callCompile-time error (no marker, no bidi)

See also