# Analyze Songs

Source: https://recoupable.dev/docs/api-reference/songs/analyze

Analyze music using a state-of-the-art Audio Language Model ([Music Flamingo](https://www.universalmusic.com/universal-music-group-to-transform-music-experience-for-billions-of-fans-with-nvidia-ai/)) that listens directly to the audio waveform.

## POST /api/songs/analyze

Full OpenAPI specification: https://recoupable.dev/docs/spec/releases.json

## Authentication

This operation requires one of the security alternatives in the specification below. Security scheme definitions are included where present in the published specification.

[Authentication guide](https://recoupable.dev/docs/authentication)

## Operation and referenced schemas

```json
{
  "openapi": "3.1.0",
  "info": {
    "title": "Recoup API - Releases",
    "description": "API documentation for the Recoup platform - an AI agent platform for the music industry",
    "license": {
      "name": "MIT"
    },
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.recoupable.dev"
    }
  ],
  "paths": {
    "/api/songs/analyze": {
      "post": {
        "description": "Analyze music using a state-of-the-art Audio Language Model ([Music Flamingo](https://www.universalmusic.com/universal-music-group-to-transform-music-experience-for-billions-of-fans-with-nvidia-ai/)) that listens directly to the audio waveform.",
        "security": [
          {
            "apiKeyAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "description": "Music analysis request",
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SongAnalyzeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Music analysis completed successfully",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad request — missing or invalid fields, including a missing `audio_url` (`missing_fields: [\"audio_url\"]`) or both `preset` and `prompt` in one request",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized — invalid or missing API key / Bearer token",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits — the balance cannot cover the base price of this request (`$0.05` per model call, `$0.65` for `full_report`). The model is not called, nothing is charged, and no Stripe object is created. The body includes a static `billingUrl`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeInsufficientCreditsResponse"
                }
              }
            }
          },
          "422": {
            "description": "Unverifiable audio. Before the model is called, `audio_url` is checked with a `HEAD` request (a ranged `GET` for hosts that reject `HEAD`). It must answer 2xx within 10 seconds with an `audio/*` or `application/octet-stream` content type. When it does not, the model is not called and nothing is charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeAudioUrlErrorResponse"
                }
              }
            }
          },
          "500": {
            "description": "Server error — upstream model unavailable or inference failed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SongAnalyzeErrorResponse"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "SongAnalyzeRequest": {
        "type": "object",
        "description": "Provide exactly one of `preset` or `prompt`, plus `audio_url`. Use `preset` for structured analysis workflows, or `prompt` for free-form questions about the audio.",
        "required": [
          "audio_url"
        ],
        "properties": {
          "preset": {
            "type": "string",
            "enum": [
              "catalog_metadata",
              "mood_tags",
              "lyric_transcription",
              "mix_feedback",
              "song_description",
              "music_theory",
              "similar_artists",
              "sample_detection",
              "sync_brief_match",
              "audience_profile",
              "content_advisory",
              "playlist_pitch",
              "artist_development_notes",
              "full_report"
            ],
            "description": "Name of a curated analysis preset. Use instead of prompt for structured, optimized output. The 'full_report' preset runs all 13 presets in parallel and returns a comprehensive report. See [List Analyze Presets](/api-reference/songs/analyze-presets) for the full list of available presets.",
            "example": "catalog_metadata"
          },
          "prompt": {
            "type": "string",
            "minLength": 1,
            "maxLength": 24000,
            "description": "Text prompt or question about the music",
            "example": "Describe the genre, tempo, and mood of this track."
          },
          "audio_url": {
            "type": "string",
            "format": "uri",
            "description": "Public URL to an audio file (MP3, WAV, or FLAC, up to 20 minutes). Required in every mode, including a custom `prompt`. Verified before the model is called: the URL must answer a `HEAD` request (or a ranged `GET`) with 2xx within 10 seconds and an `audio/*` or `application/octet-stream` content type, else the request returns 422 and nothing is charged.",
            "example": "https://example.com/song.mp3"
          },
          "max_new_tokens": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2048,
            "default": 512,
            "description": "Maximum number of tokens to generate",
            "example": 512
          },
          "temperature": {
            "type": "number",
            "minimum": 0,
            "maximum": 2,
            "default": 1,
            "description": "Controls output creativity — higher values produce more varied responses",
            "example": 0.7
          },
          "top_p": {
            "type": "number",
            "minimum": 0,
            "maximum": 1,
            "default": 1,
            "description": "Nucleus sampling probability cutoff",
            "example": 0.9
          },
          "do_sample": {
            "type": "boolean",
            "default": false,
            "description": "Enable sampling (set true when using temperature or top_p)",
            "example": false
          }
        }
      },
      "SongAnalyzeResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "success"
            ],
            "description": "Request status"
          },
          "preset": {
            "type": "string",
            "description": "Preset used for analysis, when applicable",
            "example": "catalog_metadata"
          },
          "response": {
            "description": "Model output for single-preset or custom-prompt analysis. May be plain text or structured JSON depending on the preset."
          },
          "report": {
            "type": "object",
            "description": "Full report payload returned only when using the `full_report` preset"
          },
          "elapsed_seconds": {
            "type": "number",
            "format": "float",
            "description": "Inference time in seconds"
          }
        }
      },
      "SongAnalyzeErrorResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ],
            "description": "Error status"
          },
          "missing_fields": {
            "type": "array",
            "description": "Path to the first invalid or missing field when validation fails",
            "items": {
              "type": "string"
            }
          },
          "error": {
            "type": "string",
            "description": "Error message describing what went wrong"
          }
        }
      },
      "SongAnalyzeInsufficientCreditsResponse": {
        "type": "object",
        "description": "Returned (402) when the account lacks credits for the analysis. Nothing is charged and no Stripe object is created. Credits must be bought explicitly via [`POST /api/credits/sessions`](/api-reference/credits/sessions-create).",
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "insufficient_credits"
            ]
          },
          "remaining_credits": {
            "type": "integer",
            "description": "The account's balance, in credits (micro-dollars: 1,000,000 = `$1.00`).",
            "example": 0
          },
          "required_credits": {
            "type": "integer",
            "description": "The base price of this request, in credits: 50000 (`$0.05`) for a single preset or custom prompt, 650000 (`$0.65`) for `full_report`. The per-second inference charge is added after the model returns.",
            "example": 50000
          },
          "billingUrl": {
            "type": "string",
            "description": "Static link to the Recoup app, where a human can save a card and buy credits. It is a constant, not a freshly minted Stripe Checkout Session, so a credit-gated endpoint that keeps returning 402 creates nothing. To buy credits programmatically, call [`POST /api/credits/sessions`](/api-reference/credits/sessions-create).",
            "example": "https://app.recoupable.dev"
          }
        },
        "required": [
          "error",
          "remaining_credits",
          "required_credits",
          "billingUrl"
        ]
      },
      "SongAnalyzeAudioUrlErrorResponse": {
        "type": "object",
        "description": "Returned (422) when `audio_url` could not be verified. The model is not called and nothing is charged.",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "error"
            ]
          },
          "error": {
            "type": "string",
            "enum": [
              "audio_url_unreachable",
              "audio_url_not_audio"
            ],
            "description": "`audio_url_unreachable`: the URL did not answer 2xx within 10 seconds. `audio_url_not_audio`: it answered, but with a content type that is not `audio/*` or `application/octet-stream`.",
            "example": "audio_url_not_audio"
          },
          "message": {
            "type": "string",
            "description": "What the check observed, for the caller's logs.",
            "example": "audio_url answered 200 with content type text/html"
          }
        },
        "required": [
          "status",
          "error",
          "message"
        ]
      }
    },
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "Your Recoup API key. [Learn more](/quickstart#api-keys)."
      },
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer"
      }
    }
  }
}
```
