New: Audio API, Embeddings & Realtime WebSocket now available!
osmAPI LogoosmAPI

Music Generation

Generate music with Google Lyria 3 through the synchronous /v1/music API

Music Generation

osmAPI generates music with Google Lyria 3 through a dedicated, synchronous /v1/music API. Unlike video, music returns in a single round-trip: you POST a prompt and get back either a hosted URL (when object storage is configured) or the audio bytes inline (otherwise / when the organization opts out of retention). No polling, no job IDs.

Music generation is currently Google Lyria 3 only. It is a separate flow from chat — music models do not appear in the chat model selector. Try it in the playground at /music.

Available models

You can find all music models on the models page.

Model IDNotesPrice (flat)
lyria-3-clip-previewShort-form (~30s MP3). Text-only prompt.$0.05
lyria-3-pro-previewLong-form (~2–3 min). MP3. Up to 10 reference images.$0.08

Capabilities by model

CapabilityLyria 3 ClipLyria 3 Pro
Text-to-music
Reference images≤ 10
MP3 output
WAV output—¹
Instrumental toggle
Typical duration~30 s~2–3 min
Pricing$0.05 / song$0.08 / song

¹ WAV output is documented by Google for Lyria 3 Pro but is not currently available through /v1/music — the raw :generateContent endpoint rejects the documented request shape. All output is MP3 for now.

Lifecycle

One HTTP request — no polling, no separate job. Lyria's :generateContent endpoint returns the audio bytes inline; osmAPI optionally persists them to object storage and hands you a download URL.

Generate

curl -X POST "https://api.osmapi.com/v1/music" \
  -H "Authorization: Bearer $OSM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "lyria-3-clip-preview",
    "prompt": "Upbeat lo-fi hip hop, 85 BPM, instrumental, mellow piano"
  }'

Returns 200 OK. The response always includes audio.base64 so you can play/save the track immediately. When object storage is configured (and the org isn't set to none retention) it also includes a durable audio.url for later replay (see Storage & retention):

S3-backed response (default — object storage configured, retention retain):

{
	"id": "req_…",
	"object": "music.generation",
	"model": "lyria-3-clip-preview",
	"used_model": "lyria-3-clip-preview",
	"used_provider": "google-ai-studio",
	"audio": {
		"base64": "<MP3 bytes as base64>",
		"url": "/v1/music/req_…/content",
		"mime_type": "audio/mpeg",
		"storage": "s3",
		"expires_in_days": 30
	},
	"text": "<instrumental> ...",
	"usage": { "cost_usd": 0.05, "cost_inr": 4.9 },
	"created": 1748505600
}

Why both? The billing log row is written asynchronously, so the audio.url proxy can 404 for a few seconds right after generation — decode audio.base64 for instant playback, and use audio.url for durable replay (e.g. in history). The audio.url is a gateway-proxied path; append it to https://api.osmapi.com and GET it with your bearer token:

curl -L "https://api.osmapi.com/v1/music/req_…/content" \
  -H "Authorization: Bearer $OSM_API_KEY" -o song.mp3

The gateway proxy-streams the file from the private storage bucket (the bucket is never exposed publicly). Range requests are supported, so <audio> tags seek smoothly.

Inline-only response (when object storage isn't configured, or the org's retention is none):

{
	"id": "req_…",
	"object": "music.generation",
	"audio": {
		"base64": "<MP3 bytes as base64>",
		"mime_type": "audio/mpeg",
		"storage": "inline"
	},
	"text": "<instrumental> ...",
	"usage": { "cost_usd": 0.05, "cost_inr": 4.9 },
	"created": 1748505600
}

storage is "inline" for opt-in retention configs without S3, and "none" for orgs that have explicitly disabled retention — same bytes either way, just a different label.

Save the audio

For S3-backed responses:

curl -L "https://api.osmapi.com/v1/music/$ID/content" \
  -H "Authorization: Bearer $OSM_API_KEY" -o song.mp3

For inline responses:

curl -X POST "https://api.osmapi.com/v1/music" \
  -H "Authorization: Bearer $OSM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"lyria-3-clip-preview","prompt":"…"}' \
  | jq -r '.audio.base64' | base64 -d > song.mp3

Or in TypeScript, branching on the response shape:

import fs from "fs";

const r = await fetch("https://api.osmapi.com/v1/music", {
	method: "POST",
	headers: {
		"Content-Type": "application/json",
		Authorization: `Bearer ${process.env.OSM_API_KEY}`,
	},
	body: JSON.stringify({
		model: "lyria-3-clip-preview",
		prompt: "Upbeat lo-fi hip hop, 85 BPM, instrumental",
	}),
});
const data = await r.json();

if (data.audio.url) {
	// S3-backed — fetch the file via the gateway proxy
	const audio = await fetch(`https://api.osmapi.com${data.audio.url}`, {
		headers: { Authorization: `Bearer ${process.env.OSM_API_KEY}` },
	});
	fs.writeFileSync("song.mp3", Buffer.from(await audio.arrayBuffer()));
} else {
	// Inline — decode the base64 directly
	fs.writeFileSync("song.mp3", Buffer.from(data.audio.base64, "base64"));
}

Pro: reference images

Lyria 3 Pro accepts up to 10 reference images as inline data (Gemini chat-content shape) and composes music inspired by them.

import fs from "fs";

const r = await fetch("https://api.osmapi.com/v1/music", {
	method: "POST",
	headers: {
		"Content-Type": "application/json",
		Authorization: `Bearer ${process.env.OSM_API_KEY}`,
	},
	body: JSON.stringify({
		model: "lyria-3-pro-preview",
		prompt: "Cinematic strings building to a triumphant brass finale, 100 BPM",
		instrumental: true,
		reference_images: [
			"data:image/jpeg;base64,<base64 image bytes>",
			// up to 10
		],
	}),
});
const data = await r.json();
console.log("Lyrics / structure:", data.text);
// Fetch via the proxied URL if S3-backed; else decode inline.

Request fields

FieldTypeRequiredNotes
modelstringOne of the IDs above.
promptstring (≤4096 chars)Describe genre, mood, instruments, tempo, structure.
response_format"mp3"Currently MP3 only (see note above on WAV).
instrumentalbooleanAppends "Instrumental only, no vocals." to the prompt.
lyricsstringCustom lyrics, appended as a Lyrics: block. Use [Verse]/[Chorus] tags. Ignored when instrumental is true.
reference_imagesarray of data URLs or refsPro only, up to 10. JPEG/PNG, ≤8MB each.

Response fields

FieldNotes
idosmAPI request ID — surfaces in the dashboard logs.
audio.base64Base64-encoded MP3 bytes. Always present — decode for instant playback.
audio.urlGateway proxy path for durable replay. Present only when storage="s3". Append to base URL and GET with bearer.
audio.mime_type"audio/mpeg" (MP3).
audio.storage"s3" (hosted), "inline" (returned bytes), or "none" (no copy kept anywhere).
audio.expires_in_daysDays until the hosted file is auto-deleted. Only on storage="s3".
textLyrics and <structure> markers (e.g. <intro>, <chorus>).
usage.cost_usdThe flat upstream price for this generation.
usage.cost_inrWhat you were charged in INR (USD × current rate).

Storage & retention

  • Default: when S3 is configured (it is, in osmAPI prod) and your org's retention is the standard retain mode, every generated track is uploaded to object storage. The response carries audio.url and the file lives for 30 days, then a background worker deletes it.
  • retention=none (org-level setting): the gateway never uploads the bytes. The response carries audio.base64 and storage:"none". Nothing is persisted; if your client doesn't save the bytes immediately, they're gone.
  • No S3 configured: same response shape as retention=none — inline base64 with storage:"inline".

The retention sweep runs every ~5 minutes. It deletes the S3 object first, then marks the log row's mediaCleanedUp flag — so a failed delete is retried, and crashed workers never leave orphan objects.

Media deletion and chat-log retention are independent. Deleting the audio file (ENABLE_MEDIA_CLEANUP) does not delete the prompt or lyrics text from your logs — that's a separate switch (ENABLE_DATA_RETENTION_CLEANUP, off by default, keeps history forever). So after 30 days a track's audio is gone but its prompt still shows in history with an "expired" badge.

List recent tracks (history)

GET /v1/music returns the most recent generations for the bearer's organization. Tracks whose media has been auto-deleted come back with audio: null and expired: true.

curl "https://api.osmapi.com/v1/music?limit=30" \
  -H "Authorization: Bearer $OSM_API_KEY"
{
	"object": "list",
	"data": [
		{
			"id": "req_…",
			"object": "music.generation",
			"prompt": "Upbeat lo-fi hip hop…",
			"created": 1748505600,
			"audio": {
				"url": "/v1/music/req_…/content",
				"mime_type": "audio/mpeg",
				"storage": "s3"
			},
			"usage": { "cost_inr": 4.9 },
			"metadata": {
				"used_model": "lyria-3-clip-preview",
				"used_provider": "google-ai-studio",
				"response_format": "mp3"
			},
			"expired": false,
			"error": false
		}
	]
}

Query params:

  • limit — max 100, default 30.

The history endpoint sets Cache-Control: no-store so newly generated tracks always appear on the next fetch.

Download by id

GET /v1/music/:id/content is the canonical download endpoint. The :id can be either the log row id or the requestId returned by POST — both resolve. Returns:

  • 302 redirect to the durable storage URL when the file is still hosted
  • 410 Gone when the track has been retention-swept
  • 404 when the track isn't yours or doesn't exist

Prompt tips (from Google)

  • Name the genre, mood, and instruments.
  • Add tempo (e.g. 90 BPM) and structure tags (intro / verse / chorus / outro).
  • Use the instrumental flag (or write "instrumental only, no vocals") to skip vocals.
  • Pro: attach reference images for visual inspiration — Lyria reads mood and color.
  • Don't name real artists or copyrighted lyrics — the safety filter blocks these.

Errors

CodeWhen
400Missing/invalid model or prompt, image too large, format wrong.
402Insufficient credits and no provider key configured.
410Track has been retention-swept (older than the 30-day window).
422Blocked by Lyria's safety / RAI filter. You are not charged.
429Provider rate-limited the request.
502Upstream provider error.
504Upstream timed out (we wait up to 4 minutes).

Music generation is not idempotent. osmAPI calls Lyria exactly once per request — there are no automatic retries on 5xx, so a failed network round-trip never double-charges. If you build a retry loop on the client, deduplicate on your side or accept the duplicate cost.

Pricing

Music is billed per generation (not per second or per token). The current rates are visible on each model's page and in the /v1/models response under pricing.audio_music_per_generation (USD). osmAPI converts that to INR at the platform rate before charging your credit balance — same as every other modality.

Where it lives

  • API: POST /v1/music, GET /v1/music, GET /v1/music/:id/content
  • Playground: playground.osmapi.com/music — chat-style composer with reference-image attach (Pro), instrumental + custom-lyrics options, and a scrollable history of recent tracks.
  • Dashboard nav: Music Studio (under Video Studio)
  • Usage analytics: a Music category appears in the cost-by-modality chart as soon as you make your first request.

How is this guide?