# MusicBrainz Scraper: Artists, Releases & Recordings (`arman-bd/musicbrainz-scraper`) Actor

Scrape MusicBrainz artists, releases, recordings, labels and works with MBIDs, ISRCs, tags, genres, dates and relationships. No key, no proxy, no browser.

- **URL**: https://apify.com/arman-bd/musicbrainz-scraper.md
- **Developed by:** [Arman Hossain](https://apify.com/arman-bd) (community)
- **Categories:** Automation, AI, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.37 / 1,000 entity scrapeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## MusicBrainz Scraper: Artists, Releases & Recordings

![MusicBrainz Scraper: Artists, releases, recordings, labels and works, MBIDs, ISRCs, tags, genres, ratings and the full relationship graph](https://api.apify.com/v2/key-value-stores/ZQOcNAOHrIgTacAmy/records/musicbrainz-scraper.jpg)

Pulls structured entities out of the MusicBrainz open music encyclopedia: artists, releases, recordings, labels and works. You get MBIDs, ISRCs, ISWCs, tags, genres, aliases, ratings, dates and the full relationship graph.

MusicBrainz publishes everything through its WS/2 web service, and this Actor reads that service directly. no credentials, no proxy, no browser, no login. It sends a descriptive `User-Agent` and paces itself at one request per second, which is exactly what MusicBrainz asks of API clients.

**Agent skill: [SKILL.md](https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/musicbrainz-scraper.md)**

```
https://api.apify.com/v2/key-value-stores/t7YoTxpZEJOWvw4Ug/records/musicbrainz-scraper.md
```

### What you get

| Field | What it holds |
|---|---|
| `entityType`, `mbid`, `url` | Entity kind, MusicBrainz ID and the canonical musicbrainz.org page |
| `name`, `sortName` | Display name, or title for releases, recordings and works, plus the sort name |
| `type`, `disambiguation` | Sub-type such as Group, Person, Song or Album, and the community disambiguation comment |
| `country`, `area` | ISO country code and the human-readable area name |
| `beginDate`, `endDate` | Life-span for artists and labels, release date for releases, first-release date for recordings |
| `lengthMs` | Recording duration in milliseconds |
| `artistCredit` | Credited artist names, in order |
| `tags`, `genres`, `aliases` | Community tags, curated genres and every known alias |
| `isrcs`, `iswcs` | Recording ISRCs and work ISWCs |
| `labels` | Label name, MBID and catalogue number for a release |
| `releaseCount` | Number of releases MusicBrainz links to the entity |
| `rating`, `ratingVotes` | Community rating out of 5 and the vote count |
| `relations` | Flattened relationship graph: band members, writers, official URLs, label links |
| `score`, `matchedQuery` | Search relevance from 0 to 100, and the query that produced the hit |
| `scrapedAt` | Run timestamp |

`RUN_SUMMARY` in the key-value store holds per-run counts, the filters you used, and every query or MBID that failed.

### Use cases

- **Music catalogues.** Seed a metadata layer with open, permissively licensed reference data.
- **Track identity via ISRC.** Match a recording across streaming platforms and rights databases.
- **AI agent enrichment.** A cheap, reliable lookup tool for music questions that would otherwise be hallucinated.
- **Discography research.** Count releases per artist or label and track them over time.
- **Credit and rights analysis.** Walk the `relations` graph to find writers, performers and producers.

### Quick start

Search for artists by name:

```json
{
 "entityType": "artist",
 "searchQueries": ["radiohead", "aphex twin"],
 "maxResults": 20
}
```

Look up specific entities by MBID, with the relationship graph:

```json
{
 "entityType": "artist",
 "mbids": [
 "a74b1b7f-71a5-4011-9441-d0b5e4122711",
 "https://musicbrainz.org/artist/f23074f4-2c06-477a-bf1d-12fa66e087ee"
 ],
 "includeRelations": true,
 "contactEmail": "you@example.com"
}
```

Lucene-syntax search over recordings, for ISRC harvesting:

```json
{
 "entityType": "recording",
 "searchQueries": ["artist:queen AND recording:\"bohemian rhapsody\""],
 "maxResults": 50,
 "contactEmail": "you@example.com"
}
```

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `entityType` | string | `artist` | One of `artist`, `release`, `recording`, `label`, `work`. Applies to every query and MBID in the run. |
| `searchQueries` | array | `[]` | Free text or Lucene syntax, run against the MusicBrainz search index. |
| `mbids` | array | `[]` | MusicBrainz IDs to fetch directly. Full musicbrainz.org URLs are accepted and the MBID is extracted. |
| `includeRelations` | boolean | `false` | Adds the relationship graph. No extra requests, but records get much larger: 92 relations for Radiohead. |
| `maxResults` | integer | `25` | Total entities saved across all queries and MBIDs. |
| `contactEmail` | string | none | Optional. Added to the `User-Agent`, because MusicBrainz asks clients to identify themselves. |

You need at least one of `searchQueries` or `mbids`. Supplying both is fine, and MBIDs are processed first. Duplicates across queries are removed before anything is fetched, so overlapping searches never cost you twice.

### Output example

A real record, produced by `{"entityType": "recording", "mbids": ["b1a9c0e9-d987-4042-ae91-78d6a3267d69"]}`, with tag and genre lists truncated here:

```json
{
 "entityType": "recording",
 "mbid": "b1a9c0e9-d987-4042-ae91-78d6a3267d69",
 "name": "Bohemian Rhapsody",
 "sortName": null,
 "type": null,
 "disambiguation": null,
 "country": null,
 "area": null,
 "beginDate": "1975-11-21",
 "endDate": null,
 "lengthMs": 355106,
 "artistCredit": ["Queen"],
 "tags": ["album rock", "alternative rock", "arena rock", "art rock"],
 "genres": ["alternative rock", "arena rock", "art rock", "baroque pop"],
 "aliases": ["Bohemian Rapsody", "Bohemian Rhapsody (remastered 2011)", "ボヘミアン・ラプソディ"],
 "isrcs": ["GBCEE0100112", "GBCEE0500364", "GBCEE7500015", "GBUM71029604"],
 "iswcs": [],
 "labels": [],
 "releaseCount": 352,
 "rating": null,
 "ratingVotes": null,
 "relations": [],
 "score": null,
 "matchedQuery": null,
 "url": "https://musicbrainz.org/recording/b1a9c0e9-d987-4042-ae91-78d6a3267d69",
 "scrapedAt": "2026-08-06T11:38:26.193Z"
}
```

Every key is present on every record. Fields that do not apply to an entity type, such as `lengthMs` on a label or `labels` on a work, come back `null` or `[]` rather than disappearing, so CSV exports stay rectangular.

### Finding an MBID

An MBID is the UUID in any musicbrainz.org URL.

| URL you see | MBID |
|---|---|
| `musicbrainz.org/artist/a74b1b7f-71a5-4011-9441-d0b5e4122711` | `a74b1b7f-71a5-4011-9441-d0b5e4122711` |
| `musicbrainz.org/release/4b3d18cc-8937-36f4-8de0-481088be58e6` | `4b3d18cc-8937-36f4-8de0-481088be58e6` |

Paste the whole URL if you like, and the Actor extracts the UUID itself. If you do not have one, use `searchQueries` instead and read the MBID off the results. `score` tells you how confident the search index was, and 100 means an exact match.

### API example

```bash
curl -X POST "https://api.apify.com/v2/acts/arman-bd~musicbrainz-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
 "entityType": "artist",
 "searchQueries": ["radiohead"],
 "maxResults": 10,
 "contactEmail": "you@example.com"
 }'
```

### JavaScript example

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

const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('arman-bd/musicbrainz-scraper').call({
 entityType: 'artist',
 searchQueries: ['radiohead', 'portishead'],
 includeRelations: true,
 maxResults: 20,
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const a of items) console.log(`${a.name} (${a.country}): ${a.releaseCount} releases`);
```

### Notes

- One request per second is enforced in code. MusicBrainz throttles anonymous clients hard and blocks repeat offenders. A single global rate limiter spaces every call 1.2 seconds apart, so budget roughly 2.5 seconds per entity for artists, labels and recordings, and 1.2 seconds for releases and works. When the service throttles anyway it answers 503, which the retry logic absorbs.
- Some entities take two requests. Artists, labels and recordings get a second, tiny `browse` call that returns an exact `releaseCount`. Releases and works have no equivalent browse filter, so `releaseCount` is `null` for them.
- The `inc` parameter is type-specific. MusicBrainz rejects `isrcs` on a label with HTTP 400 rather than ignoring it, so each entity type sends its own include list. You never have to think about this.
- One failure does not kill the run. A bad MBID, a dead query or a browse-count hiccup is recorded in `RUN_SUMMARY.failures` and the run continues. The Actor errors only if nothing at all was saved.
- Transient errors get retried. 429, 503 and 5xx get three attempts with linear backoff. 400 and 404 are treated as fatal for that entity, because retrying a malformed MBID never helps.
- Public data only. MusicBrainz core data is in the public domain, and no authentication or personal data is involved.

### FAQ

**Do I need a proxy?** No. Proxy configuration is not required to run this Actor.

**Do I need a MusicBrainz account or API key?** No. You supply no credentials.

**Why is `contactEmail` worth filling in?** MusicBrainz asks clients to identify themselves so they can email you before blocking a misbehaving integration. It costs nothing and makes throttling far less likely on heavy scheduled runs.

**Why are `isrcs` empty on some recordings?** ISRCs are contributed by editors and coverage is uneven. A canonical studio recording usually has several, a live bootleg often none. The Actor reports what MusicBrainz holds and never invents identifiers.

**Can I search several entity types in one run?** No, one `entityType` per run, because search endpoints and include lists differ per type. Run the Actor once per type and merge the datasets, or schedule several runs.

**How long does a big run take?** About 2.5 seconds per artist, label or recording and 1.2 seconds per release or work. A 500-artist run is roughly 20 minutes, so raise the timeout above the 10-minute default before scheduling one.

**Can I plug it into something else?** Yes. Apify API, the client libraries, webhooks, scheduled runs, dataset exports to JSON, CSV or Excel, or MCP. The output is structured JSON.

# Actor input Schema

## `entityType` (type: `string`):

Which MusicBrainz entity to search and return. Every search query and MBID in this run is resolved against this type.

## `searchQueries` (type: `array`):

Free-text searches run against the MusicBrainz search index: plain terms or Lucene syntax such as 'artist:radiohead AND country:GB'. Leave empty if you are only using MBIDs.

## `mbids` (type: `array`):

MusicBrainz IDs to fetch directly, skipping search. Full musicbrainz.org URLs are accepted and the MBID is extracted automatically. Leave empty to rely on search queries.

## `includeRelations` (type: `boolean`):

Fetch the entity's relationship graph: band members, writers, official URLs, label links. Adds no extra requests but makes each record considerably larger. Off by default.

## `maxResults` (type: `integer`):

Cap the total number of entities saved across all queries and MBIDs. MusicBrainz allows roughly one request per second, so a run takes about 2.5 seconds per artist, label or recording.

## `contactEmail` (type: `string`):

Optional email or URL added to the User-Agent header. MusicBrainz asks API clients to identify themselves so they can get in touch before blocking; supplying one is polite and reduces the chance of throttling.

## Actor input object example

```json
{
  "entityType": "artist",
  "searchQueries": [
    "radiohead",
    "artist:aphex twin"
  ],
  "mbids": [
    "a74b1b7f-71a5-4011-9441-d0b5e4122711"
  ],
  "includeRelations": false,
  "maxResults": 25,
  "contactEmail": "you@example.com"
}
```

# Actor output Schema

## `items` (type: `string`):

Every record the run produced.

## `runsummary` (type: `string`):

The RUN_SUMMARY record from the run's key-value store.

# 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 = {
    "entityType": "artist",
    "searchQueries": [
        "radiohead"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arman-bd/musicbrainz-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 = {
    "entityType": "artist",
    "searchQueries": ["radiohead"],
}

# Run the Actor and wait for it to finish
run = client.actor("arman-bd/musicbrainz-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 '{
  "entityType": "artist",
  "searchQueries": [
    "radiohead"
  ]
}' |
apify call arman-bd/musicbrainz-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,arman-bd/musicbrainz-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/RHYUeLsZl7ctadnTe/builds/zdV0d9xEAy0ZsnLgQ/openapi.json
