# 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 { "fingerprint": "AQADtNGSJE...", "duration": 301 } ``` **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 { "artists": ["6olE6TJLqED3rqDCT0FyPh", "0k17h0D3J5VfsdmQ1iZtE9"], "albums": ["2guirTSEqLizK7j9i1MTTZ"] } ``` **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 { "artists": [ "5b11f4ce-a62d-471e-81fc-a69a8278c7da", "83d91898-7763-47d7-b03b-b92132375c47" ], "albums": [ "1b022e01-4da6-387b-8658-8678046e4cef" ] } ``` **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.