Skip to content

Instantly share code, notes, and snippets.

@kemo
Created February 5, 2026 06:59
Show Gist options
  • Select an option

  • Save kemo/506ca56e35b9506ee5233bc4d773c1c8 to your computer and use it in GitHub Desktop.

Select an option

Save kemo/506ca56e35b9506ee5233bc4d773c1c8 to your computer and use it in GitHub Desktop.
# Beatport (Internal + External) API Documentation

Beatport API Documentation

This document provides comprehensive documentation for Beatport's APIs, discovered through frontend analysis and reverse engineering.

Overview

Beatport uses two main API infrastructures:

API Base URL Purpose Authentication
Internal API https://api-internal.beatportprod.com/v4 Server-side rendering, frontend data Server-side only
Public API https://api.beatport.com/v4 OAuth-based access for third-party apps OAuth 2.0 (PKCE)

The internal API (api-internal.beatportprod.com) is what Beatport's Next.js frontend uses server-side to fetch data during SSR. The public API (api.beatport.com) provides OAuth-authenticated access for external applications.


Internal API (api-internal.beatportprod.com)

This API is used by Beatport's server-side rendering. Requests are made from Beatport's servers, not from the browser.

Base URL

https://api-internal.beatportprod.com/v4

Catalog Endpoints

Tracks

Endpoint Description
GET /catalog/tracks/{id}/ Get track details by ID
GET /catalog/tracks/ List tracks with pagination
GET /catalog/tracks/top/{genre_id}/?per_page=100&page=1 Get top tracks for a genre

Track Response Fields:

{
  "id": 23011269,
  "name": "Greece 2000",
  "mix_name": "Max Styler Extended Rework",
  "slug": "greece-2000",
  "artists": [
    {"id": 8783, "name": "Three Drives", "slug": "three-drives", "url": "/artist/three-drives/8783", "image": {...}}
  ],
  "remixers": [],
  "release": {
    "id": 5707323,
    "name": "Greece 2000 - Max Styler Rework",
    "slug": "greece-2000-max-styler-rework",
    "label": {"id": 33099, "name": "Armada Music", "slug": "armada-music"}
  },
  "genre": {"id": 7, "name": "Trance (Main Floor)", "slug": "trance-main-floor"},
  "sub_genre": {"id": 27, "name": "...", "slug": "..."},
  "bpm": 128,
  "key": {"id": 16, "name": "A min", "camelot": "8A"},
  "length": "6:45",
  "length_ms": 405000,
  "price": {"code": "USD", "symbol": "$", "value": 1.69},
  "publish_date": "2024-01-15",
  "new_release_date": "2024-01-15",
  "sample_url": "https://geo-samples.beatport.com/track/{uuid}.LOFI.mp3",
  "sample_start_ms": 130976,
  "sample_end_ms": 250976,
  "image": {
    "id": "...",
    "uri": "https://geo-media.beatport.com/image_size/590x404/{uuid}.jpg",
    "dynamic_uri": "https://geo-media.beatport.com/image_size/{w}x{h}/{uuid}.jpg"
  },
  "isrc": "...",
  "exclusive": false,
  "is_available_for_streaming": true,
  "is_explicit": false,
  "is_hype": false,
  "is_classic": false,
  "current_status": {"id": 8, "name": "Published"},
  "sale_type": {"id": 1, "name": "..."}
}

Artists

Endpoint Description
GET /catalog/artists/{id}/ Get artist details
GET /catalog/artists/{id}/top-10-tracks/?per_page=10 Get artist's top tracks

Artist Response Fields:

{
  "id": 26182,
  "name": "deadmau5",
  "slug": "deadmau5",
  "bio": "...",
  "website": "https://...",
  "image": {...},
  "dj_association": {...},
  "enabled": true
}

Labels

Endpoint Description
GET /catalog/labels/{id}/ Get label details
GET /catalog/labels/{id}/top-10-tracks/?per_page=10 Get label's top tracks

Label Response Fields:

{
  "id": 2027,
  "name": "Drumcode",
  "slug": "drumcode",
  "bio": "...",
  "image": {...},
  "enabled": true,
  "has_exclusive_contract": false,
  "is_available_for_streaming": true,
  "is_available_for_bundle_subscription": true,
  "is_available_for_hype": true,
  "latest_active_publish_date": "2024-01-20"
}

Releases

Endpoint Description
GET /catalog/releases/{id}/ Get release details

Release Response Fields:

{
  "id": 5691573,
  "name": "Misbehave",
  "slug": "misbehave",
  "artists": [...],
  "label": {...},
  "catalog_number": "...",
  "upc": "...",
  "type": "EP",
  "desc": "...",
  "bpm_range": {"min": 120, "max": 128},
  "tracks": [...],
  "track_count": 4,
  "price": {...},
  "publish_date": "2024-01-15",
  "image": {...},
  "exclusive": false,
  "is_available_for_streaming": true,
  "is_hype": false
}

Genres

Endpoint Description
GET /catalog/genres/ List all genres
GET /catalog/genres/{id}/ Get genre details
GET /catalog/genres/{id}/top-10-tracks/?per_page=10 Get genre's top tracks
GET /catalog/genres/{id}/top-10-releases Get genre's top releases

Known Genre IDs:

ID Name
1 Drum & Bass
3 Electronica
5 House
6 Techno (Peak Time / Driving)
7 Trance (Main Floor)
8 Hard Dance / Hardcore / Neo Rave
9 Breaks / Breakbeat / UK Bass
11 Tech House
12 Deep House
13 Psy-Trance
14 Minimal / Deep Tech
15 Progressive House
18 Dubstep
37 Indie Dance
38 Trap / Future Bass
39 Dance / Pop
50 Nu Disco / Disco
86 UK Garage / Bassline
89 Afro House
90 Melodic House & Techno
91 Bass House
92 Techno (Raw / Deep / Hypnotic)
96 Mainstage

Genre Response Fields:

{
  "id": 6,
  "name": "Techno (Peak Time / Driving)",
  "slug": "techno-peak-time-driving",
  "enabled": true,
  "is_included_in_hype": true,
  "sub_genres": [...]
}

Sub-Genres

Endpoint Description
GET /catalog/sub-genres/{id}/ Get sub-genre details

Charts

Endpoint Description
GET /catalog/charts/ List charts
GET /catalog/charts/{id}/ Get chart details

Keys (Musical)

Endpoint Description
GET /catalog/keys/{id}/ Get key details

Chord Types

Endpoint Description
GET /catalog/chord-types/{id}/ Get chord type

Search

Endpoint Description
GET /catalog/search/?q={query}&type={type} Search catalog

Search Types: tracks, artists, labels, releases, charts

Search Response:

{
  "tracks": {
    "data": [
      {
        "score": 100.5,
        "artist_id": 26182,
        "artist_name": "deadmau5",
        "genre": [{"genre_id": 1, "genre_name": "..."}],
        "artist_image_uri": "...",
        "artist_image_dynamic_uri": "..."
      }
    ]
  },
  "artists": {...},
  "labels": {...},
  "releases": {...},
  "charts": {...}
}

Auxiliary Endpoints

Endpoint Description
GET /auxiliary/current-status/{id}/ Track status (e.g., Published)
GET /auxiliary/sale-types/{id}/ Sale type info
GET /auxiliary/exclusive-period/{id}/ Exclusive period info
GET /auxiliary/item-types/ List item types
GET /auxiliary/page-module-types/ Page module types
GET /auxiliary/page-types/ Page types
GET /auxiliary/source-types/ Source types

Curation Endpoints

Endpoint Description
GET /curation/pages/ Curated pages
GET /curation/page-module-styles/ Page module styles

Response Pagination

All list endpoints use consistent pagination:

{
  "next": "https://api-internal.beatportprod.com/v4/...?page=2",
  "previous": null,
  "count": 1000,
  "page": 1,
  "per_page": 25,
  "results": [...]
}

Query Parameters:

  • page - Page number (default: 1)
  • per_page - Results per page (default: 25, max: 100)

Public API (api.beatport.com)

The public API requires OAuth 2.0 authentication and provides access to user-specific data.

Base URL

https://api.beatport.com/v4

Authentication

OAuth 2.0 with PKCE

  1. Get Client ID - Scraped from https://api.beatport.com/v4/docs/ JavaScript files
  2. Authorization URL:
GET /auth/o/authorize
?client_id={client_id}
&response_type=code
&redirect_uri={redirect_uri}
&scope=library
&code_challenge={code_challenge}
&code_challenge_method=S256
  1. Token Exchange:
POST /auth/o/token/
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code={authorization_code}
&redirect_uri={redirect_uri}
&client_id={client_id}
&code_verifier={code_verifier}
  1. Token Refresh:
POST /auth/o/token/
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
&refresh_token={refresh_token}
&client_id={client_id}

Token Response:

{
  "access_token": "...",
  "refresh_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "library"
}

Direct Login (Session-based)

POST /auth/login/
Content-Type: application/json

{
  "username": "user@example.com",
  "password": "password"
}

Returns session cookies for web scraping.

Authentication Headers

Authorization: Bearer {access_token}
Accept: application/json

User Endpoints

Account

Endpoint Description
GET /my/account/ Get current user profile

Response:

{
  "id": 12345,
  "username": "user",
  "email": "user@example.com",
  "first_name": "...",
  "last_name": "...",
  "created": "...",
  "modified": "..."
}

My Beatport Library

Endpoint Description
GET /my/beatport/tracks/?page=1&per_page=100 Get user's saved tracks

Catalog Endpoints

Same as internal API, but authenticated:

Endpoint Description
GET /catalog/tracks/{id}/ Get track details
GET /catalog/tracks/?page=1&per_page=25&order_by=-publish_date List tracks
GET /catalog/search/?q={query}&type=tracks Search tracks

Order Parameters

  • order_by=publish_date - Oldest first
  • order_by=-publish_date - Newest first (descending)

Media URLs

Audio Samples

https://geo-samples.beatport.com/track/{uuid}.LOFI.mp3

Sample playback is controlled by sample_start_ms and sample_end_ms fields (typically 2-minute preview window).

Images

Static size:

https://geo-media.beatport.com/image_size/590x404/{uuid}.jpg

Dynamic size:

https://geo-media.beatport.com/image_size/{width}x{height}/{uuid}.jpg

Common sizes: 100x100, 250x250, 500x500, 590x404, 1400x1400


Frontend Architecture

Beatport uses Next.js with Server-Side Rendering (SSR). Key observations:

  1. Data Fetching: All API calls happen server-side during SSR

  2. State Management: Uses React Query with dehydrated state

  3. Query Keys: Follow patterns like:

    • track-{id}
    • release-{id}
    • artist-{id}
    • label-{id}
    • genre-{id}
    • search-all
    • top-10-tracks-per_page=10
    • page-module-{id}-items
  4. Data available in window.__NEXT_DATA__:

{
  "buildId": "...",
  "page": "/track/[slug]/[id]",
  "props": {
    "pageProps": {
      "dehydratedState": {
        "queries": [
          {
            "queryKey": ["track-23011269"],
            "state": { "data": {...} }
          }
        ]
      }
    }
  }
}

Rate Limiting

No official rate limits documented, but recommended practices:

  • Add 500ms delay between requests when scraping
  • Use pagination instead of large per_page values
  • Cache responses where appropriate

Error Responses

{
  "detail": "Not found.",
  "error": "...",
  "status_code": 404
}

Common HTTP status codes:

  • 200 - Success
  • 302 - Redirect (often to login for auth-required endpoints)
  • 400 - Bad request
  • 401 - Unauthorized
  • 403 - Forbidden
  • 404 - Not found
  • 429 - Rate limited

Example Usage

Get Track Details (PHP/Symfony)

$response = $httpClient->request('GET', 'https://api.beatport.com/v4/catalog/tracks/23011269/', [
    'headers' => [
        'Authorization' => 'Bearer ' . $accessToken,
        'Accept' => 'application/json',
    ],
]);

$track = $response->toArray();

Search Tracks

$response = $httpClient->request('GET', 'https://api.beatport.com/v4/catalog/search/', [
    'query' => [
        'q' => 'deadmau5',
        'type' => 'tracks',
        'page' => 1,
        'per_page' => 25,
    ],
    'headers' => [
        'Authorization' => 'Bearer ' . $accessToken,
    ],
]);

Get User Library

$tracks = [];
$page = 1;

do {
    $response = $httpClient->request('GET', 'https://api.beatport.com/v4/my/beatport/tracks/', [
        'query' => ['page' => $page, 'per_page' => 100],
        'headers' => ['Authorization' => 'Bearer ' . $accessToken],
    ]);

    $data = $response->toArray();
    $tracks = array_merge($tracks, $data['results']);
    $hasMore = $data['next'] !== null;
    $page++;
} while ($hasMore);

Changelog

  • 2026-02-05: Initial documentation created from frontend analysis
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment