suno-cli 0.5.0

Generate AI music from your terminal — Suno v5.5 with tags, exclude, vocal control, and all generation features
# Suno API Intelligence — Reverse-Engineered April 6, 2026

## Auth
- **Base URL**: `https://studio-api-prod.suno.com`
- **Auth**: Clerk-based. Browser has Clerk session cookies → exchanges for JWT → JWT used as `Authorization: Bearer <jwt>`
- **Required headers**:
  - `authorization: Bearer <jwt>`
  - `device-id: <uuid>` (from browser, persisted)
  - `browser-token: {"token":"<base64({"timestamp":<ms>})>"}` (dynamic, generated per-request)
  - `origin: https://suno.com`
  - `referer: https://suno.com/`
- **JWT lifetime**: ~1 hour. Auto-refreshed by Clerk SDK in browser.
- **Clerk session ID**: Found in JWT `sid` claim (e.g., `session_eece6e4f36131cbcb12aeb`)

## Account — Premier Plan
- Credits: 10,755 total remaining
- Monthly: 45/10,000 used
- Period ends: 2026-05-01
- Features: v4, cover, edit_mode, persona, stems, negative_tags, remaster, video, custom_models, etc.

## Models (from /api/billing/info/)

| Display Name | External Key | Default | Max Prompt | Max Tags | Max Neg Tags | Max GPT Desc |
|---|---|---|---|---|---|---|
| **v5.5** | `chirp-fenix` | **YES** | 5000 | 1000 | 1000 | 500 |
| v5 | `chirp-crow` | No | 5000 | 1000 | 1000 | 500 |
| v4.5+ | `chirp-bluejay` | No | 5000 | 1000 | 1000 | 500 |
| v4.5 | `chirp-auk` | No | 5000 | 1000 | 1000 | 500 |
| v4.5-all | `chirp-auk-turbo` | Free default | 5000 | 1000 | 1000 | 500 |
| v4 | `chirp-v4` | No | 3000 | 200 | 1000 | 500 |
| v3.5 | `chirp-v3-5` | No | 3000 | 200 | 1000 | 500 |
| v3 | `chirp-v3-0` | No | 1250 | 200 | 1000 | 500 |
| v2 | `chirp-v2-xxl-alpha` | No | 1250 | 200 | 1000 | 500 |

### Remaster Models
| Name | Key |
|---|---|
| v5.5 (default) | `chirp-flounder` |
| v5 | `chirp-carp` |
| v4.5+ | `chirp-bass` |

## Verified Endpoints

### GET /api/billing/info/
Returns full account info, credits, plan, models, features, limits.

### POST /api/generate/lyrics/
**Request**: `{"prompt": "description of song"}`
**Response**: `{"id": "<uuid>"}` (async — poll for result)

### GET /api/generate/lyrics/{id}
**Response** (when complete):
```json
{
  "text": "[Verse 1]\n...\n[Chorus]\n...",
  "title": "Generated Title",
  "status": "complete",
  "error_message": "",
  "tags": ["style description auto-generated by Suno"]
}
```

### POST /api/generate/v2/
**Generate music**. Payload (from gcui-art/suno-api source):
```json
{
  "make_instrumental": false,
  "mv": "chirp-fenix",
  "prompt": "",
  "generation_type": "TEXT",
  "continue_at": null,
  "continue_clip_id": null,
  "task": null,
  "token": "<captcha_token>",
  "gpt_description_prompt": "a happy pop song about summer",
  "tags": "pop, upbeat, synths",
  "title": "Summer Vibes",
  "negative_tags": "metal, heavy, dark"
}
```

**IMPORTANT**: Requires `token` field (captcha). gcui-art uses Playwright to intercept browser captcha flow.

**Two modes**:
1. **Description mode** (`gpt_description_prompt` set, `prompt` empty) — Suno writes lyrics from description
2. **Custom mode** (`prompt` = lyrics, `tags` + `title` + `negative_tags` set)

**Response**: `{"clips": [...], "metadata": {...}, "status": "..."}`

### POST /api/generate/concat/v2/
Concatenate/extend clips. `{"clip_id": "<id>"}`

### POST /api/feed/v3
**Request**: `{"page": 0}`
**Response**: `{"clips": [...], "next_cursor": "...", "has_more": true}`

Clip structure:
```
id, title, status, model_name, audio_url, audio_url_2, video_url,
image_url, image_large_url, created_at, play_count, upvote_count,
metadata: { tags, prompt, duration, avg_bpm, min_bpm, max_bpm,
            has_stem, is_mumble, is_remix, make_instrumental, type,
            can_remix, priority, stream, uses_latest_model }
```

### GET /api/playlist/me
User's playlists. Returns `{"num_total_results": N, "current_page": N, "playlists": [...]}`

### GET /api/trending/
Trending clips. Returns playlist-like structure.

### POST /api/generate/stems/
Stem separation (not tested, needs POST).

### POST /api/cover/
Cover generation (not tested, likely needs POST).

### POST /api/remaster/
Remaster with different model (not tested).

## Voices / Persona Creation Flow (captured April 6, 2026)

Full pipeline for creating a Voice persona from audio:

### Step 1: Upload initial voice sample
The S3 presigned upload happens first (not captured here), then:
```
POST /api/uploads/audio/{upload_id}/upload-finish/
```
Response: `200 OK` (empty body, content-length: 2)

### Step 2: Poll upload status
```
GET /api/uploads/audio/{upload_id}/
```
Response: JSON with processing status.

### Step 3: Extract vocal stem
```
POST /api/processed_clip/voice-vox-stem
Content-Length: ~90 bytes
```
Extracts clean vocals from uploaded audio. Body likely: `{"upload_id": "<id>"}`.
Called multiple times — once per upload (sample + verification).

### Step 4: Record & upload verification phrase
User reads: "Listening to the melody of a gentle summer breeze"
Second upload goes through the same upload-finish flow with a new upload_id.

### Step 5: Voice verification
```
POST /api/voice-verification/
Content-Length: 179 bytes
```
Verifies the voice matches. Body likely includes both upload IDs + verification text.

### Step 6: Create persona
```
POST /api/persona/create/
Content-Length: 47261 bytes (large — likely includes audio data as base64)
```
Creates the voice persona from the verified audio clips.

### Endpoints summary:
- `POST /api/uploads/audio/{id}/upload-finish/` — mark upload complete
- `GET /api/uploads/audio/{id}/` — poll upload processing
- `POST /api/processed_clip/voice-vox-stem` — extract vocals
- `POST /api/voice-verification/` — verify voice sample
- `POST /api/persona/create/` — create voice persona (47KB payload)

### To implement in CLI:
Need to capture the REQUEST BODIES (not just headers) to see exact JSON payloads.
The S3 presigned upload step (before upload-finish) also needs capturing.

## Key Insights for Rust CLI

1. **Captcha is the main challenge** — generation requires a captcha token that gcui-art solves via Playwright browser automation
2. **Lyrics generation is free and easy** — no captcha needed, just JWT auth
3. **JWT refresh** — need Clerk cookie exchange or session keepalive
4. **Browser-token header** — dynamically generated from current timestamp, base64-encoded
5. **Cookie-based approach** — store Clerk session cookies, exchange for JWT via `auth.suno.com/v1/client/sessions/<session_id>/tokens`
6. **Two auth strategies**:
   a. Cookie-based: Store browser cookies, auto-refresh JWT (what gcui-art does)
   b. Direct JWT: User pastes JWT, works for ~1 hour (simpler but expires)