# SonoVault API Documentation

> Base URL: `https://api.sonovault.now` · Auth: `x-api-key` header

This document is an AI-friendly mirror of https://sonovault.now/docs. It is generated from the same source data as the docs page itself; if anything diverges from the rendered site, the rendered site is authoritative.

## Overview

The SonoVault API gives you access to music metadata for 80 million tracks and counting. Look up ISRC codes, artist credits, label, genre tags, release dates, and cross-platform IDs — all from a single REST endpoint.

Use it to power music discovery apps, catalog management, sync and royalty tools, or any workflow that needs accurate track-level data.

All responses are JSON. Every request requires an API key in the `x-api-key` header. There are no SDKs to install — any HTTP client works.

### What you can do

- **Search tracks** by artist and title — returns ISRC, duration, genre, and artist credits.
- **Look up by ISRC** — get full metadata for a specific recording.
- **Resolve cross-platform IDs** — given a Spotify, Beatport, Apple Music, Tidal, Discogs, or MusicBrainz ID, get matching IDs on all other platforms.
- **Search artists and releases** — find artists by name or browse their discography.
- **New releases** — paginated feed of the latest releases, sorted by artist popularity.

### Try it without signing up

Every endpoint on this page has a **Try it out** button that opens the API explorer. It uses a public demo key so you can make real requests and see live responses — no account required. Click **Try it out** on any endpoint to get started.

## Quickstart

Get from zero to your first API response in under 5 minutes. Create a free account to get an API key — no credit card required.

### 1. Sign up and verify your email

Create a free account at [sonovault.now](https://sonovault.now). We send a verification link to your inbox — click it before making API calls. Until your email is verified, every `/v1` request returns `403 Email not verified`.

### 2. Get your API key

Once verified, grab your key from the [dashboard](/dashboard/keys). All requests must include it in the `x-api-key` header.

### 3. Make your first request

Search for a track by artist and title. The response includes ISRC, duration, genre, and full artist credits.

```bash
curl https://api.sonovault.now/v1/tracks/search \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "artist=Daft Punk" \
  -d "title=One More Time"
```

### 4. Explore the API

Now that you have your first result, try looking up the ISRC with the [ISRC lookup](#sec-isrc-lookup) endpoint or resolve cross-platform links with [Platform links](#sec-platform-links).

## Authentication

All API requests require a valid API key passed in the `x-api-key` header.

```http
x-api-key: svk_live_xxxxxxxxxxxxxxxxxx
```

Test keys prefixed `svk_test_` return mocked data. Live keys prefixed `svk_live_` hit the real database.

> Never expose your API key in client-side code or commit it to version control. Use environment variables.

## Base URL

All API requests use the following base URL:

```http
https://api.sonovault.now
```

All endpoints return JSON with `Content-Type: application/json`. Request parameters are passed as query strings for `GET` requests.

## Rate limits

Rate limits are enforced per API key on a rolling 60-second window. If you exceed your limit, requests return `429 Too Many Requests`.

| Tier | Requests/month | Burst (per 60s) |
| --- | --- | --- |
| Free | 1,000 | 20 |
| Starter | 50,000 | 60 |
| Growth | 500,000 | 300 |
| Scale | 5,000,000 | 1,000 |

Every response includes rate limit headers so you can track your usage and avoid hitting limits:

| Header | Description |
| --- | --- |
| `RateLimit-Limit` | Your burst limit (max requests per 60-second window). |
| `RateLimit-Remaining` | Requests remaining in the current window. |
| `RateLimit-Reset` | Seconds until the current window resets and `RateLimit-Remaining` refills. |

When you receive a `429` response, wait the number of seconds in `RateLimit-Reset` before retrying. For batch workloads, check `RateLimit-Remaining` and throttle proactively rather than waiting for a rejection.

## Errors

All errors return a JSON object with an `error` string:

```json
{
  "error": "Not found"
}
```

| Status | Description |
| --- | --- |
| `400` | Missing or invalid parameters. |
| `401` | Missing or invalid API key. |
| `403` | Endpoint requires a paid plan. [Upgrade](/pricing) to access browse. |
| `410` | Endpoint disabled. The endpoint will return in another form later. |
| `404` | No matching track or resource. |
| `429` | Rate limit exceeded. Check `RateLimit-Reset` header for retry timing. |
| `500` | Unexpected server error. |

## FAQ

**How do I get an API key?**

Sign up for a free account at sonovault.now — no credit card required. Verify your email by clicking the link we send (required before any /v1 request will succeed), then grab your key from the dashboard and include it in the x-api-key header with every request.

**What data formats does the API return?**

All endpoints return JSON with Content-Type: application/json. Request parameters are passed as query strings for GET requests. There are no SDKs to install — any HTTP client works.

**Does the API include lyrics, album artwork, or audio previews?**

No. SonoVault is a text-metadata API: it returns titles, artists, labels, release dates, ISRCs, genres, durations, and cross-platform IDs. Lyrics, album artwork, and audio previews are not served — those assets carry separate licensing restrictions and are intentionally excluded from every endpoint.

**Is there a free tier?**

Yes. The free tier gives you 1,000 requests per month with a 20 requests/minute burst limit. It includes track search, ISRC lookup, and platform links. Track browse and new releases require a paid plan.

**What happens when I hit the rate limit?**

You receive a 429 Too Many Requests response. Check the RateLimit-Reset header for the number of seconds until your window resets. For batch workloads, monitor RateLimit-Remaining and throttle proactively.

**Which music platforms are supported for cross-platform linking?**

The platform links endpoint resolves track IDs across Spotify, Apple Music, Beatport, Tidal, Discogs, and MusicBrainz. Given any one platform ID or an ISRC code, it returns matching IDs on all other platforms.

## Tracks

### Track search

`GET /v1/tracks/search`

Search for tracks by artist and title. Returns ISRC, genre, release dates, and full metadata.

#### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `artist` | query | `string` | required | Artist name. |
| `title` | query | `string` | required | Track title. |
| `limit` | query | `integer` | optional | Max results, 1–100. Default 20. |
| `cursor` | query | `string` | optional | Cursor from previous response for pagination. |

#### Example request

```bash
curl https://api.sonovault.now/v1/tracks/search \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "artist=Daft Punk" \
  -d "title=One More Time"
```

#### Pagination

When more results are available, the response includes a `next_cursor` string. Pass it as the `cursor` parameter to fetch the next page:

```bash
curl https://api.sonovault.now/v1/tracks/search \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "artist=Daft Punk" \
  -d "title=Around" \
  -d "cursor=eyJpZCI6MTIzfQ"
```

#### Example response

```json
{
  "results": [
    {
      "id": 123,
      "title": "One More Time",
      "releases": [
        {
          "id": 1,
          "title": "Discovery",
          "artist": {
            "id": 1,
            "name": "Daft Punk"
          },
          "label": {
            "id": 10,
            "name": "Virgin Records"
          },
          "release_date": "2001-03-12"
        }
      ],
      "artists": [
        {
          "id": 1,
          "name": "Daft Punk",
          "is_primary": true,
          "is_remixer": false
        }
      ],
      "isrc": "GBDUW0000053",
      "duration": 320,
      "genre": [
        "House"
      ],
      "subgenre": [
        "French House"
      ]
    }
  ],
  "next_cursor": "eyJpZCI6MTIzfQ"
}
```

### ISRC lookup

`GET /v1/tracks/isrc/:isrc`

Exact lookup by ISRC code. The ISRC is part of the path — there is no query string. Returns full track detail including genre and subgenre.

#### Path parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `isrc` | path | `string` | required | ISRC code (e.g. GBDUW0000053). |

#### Example request

```bash
curl https://api.sonovault.now/v1/tracks/isrc/GBDUW0000053 \
  -H "x-api-key: YOUR_API_KEY"
```

#### Example response

```json
{
  "id": 123,
  "title": "One More Time",
  "releases": [
    {
      "id": 1,
      "title": "Discovery",
      "artist": {
        "id": 1,
        "name": "Daft Punk"
      },
      "label": {
        "id": 10,
        "name": "Virgin Records"
      },
      "release_date": "2001-03-12"
    }
  ],
  "artists": [
    {
      "id": 1,
      "name": "Daft Punk",
      "is_primary": true,
      "is_remixer": false
    }
  ],
  "isrc": "GBDUW0000053",
  "duration": 320,
  "genre": [
    "House"
  ],
  "subgenre": [
    "French House"
  ]
}
```

### Track by ID

`GET /v1/tracks/:id`

Get a single track by its internal SonoVault track ID. Returns the same track payload as ISRC lookup — title, artists, releases, ISRC, duration, genre, and subgenre.

#### Path parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | `integer` | required | Internal SonoVault track ID. |

#### Example request

```bash
curl https://api.sonovault.now/v1/tracks/123 \
  -H "x-api-key: YOUR_API_KEY"
```

#### Example response

```json
{
  "id": 123,
  "title": "One More Time",
  "releases": [
    {
      "id": 1,
      "title": "Discovery",
      "artist": {
        "id": 1,
        "name": "Daft Punk"
      },
      "label": {
        "id": 10,
        "name": "Virgin Records"
      },
      "release_date": "2001-03-12"
    }
  ],
  "artists": [
    {
      "id": 1,
      "name": "Daft Punk",
      "is_primary": true,
      "is_remixer": false
    }
  ],
  "isrc": "GBDUW0000053",
  "duration": 320,
  "genre": [
    "House"
  ],
  "subgenre": [
    "French House"
  ]
}
```

### Platform links

`GET /v1/tracks/links`

Lightweight cross-platform ID resolver. Given an ISRC or a source-specific ID, returns all known external platform links.

#### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | query | `integer` | optional | Internal SonoVault track ID. Provide one lookup param at a time. |
| `isrc` | query | `string` | optional | ISRC code. |
| `spotify_id` | query | `string` | optional | Spotify track ID. |
| `beatport_id` | query | `string` | optional | Beatport track ID. |
| `discogs_id` | query | `string` | optional | Discogs release ID. |
| `musicbrainz_id` | query | `string` | optional | MusicBrainz recording ID. |
| `applemusic_id` | query | `string` | optional | Apple Music track ID. |
| `tidal_id` | query | `string` | optional | Tidal track ID. |

#### Example request

```bash
curl https://api.sonovault.now/v1/tracks/links \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "isrc=GBDUW0000053"
```

#### Example response

```json
{
  "track_id": 123,
  "title": "One More Time",
  "isrc": "GBDUW0000053",
  "links": [
    {
      "source": "spotify",
      "external_id": "5sICkBXVmaCQk5aISGR3x0",
      "url": "https://open.spotify.com/track/5sICkBXVmaCQk5aISGR3x0"
    },
    {
      "source": "beatport",
      "external_id": "12345678",
      "url": "https://www.beatport.com/track/-/12345678"
    },
    {
      "source": "applemusic",
      "external_id": "1440650",
      "url": "https://music.apple.com/song/1440650"
    },
    {
      "source": "tidal",
      "external_id": "3789025",
      "url": "https://tidal.com/browse/track/3789025"
    }
  ]
}
```

### Bulk resolve

`POST /v1/tracks/resolve`

Resolve up to 100 inputs in a single request — track names, ISRCs, or platform IDs — to canonical SonoVault tracks plus their cross-platform links. One credit is charged per input line. If your monthly quota runs out mid-batch the response is partial: unresolved lines come back with status `skipped_no_credits` instead of an error.

**Request body (JSON)**

- `input_type` (string, required) — one of `track_name`, `isrc`, `sonovault_id`, `spotify_id`, `applemusic_id`, `tidal_id`, `beatport_id`, `discogs_id`, `musicbrainz_id`.
- `items` (array, required) — 1 to 100 entries. For `track_name`, each entry is an object `{ artist, title }`; for every other input type, each entry is a string.

**Response**

- `results` — one entry per input line, in input order: `{ input, status, track, links }`, where `status` is `matched`, `not_found`, or `skipped_no_credits`.
- `partial`, `processed`, `credits_used`, `credits_remaining`, `message` — the batch billing summary.

#### Example request

```bash
curl -X POST https://api.sonovault.now/v1/tracks/resolve \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"input_type":"isrc","items":["GBDUW0000053","USQX91300108"]}'
```

#### Example response

```json
{
  "results": [
    {
      "input": "GBDUW0000053",
      "status": "matched",
      "track": {
        "id": 123,
        "title": "One More Time",
        "releases": [
          {
            "id": 1,
            "title": "Discovery",
            "artist": {
              "id": 1,
              "name": "Daft Punk"
            },
            "label": {
              "id": 10,
              "name": "Virgin Records"
            },
            "release_date": "2001-03-12"
          }
        ],
        "artists": [
          {
            "id": 1,
            "name": "Daft Punk",
            "is_primary": true,
            "is_remixer": false
          }
        ],
        "isrc": "GBDUW0000053",
        "duration": 320,
        "genre": [
          "House"
        ],
        "subgenre": [
          "French House"
        ]
      },
      "links": [
        {
          "source": "spotify",
          "external_id": "5sICkBXVmaCQk5aISGR3x0",
          "url": "https://open.spotify.com/track/5sICkBXVmaCQk5aISGR3x0"
        }
      ]
    },
    {
      "input": "USQX91300108",
      "status": "not_found",
      "track": null,
      "links": []
    }
  ],
  "partial": false,
  "processed": 2,
  "credits_used": 2,
  "credits_remaining": 968,
  "message": null
}
```

### Browse tracks *(paid plans only)*

`GET /v1/tracks/browse`

Discover tracks by filtering on label, artist, genre, or year. Returns up to 20 tracks ordered by release date (newest first) by default; pass `randomize=true` to sample 20 random tracks from the top-popularity pool instead. At least one filter parameter is required — calls with no filters return `400`. Filter by genre with either `genreId` (a canonical id from `GET /v1/genres`) or `genre` (a free-text exact name) — prefer `genreId` as it is stable and unambiguous; use `genre` only to reach rare subgenres that `/v1/genres` does not list. `genre` and `genreId` are mutually exclusive — passing both returns `400`.

#### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `labelId` | query | `integer` | optional | Filter by label ID. |
| `artistId` | query | `integer` | optional | Filter by artist ID. |
| `genre` | query | `string` | optional | Exact genre name, case-insensitive (`House` matches House but not Tech House). Free-text alternative to `genreId` — use it for rare subgenres not listed by `GET /v1/genres`. Mutually exclusive with `genreId`. |
| `genreId` | query | `integer` | optional | Canonical genre id from `GET /v1/genres` — the stable, unambiguous way to filter by genre. Mutually exclusive with `genre`. |
| `year` | query | `integer` | optional | Release year (e.g. 2025). |
| `randomize` | query | `boolean` | optional | If `true`, return 20 random tracks sampled from the top 1000 by popularity instead of the most recent releases. Defaults to `false`. |

#### Example request

```bash
curl https://api.sonovault.now/v1/tracks/browse \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "genre=House" \
  -d "year=2025"
```

#### Example response

```json
{
  "results": [
    {
      "id": 456,
      "title": "One More Time",
      "releases": [
        {
          "id": 1,
          "title": "Discovery",
          "artist": {
            "id": 1,
            "name": "Daft Punk"
          },
          "label": {
            "id": 10,
            "name": "Virgin Records"
          },
          "release_date": "2001-03-12"
        }
      ],
      "artists": [
        {
          "id": 1,
          "name": "Daft Punk",
          "is_primary": true,
          "is_remixer": false
        }
      ],
      "isrc": "GBDUW0000053",
      "duration": 320,
      "genre": [
        "House"
      ],
      "subgenre": [
        "French House"
      ]
    }
  ]
}
```

## Artists

### Artist search

`GET /v1/artists/search`

Search for artists by name. Returns each artist with country, formation year (and full formation date when day-level precision is known), social links, and Wikidata ID where available.

#### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `name` | query | `string` | required | Artist name to search. |
| `limit` | query | `integer` | optional | Max results, 1–100. Default 20. |
| `cursor` | query | `string` | optional | Cursor from previous response for pagination. |

#### Example request

```bash
curl https://api.sonovault.now/v1/artists/search \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "name=Daft Punk"
```

#### Pagination

When more results are available, the response includes a `next_cursor` string. Pass it as the `cursor` parameter to fetch the next page:

```bash
curl https://api.sonovault.now/v1/artists/search \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "name=Daft Punk" \
  -d "cursor=0:5000000:1"
```

#### Example response

```json
{
  "results": [
    {
      "id": 1,
      "name": "Daft Punk",
      "country": "France",
      "formation_year": 1993,
      "formation_date": null,
      "social_links": {
        "website": "https://daftpunk.com/",
        "twitter": "daftpunk_music",
        "instagram": "daftpunk",
        "facebook": "100044021024570"
      },
      "wikidata_id": "Q185828"
    }
  ],
  "next_cursor": "0:5000000:1"
}
```

### Artist by ID

`GET /v1/artists/:id`

Get artist by ID. Returns the public artist fields (country, formation_year + optional formation_date, social_links, wikidata_id) flattened alongside a release_count summary. For the actual release list, use /v1/artists/:id/releases — the embedded array was removed because it became huge for prolific artists.

#### Path parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | `integer` | required | Artist ID. |

#### Example request

```bash
curl https://api.sonovault.now/v1/artists/1 \
  -H "x-api-key: YOUR_API_KEY"
```

#### Example response

```json
{
  "id": 1,
  "name": "Daft Punk",
  "country": "France",
  "formation_year": 1993,
  "formation_date": null,
  "social_links": {
    "website": "https://daftpunk.com/",
    "twitter": "daftpunk_music",
    "instagram": "daftpunk",
    "facebook": "100044021024570"
  },
  "wikidata_id": "Q185828",
  "release_count": 14
}
```

### Artists by label

`GET /v1/labels/:id/artists`

Paginated list of artists with releases on a specific label, ordered by number of releases on that label (descending). Each artist carries the public profile fields and a release_count scoped to this label.

#### Path parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | `integer` | required | Label ID. |

#### Query parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | optional | Max results, 1–100. Default 20. |
| `cursor` | query | `string` | optional | Cursor from previous response for pagination. |

#### Example request

```bash
curl https://api.sonovault.now/v1/labels/1/artists \
  -H "x-api-key: YOUR_API_KEY"
```

#### Example response

```json
{
  "results": [
    {
      "id": 5,
      "name": "Adam Beyer",
      "country": "Sweden",
      "formation_year": 1976,
      "formation_date": null,
      "social_links": {
        "website": "https://adambeyer.com",
        "twitter": "adam_beyer"
      },
      "wikidata_id": "Q469843",
      "release_count": 42
    }
  ],
  "next_cursor": "42:5"
}
```

## Labels

### Label search

`GET /v1/labels/search`

Search for record labels by name. Returns labels sorted by relevance and release count.

#### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `name` | query | `string` | required | Label name to search. |
| `limit` | query | `integer` | optional | Max results, 1–100. Default 20. |
| `cursor` | query | `string` | optional | Cursor from previous response for pagination. |

#### Example request

```bash
curl https://api.sonovault.now/v1/labels/search \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "name=Drumcode"
```

#### Pagination

When more results are available, the response includes a `next_cursor` string. Pass it as the `cursor` parameter to fetch the next page:

```bash
curl https://api.sonovault.now/v1/labels/search \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "name=Drumcode" \
  -d "cursor=0:42:1"
```

#### Example response

```json
{
  "results": [
    {
      "id": 1,
      "name": "Drumcode",
      "release_count": 842
    }
  ],
  "next_cursor": "0:842:1"
}
```

### Label by ID

`GET /v1/labels/:id`

Get label by ID. Returns the public label fields (id, name) flattened alongside release_count and artist_count summaries. For the actual lists, use /v1/labels/:id/releases and /v1/labels/:id/artists — embedded arrays were removed because they became huge for prolific labels.

#### Path parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | `integer` | required | Label ID. |

#### Example request

```bash
curl https://api.sonovault.now/v1/labels/1 \
  -H "x-api-key: YOUR_API_KEY"
```

#### Example response

```json
{
  "id": 1,
  "name": "Drumcode",
  "release_count": 842,
  "artist_count": 67
}
```

## Releases

### Releases by artist

`GET /v1/artists/:id/releases`

Paginated feed of releases by a specific artist, ordered by release date (newest first). The per-item artist field is omitted (the caller already knows it).

#### Path parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | `integer` | required | Artist ID. |

#### Query parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | optional | Max results, 1–100. Default 20. |
| `cursor` | query | `string` | optional | Cursor from previous response for pagination. |

#### Example request

```bash
curl https://api.sonovault.now/v1/artists/1/releases \
  -H "x-api-key: YOUR_API_KEY"
```

#### Example response

```json
{
  "results": [
    {
      "id": 1,
      "title": "Discovery",
      "label": {
        "id": 10,
        "name": "Virgin Records"
      },
      "release_date": "2001-03-12",
      "catalog_no": "724384960650",
      "track_count": 14
    }
  ],
  "next_cursor": "2001-03-12:1"
}
```

### Releases by label

`GET /v1/labels/:id/releases`

Paginated feed of releases on a specific label, ordered by release date (newest first). Each result carries the primary artist, label, catalog number, and track count.

#### Path parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | `integer` | required | Label ID. |

#### Query parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | optional | Max results, 1–100. Default 20. |
| `cursor` | query | `string` | optional | Cursor from previous response for pagination. |

#### Example request

```bash
curl https://api.sonovault.now/v1/labels/1/releases \
  -H "x-api-key: YOUR_API_KEY"
```

#### Example response

```json
{
  "results": [
    {
      "id": 10,
      "title": "Drumcode 01",
      "artist": {
        "id": 5,
        "name": "Adam Beyer"
      },
      "label": {
        "id": 1,
        "name": "Drumcode"
      },
      "release_date": "2024-03-15",
      "catalog_no": "DC001",
      "track_count": 8
    }
  ],
  "next_cursor": "2024-03-15:10"
}
```

### Release search

`GET /v1/releases/search`

Search for releases by title, optionally filtered by artist.

#### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `title` | query | `string` | required | Release title to search. |
| `artist` | query | `string` | optional | Artist name filter. |
| `limit` | query | `integer` | optional | Max results, 1–100. Default 20. |
| `cursor` | query | `string` | optional | Cursor from previous response for pagination. |

#### Example request

```bash
curl https://api.sonovault.now/v1/releases/search \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "title=Discovery" \
  -d "artist=Daft Punk"
```

#### Pagination

When more results are available, the response includes a `next_cursor` string. Pass it as the `cursor` parameter to fetch the next page:

```bash
curl https://api.sonovault.now/v1/releases/search \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "title=Discovery" \
  -d "cursor=0:85:1"
```

#### Example response

```json
{
  "results": [
    {
      "id": 1,
      "title": "Discovery",
      "artist": {
        "id": 1,
        "name": "Daft Punk"
      },
      "label": {
        "id": 10,
        "name": "Virgin Records"
      },
      "release_date": "2001-03-12"
    }
  ],
  "next_cursor": "0:85:1"
}
```

### Release by ID

`GET /v1/releases/:id`

Get release by ID. Returns the release fields flat with nested artist and label objects, plus a peer tracks[] array — each track has ISRC, genre, subgenre, and artist credits.

#### Path parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | `integer` | required | Release ID. |

#### Example request

```bash
curl https://api.sonovault.now/v1/releases/1 \
  -H "x-api-key: YOUR_API_KEY"
```

#### Example response

```json
{
  "id": 1,
  "title": "Discovery",
  "artist": {
    "id": 1,
    "name": "Daft Punk"
  },
  "label": {
    "id": 10,
    "name": "Virgin Records"
  },
  "release_date": "2001-03-12",
  "catalog_no": "724384960650",
  "tracks": [
    {
      "id": 123,
      "title": "One More Time",
      "duration": 320,
      "isrc": "GBDUW0000053",
      "artists": [
        {
          "id": 1,
          "name": "Daft Punk",
          "is_primary": true,
          "is_remixer": false
        }
      ],
      "genre": [
        "House"
      ],
      "subgenre": [
        "French House"
      ]
    }
  ]
}
```

### New releases *(paid plans only)*

`GET /v1/releases/new`

Paginated feed of releases ordered by release date (newest first).

#### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | optional | 1–100. Default 20. |
| `cursor` | query | `string` | optional | Cursor from previous response for pagination. |

#### Example request

```bash
curl https://api.sonovault.now/v1/releases/new \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "limit=20"
```

#### Pagination

When more results are available, the response includes a `next_cursor` string. Pass it as the `cursor` parameter to fetch the next page:

```bash
curl https://api.sonovault.now/v1/releases/new \
  -H "x-api-key: YOUR_API_KEY" \
  -G   -d "limit=20" \
  -d "cursor=2026-03-22:78:789"
```

#### Example response

```json
{
  "results": [
    {
      "id": 789,
      "title": "Resonance EP",
      "artist": {
        "id": 3,
        "name": "Bicep"
      },
      "label": {
        "id": 42,
        "name": "Ninja Tune"
      },
      "release_date": "2026-03-22",
      "track_count": 4
    }
  ],
  "next_cursor": "2026-03-22:78:789"
}
```

## Genres

### List genres

`GET /v1/genres`

Lists every canonical genre and subgenre SonoVault recognises. Use this to find the numeric `genreId` for a genre suggestion — the suggestion endpoint validates against this exact list, so it never goes out of date.

#### Example request

```bash
curl https://api.sonovault.now/v1/genres \
  -H "x-api-key: YOUR_API_KEY"
```

#### Example response

```json
{
  "genres": [
    {
      "id": 12,
      "name": "House",
      "type": "main",
      "parent": null
    },
    {
      "id": 47,
      "name": "French House",
      "type": "subgenre",
      "parent": "House"
    }
  ]
}
```

## Suggestions

### Suggest a genre *(paid plans only)*

`POST /v1/tracks/:id/suggestions`

Suggest a better genre for a track. Suggestions are reviewed by the SonoVault team before they affect the catalog — a successful call returns the suggestion with `status: "pending"`. Requires a paid plan.

**Request body (JSON)**

- `genreId` (integer, required) — a genre id from `GET /v1/genres`.
- `note` (string, optional) — a short explanation for the reviewer, max 100 characters.

**Limits**

- Up to 100 suggestions per day per account. Can be increased upon request.

#### Path parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | `integer` | required | Internal SonoVault track ID. |

#### Example request

```bash
curl -X POST https://api.sonovault.now/v1/tracks/123/suggestions \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"genreId":47,"note":"This is clearly French House, not Pop."}'
```

#### Example response

```json
{
  "id": 5012,
  "track_id": 123,
  "field": "genre",
  "proposed_value": {
    "genre_id": 47,
    "genre_name": "French House"
  },
  "current_value": {
    "genre": [
      "Pop"
    ],
    "subgenre": []
  },
  "note": "This is clearly French House, not Pop.",
  "status": "pending",
  "review_note": null,
  "created_at": "2026-05-15T10:32:00.000Z",
  "reviewed_at": null
}
```

### List your suggestions

`GET /v1/suggestions`

Lists the edit suggestions you have submitted and their review status (`pending`, `approved`, or `rejected`), newest first.

#### Parameters

| Parameter | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | optional | Max results, 1–100. Default 20. |
| `cursor` | query | `string` | optional | Cursor from previous response for pagination. |

#### Example request

```bash
curl https://api.sonovault.now/v1/suggestions \
  -H "x-api-key: YOUR_API_KEY"
```

#### Example response

```json
{
  "results": [
    {
      "id": 5012,
      "track_id": 123,
      "field": "genre",
      "proposed_value": {
        "genre_id": 47,
        "genre_name": "French House"
      },
      "current_value": {
        "genre": [
          "Pop"
        ],
        "subgenre": []
      },
      "note": "This is clearly French House, not Pop.",
      "status": "approved",
      "review_note": null,
      "created_at": "2026-05-15T10:32:00.000Z",
      "reviewed_at": "2026-05-15T14:05:00.000Z"
    }
  ],
  "next_cursor": null
}
```
