# Beatport Scraper (`crawlerbros/beatport-scraper`) Actor

Scrape Beatport - the world's largest DJ & electronic music store. Search tracks, browse genres, DJ charts, labels and artists, and get full track metadata: BPM, key, duration, price, artwork, audio previews and release dates.

- **URL**: https://apify.com/crawlerbros/beatport-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Social media, Automation, Other
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Beatport Scraper

Scrape **Beatport** — the world's largest DJ & electronic music store. Search tracks, browse genres and DJ charts, or pull labels, artists, releases and single URLs, and get full track metadata: title, artists & remixers, label, genre, release date, BPM, musical key, duration, price, artwork and 30–60 second audio previews. Fast HTTP access to Beatport's public catalog API; no login required.

### What this actor does

- **Seven modes:** `search`, `browseByGenre`, `topCharts`, `browseByLabel`, `browseByArtist`, `browseReleases`, `byUrl`
- **Full track metadata:** BPM, key (with Camelot number), duration, price, catalog number, ISRC, release dates
- **All 46 Beatport genres** browsable — from Tech House and Techno to Trance, Drum & Bass and Dubstep
- **DJ charts** with rank, chart type (`latest` / `trending` / `popular`) and optional per-chart tracks
- **Releases:** newest releases per genre/label with five sort orders and optional per-release tracks
- **Audio previews & artwork:** direct MP3 preview URLs and CDN artwork URLs on every track
- **Filters:** BPM range, musical key, keyword, release date, genre
- **Empty fields are omitted**

### Output per track

- `id`, `title`, `mixName`, `artists[]`, `primaryArtist`, `remixers[]`
- `label`, `releaseId`, `releaseName`, `genre` (id/name/slug), `subGenre`
- `bpm`, `key` (name, Camelot number/letter)
- `duration` (e.g. `6:42`), `durationMs`, `trackNumber`
- `price` (value, currency, display), `catalogNumber`, `isrc`
- `releaseDate`, `newReleaseDate`, `encodedDate`
- `previewUrl` — MP3 sample, `sampleStartMs`, `sampleEndMs`
- `artworkUrl`, `availableWorldwide`, `isExplicit`, `preOrder`, `isAvailableForStreaming`
- `audioFormat`, `desc`, `saleType`, `currentStatus`, `labelTrackIdentifier`
- `isDjEdit`, `isHype`, `isClassic`, `isUgcRemix`, `exclusive`, `wasEverExclusive`
- `freeDownloads`, `freeDownloadStartDate`, `freeDownloadEndDate`, `preOrderDate`
- `apiUrl` — canonical catalog API resource
- `chartRank` / `chartName` (when fetched inside a chart)
- `sourceUrl`, `recordType: "track"`, `scrapedAt`

### Output per chart

- `id`, `name`, `slug`, `description`, `person`, `artist`
- `genres[]`, `trackCount`, `price`, `publishDate`, `addDate`, `changeDate`
- `rank` (position in the listing), `chartType`
- `imageUrl`, `apiUrl`, `sourceUrl`, `recordType: "chart"`, `scrapedAt`

### Output per release

- `id`, `name`, `slug`, `artists[]`, `primaryArtist`, `remixers[]`, `label`
- `catalogNumber`, `upc`, `trackCount`, `bpmRange`
- `publishDate`, `newReleaseDate`, `encodedDate`, `updated`, `desc`
- `price`, `imageUrl`, `isExplicit`, `isDjEdit`, `isHype`, `isUgcRemix`, `exclusive`, `preOrder`
- `apiUrl`, `sourceUrl`, `recordType: "release"`, `scrapedAt`

### Output per artist / label

- `id`, `name`, `bio`, `website`, `djAssociation` (artists)
- `imageUrl`, `latestActivePublishDate`, `created`, `updated`
- `hasExclusiveContract`, `hypeActive`, `isAvailableForPreOrder`, `isAvailableForBundleSubscription`, `isIncludedInRightsflow` (labels)
- `sourceUrl`, `recordType`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` / `browseByGenre` / `topCharts` / `browseByLabel` / `browseByArtist` / `browseReleases` / `byUrl` |
| `searchQuery` | string | `tech house` | Free-text query (mode=search) |
| `searchType` | string | `tracks` | `tracks` / `artists` / `labels` / `charts` (mode=search) |
| `genre` | string | `tech-house` | One of 46 Beatport genres (mode=browseByGenre, also narrows topCharts / browseReleases) |
| `chartType` | string | `latest` | `latest` / `trending` / `popular` (mode=topCharts) |
| `releaseSort` | string | `newest` | `newest` / `oldest` / `name` / `trackCount` / `recentlyEncoded` (mode=browseReleases) |
| `labelId` | int | – | Label ID from the label page URL (mode=browseByLabel / browseReleases) |
| `artistId` | int | – | Artist ID from the artist page URL (mode=browseByArtist) |
| `startUrls` | array | – | Track/chart/artist/label/release/genre URLs (mode=byUrl) |
| `minBpm` / `maxBpm` | int | – | Tempo range filter (40–200) |
| `key` | string | – | Musical key filter (all 24 keys) |
| `containsKeyword` | string | – | Keep only titles containing this text |
| `releasedAfter` | string | – | `YYYY-MM-DD`; keep tracks released on/after this date |
| `includeChartTracks` | boolean | `false` | Also emit the tracks inside each chart (mode=topCharts) |
| `includeReleaseTracks` | boolean | `false` | Also emit every track inside each release (mode=browseReleases / byUrl) |
| `proxyConfiguration` | proxy | off | Optional — route API + token requests via Apify proxy when your IP range gets 401s (AUTO datacenter is free) |
| `maxItems` | int | `50` | Hard cap (1–1000) |

#### Example: search Tech House tracks with filters

```json
{
  "mode": "search",
  "searchQuery": "tech house",
  "minBpm": 120,
  "maxBpm": 130,
  "key": "A Minor",
  "containsKeyword": "remix",
  "releasedAfter": "2024-01-01"
}
```

#### Example: browse a genre

```json
{
  "mode": "browseByGenre",
  "genre": "trance-main-floor",
  "maxItems": 100
}
```

#### Example: top DJ charts with tracks

```json
{
  "mode": "topCharts",
  "chartType": "trending",
  "includeChartTracks": true,
  "maxItems": 10
}
```

#### Example: fetch by URL

```json
{
  "mode": "byUrl",
  "startUrls": [
    "https://www.beatport.com/track/the-kid-original-mix/3731110",
    "https://www.beatport.com/chart/amy-dabbs-slightly-involved-chart/811900"
  ]
}
```

#### Example: newest releases in a genre with tracks

```json
{
  "mode": "browseReleases",
  "genre": "tech-house",
  "releaseSort": "newest",
  "includeReleaseTracks": true,
  "maxItems": 20
}
```

### Data Source

Beatport's public catalog API (`api.beatport.com/v4/catalog`) powers the beatport.com website. It returns real store data — search results, genre listings, DJ charts, label and artist catalogues — including audio preview samples and artwork hosted on Beatport's CDN (`geo-media.beatport.com`, `geo-samples.beatport.com`). Anonymous access tokens are minted from the storefront's own visitor session (the `__NEXT_DATA__` payload on the homepage) and rotated automatically when they expire; if the API answers `401`, the actor invalidates the token, mints a fresh one and retries once before reporting an environment-level block (see the FAQ). This actor is a third-party tool and is not affiliated with, endorsed by, or sponsored by Beatport.

### Use cases

- **DJs & producers** — research tracks by BPM and key for harmonic mixing
- **Record pools** — track new releases by genre or label weekly
- **Chart monitoring** — watch DJ charts and their movement over time
- **Music analytics** — price, BPM, key and release-date data for catalog research
- **Archiving** — snapshot track metadata with previews and artwork

### Limitations

- **Track search is capped by Beatport's API** — track search caps at 10 results per page (any `per_page` value returns 10) and pages become unstable after the first one or two; the actor requests the real 10-page cap, dedupes by track id and stops once the API repeats itself. A search run therefore emits every unique track Beatport offers for the query (typically ~10–20) with no duplicates. Genre, label, artist and release endpoints are unaffected and honor full pages.
- **`401` on some IP ranges** — the catalog API can reject an entire network; the actor re-mints its anonymous token and retries once, then finishes cleanly with a status message. Re-running from another network (or with Apify proxy) usually resolves it.
- **Fields are omitted when Beatport omits them** — e.g. BPM or ISRC on certain tracks; only the data Beatport actually provides is output.
- **API is rate-limited** — the actor pages politely and retries on throttling (429).
- **No audio downloads** — only metadata and the short 30–60 second preview URLs that Beatport publishes on every track page.

### FAQ

**Is a Beatport account required?** No. All data is read from the public catalog API used by the beatport.com website, with the same anonymous visitor access the storefront uses.

**What are the limits?** The catalog API is rate-limited. The actor pages politely, retries on throttling (429), and re-acquires its anonymous access token automatically when it expires.

**Why do some fields disappear on some records?** Beatport's catalog API omits fields (e.g. BPM or ISRC) on certain tracks. Empty fields are dropped from the record rather than emitted as nulls — only the data Beatport actually provides is output.

**Why does track search return at most ~20 records even with a high `maxItems`?** Beatport's search API caps *track* results at 10 per page (any `per_page` value returns 10) and its pages are unstable — after the first one or two pages it recycles the same result sets. The actor requests the real 10-page cap, dedupes by track id across pages, and stops once the API repeats itself, so a search run emits every unique track Beatport offers for the query (verified: `tech house` → 20 unique; `café del mar` → 10 unique) with no duplicates. Genre, label, artist and release endpoints are unaffected — they honor full pages (verified: 1000/1000 unique on a genre stress run).

**How fresh is the data?** Live: every run queries Beatport directly. Search, charts and genre listings reflect the storefront at scrape time.

**Does this actor download the music?** No. It only extracts metadata and the short audio preview URLs (30–60 second samples) that Beatport itself publishes on every track page.

**How do I find an artist or label ID?** Open the artist/label page on beatport.com — the ID is the trailing number in the URL, e.g. `beatport.com/artist/amy-dabbs/406088` → `406088`.

**Why do I sometimes get 0 records with a `401` status?** Beatport's catalog API can return HTTP 401 for an entire network (e.g. some datacenter IP ranges). The actor mints an anonymous token from the beatport.com homepage and, on any 401, **invalidates the token, re-mints a fresh one and retries the request once** (re-mint is rate-limited with backoff to avoid hammering the visitor-session service). If the 401 persists after the re-mint retry, the network block is environment-level (the whole IP range is rejected) rather than a token problem — the run finishes cleanly with a status message instead of crashing. Re-running from a different network (or with Apify datacenter/residential proxy via the actor's proxy setting) usually resolves it; the same code path succeeds from non-blocked networks.

# Actor input Schema

## `mode` (type: `string`):

What to fetch from Beatport.

## `searchQuery` (type: `string`):

Free-text query (mode=search).

## `searchType` (type: `string`):

Entity type to return for the search (mode=search).

## `genre` (type: `string`):

Beatport genre to browse (mode=browseByGenre; also narrows topCharts).

## `chartType` (type: `string`):

Which chart ranking to fetch (mode=topCharts).

## `releaseSort` (type: `string`):

How to order releases (mode=browseReleases).

## `labelId` (type: `integer`):

Beatport label ID, e.g. `20670`. Found in the label page URL: beatport.com/label/<slug>/<id>.

## `artistId` (type: `integer`):

Beatport artist ID, e.g. `406088`. Found in the artist page URL: beatport.com/artist/<slug>/<id>.

## `startUrls` (type: `array`):

Track, chart, artist, label, release or genre URLs, e.g. `https://www.beatport.com/track/the-kid-original-mix/3731110`.

## `minBpm` (type: `integer`):

Drop tracks below this tempo.

## `maxBpm` (type: `integer`):

Drop tracks above this tempo.

## `key` (type: `string`):

Only emit tracks in this musical key.

## `containsKeyword` (type: `string`):

Only emit records whose title contains this keyword (case-insensitive).

## `releasedAfter` (type: `string`):

Only emit tracks released on or after this date (mode=search / browseByGenre).

## `includeChartTracks` (type: `boolean`):

Also emit the tracks inside each fetched chart (mode=topCharts).

## `includeReleaseTracks` (type: `boolean`):

Also emit every track inside each fetched release (mode=browseReleases / mode=byUrl release URLs).

## `proxyConfiguration` (type: `object`):

Optional. If api.beatport.com returns 401s from your IP range (environment-level block), enable Apify proxy (AUTO datacenter is free) to route API + token requests through an allowed network.

## `maxItems` (type: `integer`):

Hard cap on emitted records.

## Actor input object example

```json
{
  "mode": "search",
  "searchQuery": "tech house",
  "searchType": "tracks",
  "genre": "tech-house",
  "chartType": "latest",
  "releaseSort": "newest",
  "startUrls": [
    "https://www.beatport.com/track/the-kid-original-mix/3731110"
  ],
  "includeChartTracks": false,
  "includeReleaseTracks": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxItems": 20
}
```

# Actor output Schema

## `tracks` (type: `string`):

Dataset containing all scraped Beatport records.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "mode": "search",
    "searchQuery": "tech house",
    "searchType": "tracks",
    "genre": "tech-house",
    "chartType": "latest",
    "releaseSort": "newest",
    "startUrls": [
        "https://www.beatport.com/track/the-kid-original-mix/3731110"
    ],
    "includeChartTracks": false,
    "includeReleaseTracks": false,
    "proxyConfiguration": {
        "useApifyProxy": false
    },
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/beatport-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "mode": "search",
    "searchQuery": "tech house",
    "searchType": "tracks",
    "genre": "tech-house",
    "chartType": "latest",
    "releaseSort": "newest",
    "startUrls": ["https://www.beatport.com/track/the-kid-original-mix/3731110"],
    "includeChartTracks": False,
    "includeReleaseTracks": False,
    "proxyConfiguration": { "useApifyProxy": False },
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/beatport-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "mode": "search",
  "searchQuery": "tech house",
  "searchType": "tracks",
  "genre": "tech-house",
  "chartType": "latest",
  "releaseSort": "newest",
  "startUrls": [
    "https://www.beatport.com/track/the-kid-original-mix/3731110"
  ],
  "includeChartTracks": false,
  "includeReleaseTracks": false,
  "proxyConfiguration": {
    "useApifyProxy": false
  },
  "maxItems": 20
}' |
apify call crawlerbros/beatport-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/beatport-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ul4qU8Wk6WsX5OR2N/builds/Pq7OZ1gnCK9TbiiYO/openapi.json
