Galactic Radio MusicGen API

v1.0.0 — AI-powered music generation with Google Lyria 3

Get raw markdown for LLM context

Authentication

The API supports three authentication methods:

  • Firebase OAuth — Send a Firebase ID token as Authorization: Bearer <token>. Used by the web frontend after Google sign-in.
  • API Key — Send your key as X-API-Key: grm_xxx header. Get a key via POST /api/v1/users/register or OAuth sign-in.
  • Local Dev Bypass — In development mode, requests from localhost are auto-authenticated as admin. No headers needed.

Users

POST /api/v1/users/register No Auth

Register a new user. Returns an API key (shown once).

Request Body

FieldTypeRequiredDescription
emailstringYesUser email
passwordstringYesMin 6 characters
display_namestringNoDisplay name
curl -X POST http://localhost:8216/api/v1/users/register \
  -H "Content-Type: application/json" \
  -d '{"email":"user@example.com","password":"secret123"}'

Response (201)

{
  "uid": "abc123",
  "email": "user@example.com",
  "api_key": "grm_xxxxxxxxxxxx",
  "credits_balance": 3,
  "message": "Account created. Store this API key securely..."
}
POST /api/v1/users/oauth-signin Bearer Token

OAuth sign-in callback. Provisions profile and API key for new users.

curl -X POST http://localhost:8216/api/v1/users/oauth-signin \
  -H "Authorization: Bearer <firebase_id_token>"

Response (200)

{
  "uid": "firebase_uid",
  "email": "user@gmail.com",
  "display_name": "John Doe",
  "api_key": "grm_xxxxxxxxxxxx",  // only for new users
  "credits_balance": 3,
  "is_new_user": true
}
GET /api/v1/users/me Auth Required

Get current user profile.

curl http://localhost:8216/api/v1/users/me \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Response (200)

{
  "uid": "abc123",
  "email": "user@example.com",
  "display_name": "John",
  "bio": "Music enthusiast",
  "avatar_url": "https://...",
  "role": "user",
  "credits_balance": 3,
  "api_key_count": 1,
  "created_at": "2026-04-17T12:00:00+00:00"
}
PATCH /api/v1/users/me Auth Required

Update profile fields (display_name, bio).

Request Body

FieldTypeRequiredDescription
display_namestringNoDisplay name (max 100)
biostringNoShort bio (max 500)
curl -X PATCH http://localhost:8216/api/v1/users/me \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"display_name": "DJ Nebula", "bio": "Making cosmic beats"}'
GET /api/v1/users/me/stats Auth Required

Get aggregated play statistics for the current user's songs.

curl http://localhost:8216/api/v1/users/me/stats \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Response (200)

{
  "total_songs": 12,
  "total_plays": 150,
  "plays_by_location": {"web-player": 100, "discord-bot": 50},
  "top_songs": [
    {"song_id": "song_abc123", "title": "Hit Track", "play_count": 42}
  ]
}
POST /api/v1/users/api-keys Auth Required

Generate a new API key. The raw key is returned once.

curl -X POST http://localhost:8216/api/v1/users/api-keys \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Response (200)

{
  "api_key": "grm_new_key_here",
  "message": "Store this key securely — it will not be shown again"
}
DELETE /api/v1/users/api-keys/{key_id} Auth Required

Revoke an API key by its hash prefix.

curl -X DELETE http://localhost:8216/api/v1/users/api-keys/abc123 \
  -H "X-API-Key: grm_xxxxxxxxxxxx"
POST /api/v1/users/me/avatar Auth Required

Upload a custom avatar image. Max 2MB, JPEG or PNG only.

curl -X POST http://localhost:8216/api/v1/users/me/avatar \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -F "file=@avatar.jpg"

Response (200)

{
  "uid": "abc123",
  "email": "user@example.com",
  "display_name": "John",
  "avatar_url": "https://storage.googleapis.com/.../avatar.jpg",
  ...
}

Songs

POST /api/v1/songs/generate Auth Required

Generate a new song via Google Lyria 3 API. Costs credits.

Request Body

FieldTypeRequiredDescription
titlestringYesSong title (1-200 chars)
descriptionstringNoDescription (max 500)
lyricsstringNoLyrics to use (max 5000)
genrestringNoGenre (e.g. "ambient electronic")
moodstringNoMood (e.g. "ethereal", "upbeat")
artist_namestringNoArtist name to display
artist_urlstringNoArtist profile URL (max 500)
generation_typestringNofull_song (default) or jingle
audio_formatstringNomp3 (default)
bpmintNoTempo 40-300
key_scalestringNoMusical key (e.g. "C minor")
instrumentsstring[]NoInstruments to feature
duration_hintstringNoe.g. "short", "30 seconds"
metaobjectNoArbitrary metadata (station_id, etc.)
curl -X POST http://localhost:8216/api/v1/songs/generate \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Cosmic Dawn",
    "genre": "ambient electronic",
    "mood": "ethereal",
    "artist_name": "DJ Nebula",
    "generation_type": "full_song"
  }'

Response (201)

Returns the full Song object.

POST /api/v1/songs/generate-multi Auth Required

Generate across multiple providers in one request. Same body as /generate plus an optional providers array (defaults to all). A failure in one provider never aborts the others.

Providers

ProviderOutputCredits
sunoReal audio (two variations) if a personal Suno account is configured, else a copy/paste text promptYour own Suno credits (0 app credits)
lyriaPlayable audio (Google Lyria 3)per full_song / jingle
elevenlabsPlayable audio (ElevenLabs Music API)CREDITS_PER_ELEVENLABS
minimaxTabled — the MiniMax Music API is closed to new users, so this provider is never called and always comes back failedNone — never charged
curl -X POST http://localhost:8216/api/v1/songs/generate-multi \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Cosmic Dawn",
    "genre": "ambient electronic",
    "mood": "ethereal",
    "providers": ["suno", "lyria", "elevenlabs"]
  }'

Response (201)

{
  "title": "Cosmic Dawn",
  "credits_charged": 2,
  "credits_balance": 8,
  "results": [
    {"provider": "lyria", "kind": "audio", "status": "completed", "song": { /* Song */ }},
    {"provider": "elevenlabs", "kind": "audio", "status": "completed", "song": { /* Song */ }},
    {"provider": "suno", "kind": "audio", "status": "completed", "song": { /* Song (variation 1) */ }},
    {"provider": "suno", "kind": "audio", "status": "completed", "song": { /* Song (variation 2) */ }}
  ]
}

Each result has kind (audio or text) and status (completed or failed, with an actionable error_message). Audio results carry a full Song; text results carry a text prompt.

GET /api/v1/songs/{song_id} Auth Required

Get a song by ID.

curl http://localhost:8216/api/v1/songs/song_abc123 \
  -H "X-API-Key: grm_xxxxxxxxxxxx"
GET /api/v1/songs Auth Required

List and search songs with filtering, search, and shuffle.

Query Parameters

ParamTypeDefaultDescription
mineboolfalseOnly show my songs
created_bystring[][]Filter by user UID(s) — repeat for multiple
genrestring—Substring match, case-insensitive
moodstring—Substring match, case-insensitive
qstring—Free-text search (title, desc, genre, mood, artist, lyrics)
shuffleboolfalseRandomize results instead of newest-first
statusstringcompletedFilter by status
limitint201-100
offsetint0Pagination offset
# Search for country songs about love
curl "http://localhost:8216/api/v1/songs?q=love&genre=country&limit=10" \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

# 5 random songs for radio shuffle
curl "http://localhost:8216/api/v1/songs?shuffle=true&limit=5" \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Response (200)

{
  "songs": [ ... ],  // array of Song objects
  "total": 42,
  "limit": 20,
  "offset": 0
}
POST /api/v1/songs/{song_id}/play Auth Required

Record a play event for analytics.

Request Body

FieldTypeRequiredDescription
locationstringYesWhere played (e.g. "web-player", "discord-bot")
curl -X POST http://localhost:8216/api/v1/songs/song_abc123/play \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"location": "web-player"}'
PATCH /api/v1/songs/{song_id} Auth Required

Update song metadata. Owner or admin only. At least one field required.

Request Body

FieldTypeRequiredDescription
titlestringNoSong title (1-200 chars)
artist_namestringNoArtist name (max 100)
artist_urlstringNoArtist profile URL (max 500)
descriptionstringNoDescription (max 1000)
notesstringNoTechnical listening feedback, separate from description (max 10,000). Send an empty string to clear. Included in song detail and list responses.
genrestringNoGenre (max 100)
moodstringNoMood (max 100)
curl -X PATCH http://localhost:8216/api/v1/songs/song_abc123 \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"title": "Updated Title", "genre": "jazz"}'

Response (200)

Returns the updated Song object.

POST /api/v1/songs/{song_id}/art Auth Required

Upload cover art for a song. Max 2MB, JPEG or PNG only. Owner or admin only.

curl -X POST http://localhost:8216/api/v1/songs/song_abc123/art \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -F "file=@cover.jpg"

Response (200)

{
  "cover_art_url": "https://storage.googleapis.com/.../cover.jpg",
  "song_id": "song_abc123"
}
DELETE /api/v1/songs/{song_id} Auth Required

Delete a song. Only the owner or an admin can delete.

curl -X DELETE http://localhost:8216/api/v1/songs/song_abc123 \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Credits

GET /api/v1/credits/balance Auth Required

Get current credits balance.

curl http://localhost:8216/api/v1/credits/balance \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

Response (200)

{"credits_balance": 3}
POST /api/v1/credits/purchase Auth Required

Purchase credits (AP2 placeholder — returns payment_required status).

Request Body

FieldTypeRequiredDescription
amountintYesNumber of credits to purchase (> 0)
curl -X POST http://localhost:8216/api/v1/credits/purchase \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"amount": 10}'

Response (200)

{
  "status": "payment_required",
  "ap2_metadata": {
    "protocol": "ap2",
    "provider": "galactic-radio-musicgen",
    "amount_requested": 10,
    "currency": "credits",
    "message": "AP2 payment integration coming soon"
  }
}

A2A Protocol (Agent-to-Agent)

The A2A protocol allows other AI agents to discover and interact with this service programmatically.

GET /.well-known/agent.json No Auth

Agent discovery card. Returns service capabilities, skills, and authentication requirements.

curl http://localhost:8216/.well-known/agent.json

Skills

  • generate_music — Generate a song using Lyria 3
  • search_songs — Search the song catalog
  • get_song — Retrieve a specific song
  • purchase_credits — Purchase credits (AP2 placeholder)
POST /a2a Auth Required

JSON-RPC 2.0 endpoint for agent task execution. Supports tasks/send method.

curl -X POST http://localhost:8216/a2a \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tasks/send",
    "id": 1,
    "params": {
      "skill_id": "search_songs",
      "input": {"q": "ambient", "limit": 5}
    }
  }'

Response (200)

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "id": "task-uuid",
    "status": {"state": "completed"},
    "artifacts": [{
      "parts": [{
        "type": "data",
        "data": {"songs": [...], "total": 5}
      }]
    }]
  }
}

System

GET /api/v1/health No Auth

Health check. Returns service status.

curl http://localhost:8216/api/v1/health

Response (200)

{
  "status": "ok",
  "version": "1.0.0",
  "environment": "development",
  "services": {
    "firestore": "connected",
    "music_gen": "configured"
  }
}
GET /api/v1/info No Auth

Server name, version, and environment.

curl http://localhost:8216/api/v1/info
GET /api/v1/config Admin Only

View current runtime config (models, credits, storage).

curl http://localhost:8216/api/v1/config \
  -H "X-API-Key: grm_admin_key"
PATCH /api/v1/config Admin Only

Update runtime config values (in-memory, not persisted to .env).

curl -X PATCH http://localhost:8216/api/v1/config \
  -H "X-API-Key: grm_admin_key" \
  -H "Content-Type: application/json" \
  -d '{"lyria_pro_model": "lyria-3-pro-v2", "credits_per_full_song": 2}'

Radio Station / Agent Usage

Agents and radio bots can use the search/filter/shuffle endpoints to build playlists:

# Get 5 random songs for a radio set
curl "http://localhost:8216/api/v1/songs?shuffle=true&limit=5" \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

# Get electronic songs matching a mood
curl "http://localhost:8216/api/v1/songs?genre=electronic&mood=chill&limit=10" \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

# Search by keyword
curl "http://localhost:8216/api/v1/songs?q=space+adventure&shuffle=true&limit=3" \
  -H "X-API-Key: grm_xxxxxxxxxxxx"

# Generate a custom jingle for a station break
curl -X POST http://localhost:8216/api/v1/songs/generate \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Station Break Jingle",
    "description": "Upbeat 30-second station identification",
    "generation_type": "jingle",
    "meta": {"station_id": "galactic-fm", "requested_by_agent": "dj-bot"}
  }'

# Record that a song was played
curl -X POST http://localhost:8216/api/v1/songs/song_abc123/play \
  -H "X-API-Key: grm_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"location": "galactic-fm-stream"}'

Song JSON Schema

Every song endpoint returns this shape:

{
  "song_id": "song_abc123def456gh",
  "title": "Cosmic Dawn",
  "artist_name": "DJ Nebula",
  "artist_url": "https://example.com/artist",
  "cover_art_url": "https://storage.googleapis.com/.../cover.jpg",
  "description": "Ambient track for sunrise",
  "notes": "Vocals too loud at 1:20; keep the bass tone",
  "lyrics_input": null,
  "lyrics_generated": "Stars fade into morning light...",
  "genre": "ambient electronic",
  "mood": "ethereal",
  "duration_seconds": null,
  "audio_url": "https://storage.googleapis.com/.../song.mp3",
  "audio_path": "songs/uid/song_id/song.mp3",
  "audio_format": "mp3",
  "file_size_bytes": 4521984,
  "model_used": "lyria-3-pro-preview",
  "generation_prompt": "Create a song titled...",
  "generation_type": "full_song",
  "cost_credits": 1,
  "play_count": 42,
  "play_count_by_location": {"web-player": 30, "discord-bot": 12},
  "status": "completed",
  "error_message": null,
  "created_by": "user_uid",
  "meta": {"station_id": "galactic-fm"},
  "created_at": "2026-04-17T12:00:00+00:00",
  "updated_at": "2026-04-17T12:00:00+00:00"
}

Status values: pending, generating, completed, failed