Files
metadata-agregator/docs/research/lidarr-metadata-api/analysis/API.md
T
Alexander a1f6701bac feat: initial implementation of metadata aggregator
- gRPC service with MusicBrainz provider
- PostgreSQL schema with migrations
- Service layer with database-first caching
- Repository pattern for data access
- YAML configuration support
- Research documentation for 17 music metadata projects
2026-04-28 16:28:53 +02:00

1162 lines
27 KiB
Markdown

# Lidarr Metadata API - API Reference
## Base URL and Versioning
**Production deployment**:
- Stable: `https://api.lidarr.audio/api/v0.3`
- Testing: `https://api.lidarr.audio/api/testing`
**Local development**:
- Default: `http://localhost:5001`
- Configurable via `APPLICATION_ROOT` environment variable
**Version strategy**: Dual deployment with stable (v0.3) and testing versions running simultaneously.
## Authentication
**Read endpoints**: No authentication required (public access)
**Write endpoints**: API key authentication via header
```http
POST /invalidate HTTP/1.1
X-Api-Key: your-api-key-here
```
**Configuration**:
```bash
export INVALIDATE_APIKEY=your-secure-key
```
**Default**: `replaceme` (insecure, must change in production)
## Response Format
All endpoints return JSON with consistent structure.
### Common Response Headers
```http
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: s-maxage=2592000, max-age=0
X-Application-Version: 10.0.0.0
```
**Cache-Control explanation**:
- `s-maxage=2592000`: CDN can cache for 30 days
- `max-age=0`: Clients must revalidate (always check CDN/origin)
### Error Response Format
```json
{
"error": "Artist not found",
"status": 404,
"mbid": "invalid-mbid-format"
}
```
**HTTP status codes**:
- `200`: Success
- `400`: Bad request (invalid parameters)
- `401`: Unauthorized (missing/invalid API key)
- `404`: Resource not found
- `429`: Rate limit exceeded
- `500`: Internal server error
- `503`: Service unavailable (database/cache down)
## Health and Version Endpoints
### GET /
**Purpose**: Health check and version information
**Parameters**: None
**Response**:
```json
{
"version": "10.0.0.0",
"status": "healthy",
"timestamp": "2025-04-28T12:34:56Z"
}
```
**Cache**: No caching
**Use case**: Load balancer health checks, monitoring
## Artist Endpoints
### GET /artist/{mbid}
**Purpose**: Retrieve complete artist metadata with releases
**Parameters**:
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `mbid` | UUID | Yes | - | MusicBrainz artist ID |
| `primTypes` | String | No | All | Primary release types (comma-separated) |
| `secTypes` | String | No | All | Secondary release types (comma-separated) |
| `releaseStatuses` | String | No | All | Release statuses (comma-separated) |
**Primary release types**:
- `Album`: Studio albums
- `Single`: Singles
- `EP`: Extended plays
- `Broadcast`: Radio/TV broadcasts
- `Other`: Miscellaneous
**Secondary release types**:
- `Compilation`: Compilation albums
- `Soundtrack`: Movie/game soundtracks
- `Spokenword`: Audiobooks, poetry
- `Interview`: Interview recordings
- `Audiobook`: Audiobooks
- `Live`: Live recordings
- `Remix`: Remix albums
- `DJ-mix`: DJ mix albums
- `Mixtape/Street`: Mixtapes
**Release statuses**:
- `Official`: Official releases
- `Promotion`: Promotional releases
- `Bootleg`: Bootleg recordings
- `Pseudo-Release`: Pseudo-releases
**Example request**:
```http
GET /artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da?primTypes=Album,Single&releaseStatuses=Official HTTP/1.1
```
**Response**:
```json
{
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
"OldIds": [],
"ArtistName": "Nirvana",
"SortName": "Nirvana",
"Disambiguation": "90s US grunge band",
"Overview": "Nirvana was an American rock band formed in Aberdeen, Washington, in 1987...",
"Type": "Group",
"Status": "Ended",
"Gender": null,
"Hometown": "Aberdeen, WA, United States",
"StartYear": 1987,
"EndYear": 1994,
"ArtistAliases": [
{
"Name": "ニルヴァーナ",
"SortName": "ニルヴァーナ",
"Locale": "ja",
"Primary": true
}
],
"Links": [
{
"Url": "https://www.spotify.com/artist/6olE6TJLqED3rqDCT0FyPh",
"Name": "spotify"
},
{
"Url": "https://www.allmusic.com/artist/mn0000352188",
"Name": "allmusic"
},
{
"Url": "https://www.discogs.com/artist/65450",
"Name": "discogs"
},
{
"Url": "https://en.wikipedia.org/wiki/Nirvana_(band)",
"Name": "wikipedia"
}
],
"Images": [
{
"Url": "https://imagecache.lidarr.audio/fanart/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/poster.jpg",
"CoverType": "poster",
"Extension": ".jpg"
},
{
"Url": "https://imagecache.lidarr.audio/fanart/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/banner.jpg",
"CoverType": "banner",
"Extension": ".jpg"
},
{
"Url": "https://imagecache.lidarr.audio/fanart/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/logo.png",
"CoverType": "logo",
"Extension": ".png"
},
{
"Url": "https://imagecache.lidarr.audio/fanart/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/fanart.jpg",
"CoverType": "fanart",
"Extension": ".jpg"
}
],
"Genres": [
"Grunge",
"Alternative Rock",
"Punk Rock"
],
"Rating": {
"Votes": 42,
"Value": 4.8
},
"Albums": [
{
"Id": "1b022e01-4da6-387b-8658-8678046e4cef",
"Title": "Nevermind",
"Disambiguation": "",
"ReleaseDate": "1991-09-24",
"Type": "Album",
"SecondaryTypes": [],
"Status": "Official",
"Genres": ["Grunge", "Alternative Rock"],
"Rating": {
"Votes": 156,
"Value": 4.9
},
"Images": [
{
"Url": "https://imagecache.lidarr.audio/cover/1b022e01-4da6-387b-8658-8678046e4cef/front.jpg",
"CoverType": "cover",
"Extension": ".jpg"
}
]
}
]
}
```
**Response fields**:
| Field | Type | Description |
|-------|------|-------------|
| `Id` | UUID | MusicBrainz artist ID |
| `OldIds` | Array | Historical MusicBrainz IDs (after merges) |
| `ArtistName` | String | Primary artist name |
| `SortName` | String | Name for alphabetical sorting |
| `Disambiguation` | String | Disambiguation comment |
| `Overview` | String | Artist biography (from Wikipedia) |
| `Type` | String | Artist type (Person, Group, Orchestra, etc.) |
| `Status` | String | Artist status (Active, Ended, etc.) |
| `Gender` | String | Gender (for Person type) |
| `Hometown` | String | Origin location |
| `StartYear` | Integer | Formation/birth year |
| `EndYear` | Integer | Dissolution/death year |
| `ArtistAliases` | Array | Alternative names and translations |
| `Links` | Array | External links (Spotify, AllMusic, Discogs, etc.) |
| `Images` | Array | Artist images (poster, banner, logo, fanart) |
| `Genres` | Array | Genre tags |
| `Rating` | Object | Community rating (votes and value) |
| `Albums` | Array | Release groups (filtered by parameters) |
**Cache behavior**:
- **Cache key**: `artist:{mbid}:{primTypes}:{secTypes}:{releaseStatuses}`
- **TTL**: 30 days (2592000 seconds)
- **Invalidation**: Manual via `/artist/{mbid}/refresh` or automatic via crawler
**Performance**:
- **Cold request**: 2-5 seconds (MusicBrainz DB + external APIs)
- **Cached request**: 50-200ms (Redis/PostgreSQL)
- **CDN request**: 10-50ms (Cloudflare edge)
### POST /artist/{mbid}/refresh
**Purpose**: Invalidate cache and force fresh metadata fetch
**Authentication**: Required (API key)
**Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mbid` | UUID | Yes | MusicBrainz artist ID |
**Request**:
```http
POST /artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/refresh HTTP/1.1
X-Api-Key: your-api-key-here
```
**Response**:
```json
{
"status": "success",
"mbid": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
"invalidated": [
"redis:artist:5b11f4ce-a62d-471e-81fc-a69a8278c7da",
"postgres:artist:5b11f4ce-a62d-471e-81fc-a69a8278c7da",
"cdn:https://api.lidarr.audio/api/v0.3/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da"
]
}
```
**Cache invalidation**:
1. Delete from Redis cache
2. Delete from PostgreSQL cache
3. Purge from Cloudflare CDN
4. Next request will fetch fresh data
**Use cases**:
- Artist metadata updated in MusicBrainz
- New images added to FanArt.tv
- Wikipedia article updated
- Manual cache refresh
### GET /recent/artist
**Purpose**: List recently updated artists
**Parameters**:
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `limit` | Integer | No | 50 | Maximum number of results |
| `since` | ISO 8601 | No | 24h ago | Only artists updated after this timestamp |
**Example request**:
```http
GET /recent/artist?limit=10&since=2025-04-27T00:00:00Z HTTP/1.1
```
**Response**:
```json
{
"artists": [
{
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
"ArtistName": "Nirvana",
"LastUpdated": "2025-04-28T10:30:00Z",
"ChangeType": "metadata"
},
{
"Id": "83d91898-7763-47d7-b03b-b92132375c47",
"ArtistName": "Pink Floyd",
"LastUpdated": "2025-04-28T09:15:00Z",
"ChangeType": "new_release"
}
],
"total": 2,
"limit": 10,
"since": "2025-04-27T00:00:00Z"
}
```
**Change types**:
- `metadata`: Artist metadata updated
- `new_release`: New release added
- `updated_release`: Existing release updated
- `new_link`: New external link added
- `cover_art`: Cover art updated
**Cache**: 1 hour TTL
**Use case**: Crawler scheduling, monitoring MusicBrainz updates
## Album Endpoints
### GET /album/{mbid}
**Purpose**: Retrieve complete album metadata with tracks
**Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mbid` | UUID | Yes | MusicBrainz release group ID |
**Example request**:
```http
GET /album/1b022e01-4da6-387b-8658-8678046e4cef HTTP/1.1
```
**Response**:
```json
{
"Id": "1b022e01-4da6-387b-8658-8678046e4cef",
"OldIds": [],
"Title": "Nevermind",
"Disambiguation": "",
"Overview": "Nevermind is the second studio album by American rock band Nirvana...",
"ReleaseDate": "1991-09-24",
"Type": "Album",
"SecondaryTypes": [],
"Status": "Official",
"Artist": {
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
"ArtistName": "Nirvana"
},
"Links": [
{
"Url": "https://www.spotify.com/album/2guirTSEqLizK7j9i1MTTZ",
"Name": "spotify"
},
{
"Url": "https://www.allmusic.com/album/mw0000083881",
"Name": "allmusic"
}
],
"Images": [
{
"Url": "https://imagecache.lidarr.audio/cover/1b022e01-4da6-387b-8658-8678046e4cef/front.jpg",
"CoverType": "cover",
"Extension": ".jpg"
},
{
"Url": "https://imagecache.lidarr.audio/cover/1b022e01-4da6-387b-8658-8678046e4cef/back.jpg",
"CoverType": "disc",
"Extension": ".jpg"
}
],
"Genres": ["Grunge", "Alternative Rock"],
"Rating": {
"Votes": 156,
"Value": 4.9
},
"Media": [
{
"Position": 1,
"Format": "CD",
"Tracks": [
{
"Position": 1,
"Title": "Smells Like Teen Spirit",
"Duration": 301000,
"ArtistName": "Nirvana"
},
{
"Position": 2,
"Title": "In Bloom",
"Duration": 254000,
"ArtistName": "Nirvana"
}
]
}
]
}
```
**Response fields**:
| Field | Type | Description |
|-------|------|-------------|
| `Id` | UUID | MusicBrainz release group ID |
| `OldIds` | Array | Historical IDs (after merges) |
| `Title` | String | Album title |
| `Disambiguation` | String | Disambiguation comment |
| `Overview` | String | Album description (from Wikipedia) |
| `ReleaseDate` | ISO 8601 | Original release date |
| `Type` | String | Primary type (Album, Single, EP, etc.) |
| `SecondaryTypes` | Array | Secondary types (Compilation, Live, etc.) |
| `Status` | String | Release status (Official, Promotion, etc.) |
| `Artist` | Object | Artist information |
| `Links` | Array | External links |
| `Images` | Array | Cover art images |
| `Genres` | Array | Genre tags |
| `Rating` | Object | Community rating |
| `Media` | Array | Physical/digital media with track listings |
**Cache behavior**:
- **Cache key**: `album:{mbid}`
- **TTL**: 30 days
- **Invalidation**: Manual or automatic
### POST /album/{mbid}/refresh
**Purpose**: Invalidate album cache
**Authentication**: Required (API key)
**Parameters**: Same as artist refresh
**Response**: Same structure as artist refresh
### GET /recent/album
**Purpose**: List recently updated albums
**Parameters**: Same as `/recent/artist`
**Response**: Similar structure with album-specific fields
## Search Endpoints
### GET /search/artist
**Purpose**: Search for artists by name
**Parameters**:
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `query` | String | Yes | - | Search query |
| `limit` | Integer | No | 10 | Maximum results |
**Example request**:
```http
GET /search/artist?query=nirvana&limit=5 HTTP/1.1
```
**Response**:
```json
{
"results": [
{
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
"ArtistName": "Nirvana",
"Disambiguation": "90s US grunge band",
"Type": "Group",
"Score": 100,
"Images": [
{
"Url": "https://imagecache.lidarr.audio/fanart/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da/poster.jpg",
"CoverType": "poster",
"Extension": ".jpg"
}
]
},
{
"Id": "9282c8b4-ca0b-4c6b-b7e3-4f7762dfc4d6",
"ArtistName": "Nirvana",
"Disambiguation": "60s UK psychedelic band",
"Type": "Group",
"Score": 95,
"Images": []
}
],
"total": 2,
"query": "nirvana",
"limit": 5
}
```
**Search algorithm**:
1. Query Solr artist core with dismax parser
2. Boost fields: `artist^2 sortname alias`
3. Minimum match: 1 term
4. Sort by relevance score
5. Enrich results with cached metadata
**Cache**:
- **Cache key**: `search:artist:{query}:{limit}`
- **TTL**: 1 hour
**Performance**:
- **Cold request**: 500ms-1s (Solr query + metadata enrichment)
- **Cached request**: 50-100ms
### GET /search/album
**Purpose**: Search for albums by title
**Parameters**: Same as artist search
**Response**: Similar structure with album-specific fields
**Search algorithm**: Same as artist search, using `release-group` Solr core
### GET /search/all
**Purpose**: Combined artist and album search
**Parameters**:
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `query` | String | Yes | - | Search query |
| `artistLimit` | Integer | No | 5 | Max artist results |
| `albumLimit` | Integer | No | 5 | Max album results |
**Response**:
```json
{
"artists": [...],
"albums": [...],
"query": "nevermind",
"artistLimit": 5,
"albumLimit": 5
}
```
**Use case**: Unified search UI, autocomplete
### POST /search/fingerprint
**Purpose**: Acoustic fingerprint search (AcoustID integration)
**Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `fingerprint` | String | Yes | Chromaprint fingerprint |
| `duration` | Integer | Yes | Track duration in seconds |
**Request**:
```http
POST /search/fingerprint HTTP/1.1
Content-Type: application/json
```
**Response**:
```json
{
"results": [
{
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
"ArtistName": "Nirvana",
"AlbumTitle": "Nevermind",
"TrackTitle": "Smells Like Teen Spirit",
"Score": 0.98,
"Duration": 301
}
]
}
```
**Use case**: Audio file identification, automatic tagging
## Spotify Integration Endpoints
### GET /spotify/artist/{id}
**Purpose**: Get artist metadata by Spotify ID
**Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | String | Yes | Spotify artist ID |
**Example request**:
```http
GET /spotify/artist/6olE6TJLqED3rqDCT0FyPh HTTP/1.1
```
**Response**: Same structure as `/artist/{mbid}` with MusicBrainz mapping
**Mapping algorithm**:
1. Query Spotify API for artist metadata
2. Search MusicBrainz by artist name
3. Calculate Levenshtein distance (threshold: 0.8)
4. Return best match with MusicBrainz ID
### GET /spotify/album/{id}
**Purpose**: Get album metadata by Spotify ID
**Parameters**: Same as artist endpoint
**Response**: Same structure as `/album/{mbid}` with MusicBrainz mapping
### POST /spotify/lookup
**Purpose**: Batch Spotify ID to MusicBrainz ID mapping
**Request**:
```http
POST /spotify/lookup HTTP/1.1
Content-Type: application/json
```
**Response**:
```json
{
"artists": {
"6olE6TJLqED3rqDCT0FyPh": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
"0k17h0D3J5VfsdmQ1iZtE9": "83d91898-7763-47d7-b03b-b92132375c47"
},
"albums": {
"2guirTSEqLizK7j9i1MTTZ": "1b022e01-4da6-387b-8658-8678046e4cef"
}
}
```
**Cache**: 90 days TTL (Spotify IDs rarely change)
### GET /spotify/auth
**Purpose**: Get Spotify OAuth authorization URL
**Response**:
```json
{
"auth_url": "https://accounts.spotify.com/authorize?client_id=...&response_type=code&redirect_uri=..."
}
```
**Use case**: User authentication for Spotify features
### GET /spotify/renew
**Purpose**: Refresh Spotify OAuth token
**Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `refresh_token` | String | Yes | Spotify refresh token |
**Response**:
```json
{
"access_token": "BQD...",
"expires_in": 3600
}
```
## Chart Endpoints
### GET /chart/{name}/{type}/{selection}
**Purpose**: Get music charts from various sources
**Parameters**:
| Parameter | Type | Required | Values | Description |
|-----------|------|----------|--------|-------------|
| `name` | String | Yes | `apple-music`, `billboard`, `itunes`, `lastfm` | Chart source |
| `type` | String | Yes | `artist`, `album`, `track` | Chart type |
| `selection` | String | Yes | Source-specific | Chart category |
**Chart sources and selections**:
#### Apple Music
**Selections**:
- `top-albums`: Top 100 albums
- `new-releases`: New album releases
- `top-songs`: Top 100 songs
**Example**:
```http
GET /chart/apple-music/album/top-albums HTTP/1.1
```
#### Billboard
**Selections**:
- `hot-100`: Billboard Hot 100
- `billboard-200`: Billboard 200 albums
- `artist-100`: Top artists
- `streaming-songs`: Top streaming songs
**Example**:
```http
GET /chart/billboard/track/hot-100 HTTP/1.1
```
#### iTunes
**Selections**:
- `top-albums`: iTunes top albums
- `top-songs`: iTunes top songs
**Example**:
```http
GET /chart/itunes/album/top-albums HTTP/1.1
```
#### Last.fm
**Selections**:
- `top-artists`: Most scrobbled artists
- `top-albums`: Most scrobbled albums
- `top-tracks`: Most scrobbled tracks
**Example**:
```http
GET /chart/lastfm/artist/top-artists HTTP/1.1
```
**Response**:
```json
{
"chart": "billboard",
"type": "track",
"selection": "hot-100",
"date": "2025-04-28",
"entries": [
{
"position": 1,
"previousPosition": 2,
"peakPosition": 1,
"weeksOnChart": 12,
"artist": {
"Id": "5b11f4ce-a62d-471e-81fc-a69a8278c7da",
"ArtistName": "Nirvana"
},
"album": {
"Id": "1b022e01-4da6-387b-8658-8678046e4cef",
"Title": "Nevermind"
},
"track": {
"Title": "Smells Like Teen Spirit",
"Duration": 301000
}
}
]
}
```
**Cache**: 6 hours TTL (charts update daily)
**MusicBrainz mapping**: Chart entries are automatically mapped to MusicBrainz IDs when possible
## Series Endpoints
### GET /series/{mbid}
**Purpose**: Get series metadata (box sets, compilations)
**Parameters**:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `mbid` | UUID | Yes | MusicBrainz series ID |
**Response**:
```json
{
"Id": "series-mbid",
"Name": "The Beatles Remastered Series",
"Type": "Release group series",
"Albums": [
{
"Id": "album-mbid-1",
"Title": "Please Please Me",
"Position": 1
},
{
"Id": "album-mbid-2",
"Title": "With the Beatles",
"Position": 2
}
]
}
```
**Use case**: Box set tracking, series completion
## Cache Management Endpoints
### POST /invalidate
**Purpose**: Batch cache invalidation
**Authentication**: Required (API key)
**Request**:
```http
POST /invalidate HTTP/1.1
X-Api-Key: your-api-key-here
Content-Type: application/json
```
**Response**:
```json
{
"status": "success",
"invalidated": {
"artists": 2,
"albums": 1,
"total_keys": 6
}
}
```
**Invalidation scope**:
- Redis cache
- PostgreSQL cache
- Cloudflare CDN cache
**Use case**: Bulk cache refresh after MusicBrainz updates
## Rate Limiting
**Default**: No rate limiting (NullRateLimiter)
**Optional**: Redis-based distributed rate limiting
**Configuration**:
```python
RATE_LIMITER = 'redis'
RATE_LIMIT_REQUESTS = 100
RATE_LIMIT_WINDOW = 60 # seconds
```
**Rate limit response**:
```http
HTTP/1.1 429 Too Many Requests
Retry-After: 30
{
"error": "Rate limit exceeded",
"limit": 100,
"window": 60,
"retry_after": 30
}
```
## CORS Support
**Default**: CORS enabled for all origins
**Headers**:
```http
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Api-Key
```
**Configuration**:
```python
CORS_ORIGINS = ['https://lidarr.audio', 'https://app.lidarr.audio']
```
## Pagination
**Not implemented**: All endpoints return complete result sets
**Workaround**: Use `limit` parameter on search and recent endpoints
**Future consideration**: Cursor-based pagination for large result sets
## Filtering and Sorting
**Artist endpoint**:
- Filter by release type: `primTypes`, `secTypes`
- Filter by release status: `releaseStatuses`
- Sort: Albums sorted by release date (descending)
**Search endpoints**:
- Sort: Relevance score (descending)
- Filter: Not implemented
**Chart endpoints**:
- Sort: Chart position (ascending)
- Filter: Not implemented
## Compression
**Response compression**: Supported via `Accept-Encoding: gzip`
**Request compression**: Not supported
**Example**:
```http
GET /artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da HTTP/1.1
Accept-Encoding: gzip
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Length: 1234
```
**Compression ratio**: Typically 5:1 for JSON responses
## Conditional Requests
**ETag support**: Not implemented
**Last-Modified support**: Not implemented
**Future consideration**: ETag generation based on cache timestamp
## Webhooks
**Not implemented**: No webhook support for cache invalidation or updates
**Workaround**: Poll `/recent/artist` and `/recent/album` endpoints
**Future consideration**: WebSocket or Server-Sent Events for real-time updates
## API Versioning Strategy
**Current approach**: Path-based versioning (`/api/v0.3`)
**Version lifecycle**:
1. New features developed in `testing` version
2. Tested in production alongside stable version
3. Promoted to new stable version (e.g., v0.4)
4. Old stable version deprecated after 6 months
**Breaking changes**: Only in major version increments
**Backward compatibility**: Maintained within minor versions
## Performance Benchmarks
| Endpoint | Cold (ms) | Cached (ms) | CDN (ms) |
|----------|-----------|-------------|----------|
| GET /artist/{mbid} | 2000-5000 | 50-200 | 10-50 |
| GET /album/{mbid} | 1500-3000 | 50-150 | 10-50 |
| GET /search/artist | 500-1000 | 50-100 | N/A |
| GET /chart/billboard/hot-100 | 3000-5000 | 100-200 | 20-100 |
| POST /artist/{mbid}/refresh | 100-200 | N/A | N/A |
**Factors affecting cold request performance**:
- MusicBrainz database query complexity
- Number of external API calls (FanArt, Wikipedia, etc.)
- External API response times
- Network latency
**Cache hit rate**: 85%+ with crawler enabled
## Error Handling Examples
**Invalid MBID format**:
```http
GET /artist/invalid-mbid HTTP/1.1
HTTP/1.1 400 Bad Request
{
"error": "Invalid MBID format",
"mbid": "invalid-mbid"
}
```
**Artist not found**:
```http
GET /artist/00000000-0000-0000-0000-000000000000 HTTP/1.1
HTTP/1.1 404 Not Found
{
"error": "Artist not found",
"mbid": "00000000-0000-0000-0000-000000000000"
}
```
**Missing API key**:
```http
POST /invalidate HTTP/1.1
HTTP/1.1 401 Unauthorized
{
"error": "Missing or invalid API key"
}
```
**Database connection error**:
```http
GET /artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da HTTP/1.1
HTTP/1.1 503 Service Unavailable
{
"error": "Database unavailable",
"retry_after": 60
}
```
## Client Implementation Examples
### Python
```python
import requests
class LidarrMetadataClient:
def __init__(self, base_url='https://api.lidarr.audio/api/v0.3'):
self.base_url = base_url
self.session = requests.Session()
def get_artist(self, mbid, prim_types=None, sec_types=None, statuses=None):
params = {}
if prim_types:
params['primTypes'] = ','.join(prim_types)
if sec_types:
params['secTypes'] = ','.join(sec_types)
if statuses:
params['releaseStatuses'] = ','.join(statuses)
response = self.session.get(f'{self.base_url}/artist/{mbid}', params=params)
response.raise_for_status()
return response.json()
def search_artist(self, query, limit=10):
response = self.session.get(
f'{self.base_url}/search/artist',
params={'query': query, 'limit': limit}
)
response.raise_for_status()
return response.json()
# Usage
client = LidarrMetadataClient()
artist = client.get_artist('5b11f4ce-a62d-471e-81fc-a69a8278c7da', prim_types=['Album'])
results = client.search_artist('nirvana')
```
### JavaScript
```javascript
class LidarrMetadataClient {
constructor(baseUrl = 'https://api.lidarr.audio/api/v0.3') {
this.baseUrl = baseUrl;
}
async getArtist(mbid, options = {}) {
const params = new URLSearchParams();
if (options.primTypes) params.set('primTypes', options.primTypes.join(','));
if (options.secTypes) params.set('secTypes', options.secTypes.join(','));
if (options.releaseStatuses) params.set('releaseStatuses', options.releaseStatuses.join(','));
const response = await fetch(`${this.baseUrl}/artist/${mbid}?${params}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
async searchArtist(query, limit = 10) {
const params = new URLSearchParams({ query, limit });
const response = await fetch(`${this.baseUrl}/search/artist?${params}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
}
// Usage
const client = new LidarrMetadataClient();
const artist = await client.getArtist('5b11f4ce-a62d-471e-81fc-a69a8278c7da', {
primTypes: ['Album']
});
const results = await client.searchArtist('nirvana');
```
## Conclusion
The API provides comprehensive music metadata with:
- **15+ endpoints** covering artists, albums, search, charts, and Spotify integration
- **Multi-source aggregation** from MusicBrainz, FanArt.tv, Wikipedia, Spotify, Last.fm, Billboard
- **Intelligent caching** with 30-day TTL and CDN integration
- **Flexible filtering** for release types and statuses
- **Chart integration** from 4 major sources
- **Spotify mapping** for cross-platform compatibility
The API is production-ready with excellent performance characteristics when properly cached. The dual-version deployment strategy allows safe rollout of new features.
Key strengths: comprehensive metadata, excellent caching, multi-source enrichment.
Key weaknesses: no authentication on read endpoints, no pagination, no webhooks.