# Bandcamp Music Insights Scraper (`brilliant_gum/bandcamp-music-scraper`) Actor

Bandcamp artists, albums, tracks, merch & supporter communities. ISRC codes, label graphs, contact extraction, tag-based discovery. Input-time filters and history tracking.

- **URL**: https://apify.com/brilliant\_gum/bandcamp-music-scraper.md
- **Developed by:** [Yuliia Kulakova](https://apify.com/brilliant_gum) (community)
- **Categories:** E-commerce, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / result

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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

![Bandcamp Music Insights Scraper](https://i.imgur.com/NFnD00b.png)

## Bandcamp Music Insights Scraper

**Independent music industry intelligence — at scale.**

Scrape Bandcamp artists, full discographies, supporter communities, label graphs, merch SKUs, prices, contacts and tags. Designed for A&R scouts, sync licensing supervisors, indie label owners, music distributors, journalists and AI/ML teams.

---

### ✨ What you get

- **Artist profiles** — name, location, bio, links, currency, creation date, label-signed flag, full discography summary, top albums by support, contact info (emails, socials, website).
- **Albums** — every release variant (Digital / CD / Vinyl / Cassette / T-shirt / box set) with prices, currencies, availability (in stock / sold out / online-only), UPC codes, release dates, tags, record label, full description text.
- **Tracks** — title, duration, track number, lyrics (when public), ISRC codes, license type, preview MP3 link, play count.
- **Supporter communities** — public supporter count per album + up to 60 supporter profile URLs (the unique Bandcamp "supported by" signal nobody else surfaces).
- **Label graph** — when an artist's release goes through a label, the label name and Bandcamp URL are extracted so you can build a clean artist↔label map.
- **Per-artist analytics** — average album price, release cadence, top genre tags, top formats, label distribution, total supporters, free-vs-paid ratio.
- **Tag co-occurrence clusters** — which tags appear together across an artist's catalog. Useful for micro-niche genre discovery.
- **Historical tracking** — run on a schedule and the actor computes deltas between snapshots: supporter growth, new releases per month, price drift, trend.

---

### 🎯 Use cases

- **A&R scouts** — find rising independent artists by location, genre, or supporter momentum.
- **Sync licensing & music supervisors** — pull tracks tagged "cinematic + instrumental + sub-3-min" with clear artist contacts and license-friendly pricing.
- **Indie label owners** — monitor competitor labels: every release, every format, every price point.
- **Music distributors** — identify unsigned artists generating sales, prime targets for distribution deals.
- **Booking agents** — geo-filter rising artists per city ("Berlin, added in the last 60 days, >1K supporters").
- **AI / ML teams** — clean, structured metadata for training tag classifiers, recommendation models and audio-language datasets.
- **Music journalists** — daily release feeds in any niche tag, ranked by community traction.
- **Researchers** — longitudinal genre data, supporter graphs, price distributions for market reports.

---

### 🚀 Quick start

#### Example 1 — Scrape one artist's full discography

```json
{
  "artistSubdomains": ["daughters"],
  "maxAlbumsPerArtist": 25,
  "includeAnalytics": true,
  "includeContactInfo": true
}
````

Returns: artist profile, every album with all release variants, every track, supporter counts, analytics block.

#### Example 2 — Filter at input (you only pay for what passes)

```json
{
  "artistSubdomains": ["daughters", "kingkrule", "noisia"],
  "tagsInclude": ["ambient", "drone"],
  "minPrice": 5,
  "maxPrice": 15,
  "formats": ["vinyl"],
  "includeAnalytics": true
}
```

Returns only releases that match: ambient/drone tags, price $5–15, available on vinyl. No charge for filtered-out items.

#### Example 3 — Lead-gen mode (only artists with extractable contacts)

```json
{
  "artistSubdomains": ["daughters", "kingkrule"],
  "hasContact": true,
  "includeContactInfo": true,
  "minSupporters": 50
}
```

Returns artists with extractable emails / socials / websites and at least 50 supporters per release — ideal for distributor outreach or sync clearance.

#### Example 4 — Track competitor labels over time

```json
{
  "artistUrls": [
    "https://ipecacrecordings.bandcamp.com/",
    "https://xlrecordingsuk.bandcamp.com/"
  ],
  "maxAlbumsPerArtist": 100,
  "enableHistory": true,
  "historyDatasetName": "label-watch"
}
```

Schedule daily/weekly — the actor persists snapshots and computes new-releases-per-month, supporter growth deltas, price drift per label.

***

### 📋 Common inputs

| Input | What it does |
|---|---|
| `artistUrls` | Full Bandcamp URLs of artists, labels, albums or tracks |
| `artistSubdomains` | Bare subdomains (e.g. `daughters`) — convenient for CSV lists |
| `albumUrls` / `trackUrls` | Direct album or track URLs |
| `tags` | Tags to discover artists from (e.g. `ambient`, `black-metal`) |
| `maxAlbumsPerArtist` | Cap on albums per artist (default 50) |
| `tagsInclude` / `tagsExclude` | Filter releases by tag — applied before billing |
| `minPrice` / `maxPrice` | Filter by minimum buyer price |
| `freeOnly` / `paidOnly` | Restrict to free downloads or paid releases |
| `formats` | Format allow-list: vinyl / cassette / cd / digital |
| `releaseDateFrom` / `releaseDateTo` | Release date range filter |
| `minSupporters` | Drop releases below a supporter floor |
| `location` | Artist location substring (e.g. `Berlin`) |
| `labelsOnly` / `signedToLabelOnly` | Restrict to labels or signed artists |
| `hasContact` | Keep only artists with extractable contacts |
| `includeContactInfo` | Extract emails + socials + websites for lead-gen |
| `includeAnalytics` | Per-artist analytics block |
| `includeTagClustering` | Genre co-occurrence map across catalog |
| `enableHistory` | Snapshot delta tracking across runs |

Every filter runs **before billing** — you only pay for entities that pass.

***

### 📤 Output

Three record types are pushed to the dataset:

- **`type: "artist"`** — one per artist scraped (name, location, bio, socials, currency, discographySummary, topAlbums, contactInfo, analytics, history)
- **`type: "album"`** — one per album (title, releaseDate, numTracks, keywords, minimumPrice, currency, releaseVariants\[], supportersCount, supporterProfileSample, recordLabel)
- **`type: "track"`** — one per track (title, durationSeconds, trackNumber, hasLyrics, lyrics, isStreamable, previewMp3Url, ISRC when available)

All records are flat JSON with stable field names — feed directly into BI tools, AI pipelines, RAG indexes, CRMs.

***

### 💰 Pricing

- **$0.01** per actor start (one-time per run)
- **$0.01** per record (artist / album / track) — `$10 per 1,000 records`

#### Cost examples

| Scenario | Records | Cost |
|---|---|---|
| One artist + 20 albums + 200 tracks | 221 | **$2.22** |
| 10 artists × 10 albums × 100 tracks | 1,110 | **$11.11** |
| Weekly label-watch (5 labels × 50 releases) | ~260 | **$2.61/week** |
| AI/ML training crawl (100K records) | 100,000 | **$1,000 one-off** |

**Filters cut cost further** — if you only want 1–2★ rated tracks or vinyl-only releases under $20, only the matching records are billed.

***

### ❓ FAQ

**Q: How fresh is the data?**
Real-time — every run hits Bandcamp directly. Schedule the actor daily, weekly, or on-demand.

**Q: Does it work for labels, not just individual artists?**
Yes. Pass the label's Bandcamp URL just like an artist. Note that labels with very large catalogs may surface only the most recent releases on a single run — for full label sweeps, paginate over multiple sessions.

**Q: What about supporter usernames / private fan data?**
The actor extracts only **publicly visible** supporter profile URLs that Bandcamp itself shows on each album's "supported by" widget. No private data, no logins required.

**Q: Can I get audio downloads?**
No. We extract metadata, preview MP3 URLs (where Bandcamp itself exposes them), prices, and rich release info. Audio purchase / download is up to you, through Bandcamp directly.

**Q: Why don't I see supporter count for some albums?**
Bandcamp only displays a supporter list when there are public supporters. Brand-new or zero-supporter releases legitimately return `supportersCount: null` — that's the platform's behaviour, not a parsing issue.

**Q: What about tag-based discovery?**
Tag input is best-effort: Bandcamp's discovery surface is highly dynamic, and pure-API discovery sometimes returns no items. For guaranteed coverage, pass artist URLs or subdomains directly. We're working on a richer tag-walk in a future version.

**Q: Is bio always populated?**
About 70% of artists have a public bio. The remaining 30% genuinely have no bio (especially solo project pages, side aliases, or one-off releases) — `bio: null` reflects what Bandcamp actually shows.

***

### ⚖️ Legal

This actor scrapes only public Bandcamp data — pages and metadata that are visible to any logged-out user. It respects Bandcamp's `robots.txt`, applies polite request rate-limiting, and doesn't impersonate users, attempt to log in, or access private content.

You're responsible for how you use the data. Bulk scraping for commercial republication, harassment of artists, or anything that violates Bandcamp's terms of service is on you — please review their terms and use responsibly. Buyers in regulated industries (sync licensing, distribution, EU GDPR contexts) should validate that their use complies with their own legal frameworks.

***

### 👤 Maintained by

Built and maintained by [brilliant\_gum](https://apify.com/brilliant_gum). Questions, edge-case requests, or feature ideas — open an issue on the Apify Console or message us. Active maintenance, regular updates as Bandcamp evolves their pages.

🎵 *Built for indie music discovery, not for harm.*

# Actor input Schema

## `artistUrls` (type: `array`):

Bandcamp artist or label URLs (e.g. https://daughters.bandcamp.com/, https://kingkrule.bandcamp.com/). Album/track URLs are also accepted and routed to the right handler.

## `artistSubdomains` (type: `array`):

Bare Bandcamp subdomains (e.g. 'daughters', 'noisia'). Convenient for CSV lists.

## `albumUrls` (type: `array`):

Direct album URLs (e.g. https://daughters.bandcamp.com/album/you-wont-get-what-you-want).

## `trackUrls` (type: `array`):

Direct track URLs (e.g. https://daughters.bandcamp.com/track/satan-in-the-wait).

## `tags` (type: `array`):

Tags to browse via Bandcamp's discover API (e.g. 'noise-rock', 'ambient', 'black-metal'). Returns the top albums currently surfaced for each tag.

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

Free-text queries — routed through the discover API.

## `maxAlbumsPerArtist` (type: `integer`):

Cap on albums fetched per artist (drawn from the discography grid).

## `maxTracksPerArtist` (type: `integer`):

Cap on standalone tracks fetched per artist (tracks not bundled in an album).

## `maxDiscoveryItemsPerTag` (type: `integer`):

Cap on items returned per tag via the discover API.

## `includeArtist` (type: `boolean`):

Push one artist record per artist scraped (in addition to per-album/track records).

## `includeDiscography` (type: `boolean`):

Fetch every album in the artist's discography (up to 'Max albums per artist').

## `includeTrackDetails` (type: `boolean`):

Push one record per track inside each album (with ISRC, duration, lyrics, play count, preview MP3).

## `includeMerch` (type: `boolean`):

Keep CD/Vinyl/Cassette/T-shirt variants in the album's releaseVariants array.

## `fetchAlbumDetailsForDiscovery` (type: `boolean`):

For tag/search results, fetch each item's full album page (richer data but more requests).

## `tagsInclude` (type: `array`):

Drop albums whose tag list doesn't include at least one of these (case-insensitive). Filter applied BEFORE billing — no charge for filtered entities.

## `tagsExclude` (type: `array`):

Drop albums whose tag list includes any of these.

## `minPrice` (type: `integer`):

Minimum 'minimum price' for paid albums. 0 = include free / name-your-price.

## `maxPrice` (type: `integer`):

Maximum 'minimum price'. 0 means no upper limit.

## `freeOnly` (type: `boolean`):

Keep only releases with free downloads available.

## `paidOnly` (type: `boolean`):

Keep only releases with a non-zero minimum price.

## `releaseDateFrom` (type: `string`):

ISO date or YYYY-MM-DD. Drop albums released before this.

## `releaseDateTo` (type: `string`):

ISO date or YYYY-MM-DD. Drop albums released after this.

## `formats` (type: `array`):

Keep only releases offered in any of these formats (e.g. 'vinyl', 'cassette', 'cd'). Substring match.

## `minSupporters` (type: `integer`):

Drop albums below this supporter count. Bandcamp's unique 'supported by' signal.

## `minTracks` (type: `integer`):

Drop albums with fewer tracks than this (e.g. filter out singles).

## `keywordsInclude` (type: `array`):

Drop albums whose title + description + tags don't include at least one of these (case-insensitive).

## `keywordsExclude` (type: `array`):

Drop albums whose text contains any of these keywords.

## `location` (type: `string`):

Substring match on the artist's location (e.g. 'Berlin', 'Brooklyn'). Case-insensitive.

## `labelsOnly` (type: `boolean`):

Keep only Bandcamp pages flagged as labels.

## `signedToLabelOnly` (type: `boolean`):

Drop unsigned/self-released artists.

## `hasContact` (type: `boolean`):

Drop artists without any extractable email, website or social handle (useful for lead-gen runs).

## `includeContactInfo` (type: `boolean`):

Parse emails, websites and canonical socials from bio + sites array.

## `includeAnalytics` (type: `boolean`):

Per-artist average album price, release cadence, top tags, total supporters, dominant formats, label list, paid-vs-free ratio.

## `includeTagClustering` (type: `boolean`):

Add a tagClusters block listing which tags co-occur across the artist's catalog (useful for genre micro-niche discovery).

## `enableHistory` (type: `boolean`):

Persist a timestamped snapshot every run into a named store, then compute deltas (releases per month, supporter growth per day, price drift, trend) vs the previous run. Schedule daily/weekly to build a time-series.

## `historyDatasetName` (type: `string`):

Named key-value store to persist snapshots into. Reused across scheduled runs.

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

Optional. Proxies are included and configured automatically — leave empty unless overriding.

## `maxConcurrency` (type: `integer`):

Parallel workers.

## `minDelayMs` (type: `integer`):

Per-request throttle.

## Actor input object example

```json
{
  "artistUrls": [
    "https://daughters.bandcamp.com/",
    "https://kingkrule.bandcamp.com/",
    "https://ipecacrecordings.bandcamp.com/"
  ],
  "artistSubdomains": [
    "daughters",
    "kingkrule",
    "noisia"
  ],
  "albumUrls": [
    "https://daughters.bandcamp.com/album/you-wont-get-what-you-want"
  ],
  "tags": [
    "noise-rock",
    "ambient",
    "black-metal"
  ],
  "searchQueries": [
    "lo-fi",
    "drone metal"
  ],
  "maxAlbumsPerArtist": 50,
  "maxTracksPerArtist": 0,
  "maxDiscoveryItemsPerTag": 100,
  "includeArtist": true,
  "includeDiscography": true,
  "includeTrackDetails": true,
  "includeMerch": true,
  "fetchAlbumDetailsForDiscovery": false,
  "tagsInclude": [
    "ambient",
    "drone"
  ],
  "tagsExclude": [
    "country"
  ],
  "minPrice": 0,
  "maxPrice": 0,
  "freeOnly": false,
  "paidOnly": false,
  "formats": [
    "vinyl"
  ],
  "minSupporters": 0,
  "minTracks": 0,
  "labelsOnly": false,
  "signedToLabelOnly": false,
  "hasContact": false,
  "includeContactInfo": true,
  "includeAnalytics": true,
  "includeTagClustering": false,
  "enableHistory": false,
  "historyDatasetName": "bandcamp-history",
  "maxConcurrency": 5,
  "minDelayMs": 1000
}
```

# Actor output Schema

## `summary` (type: `string`):

Counts of scraped records and pointers to the dataset and historical snapshot 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 = {
    "artistUrls": [
        "https://daughters.bandcamp.com/"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("brilliant_gum/bandcamp-music-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 = { "artistUrls": ["https://daughters.bandcamp.com/"] }

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

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

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

```

## CLI example

```bash
echo '{
  "artistUrls": [
    "https://daughters.bandcamp.com/"
  ]
}' |
apify call brilliant_gum/bandcamp-music-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=brilliant_gum/bandcamp-music-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Bandcamp Music Insights Scraper",
        "description": "Bandcamp artists, albums, tracks, merch & supporter communities. ISRC codes, label graphs, contact extraction, tag-based discovery. Input-time filters and history tracking.",
        "version": "1.0",
        "x-build-id": "3aJS5Lzkb4k6ugAY4"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/brilliant_gum~bandcamp-music-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-brilliant_gum-bandcamp-music-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/brilliant_gum~bandcamp-music-scraper/runs": {
            "post": {
                "operationId": "runs-sync-brilliant_gum-bandcamp-music-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/brilliant_gum~bandcamp-music-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-brilliant_gum-bandcamp-music-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "artistUrls": {
                        "title": "Artist URLs",
                        "type": "array",
                        "description": "Bandcamp artist or label URLs (e.g. https://daughters.bandcamp.com/, https://kingkrule.bandcamp.com/). Album/track URLs are also accepted and routed to the right handler.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "artistSubdomains": {
                        "title": "Artist subdomains",
                        "type": "array",
                        "description": "Bare Bandcamp subdomains (e.g. 'daughters', 'noisia'). Convenient for CSV lists.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "albumUrls": {
                        "title": "Album URLs",
                        "type": "array",
                        "description": "Direct album URLs (e.g. https://daughters.bandcamp.com/album/you-wont-get-what-you-want).",
                        "items": {
                            "type": "string"
                        }
                    },
                    "trackUrls": {
                        "title": "Track URLs",
                        "type": "array",
                        "description": "Direct track URLs (e.g. https://daughters.bandcamp.com/track/satan-in-the-wait).",
                        "items": {
                            "type": "string"
                        }
                    },
                    "tags": {
                        "title": "Tag-based discovery",
                        "type": "array",
                        "description": "Tags to browse via Bandcamp's discover API (e.g. 'noise-rock', 'ambient', 'black-metal'). Returns the top albums currently surfaced for each tag.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "searchQueries": {
                        "title": "Search queries (discovery)",
                        "type": "array",
                        "description": "Free-text queries — routed through the discover API.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxAlbumsPerArtist": {
                        "title": "Max albums per artist",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Cap on albums fetched per artist (drawn from the discography grid).",
                        "default": 50
                    },
                    "maxTracksPerArtist": {
                        "title": "Max standalone tracks per artist (0 = unlimited within page)",
                        "minimum": 0,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Cap on standalone tracks fetched per artist (tracks not bundled in an album).",
                        "default": 0
                    },
                    "maxDiscoveryItemsPerTag": {
                        "title": "Max discovery items per tag",
                        "minimum": 1,
                        "maximum": 500,
                        "type": "integer",
                        "description": "Cap on items returned per tag via the discover API.",
                        "default": 100
                    },
                    "includeArtist": {
                        "title": "Include artist record",
                        "type": "boolean",
                        "description": "Push one artist record per artist scraped (in addition to per-album/track records).",
                        "default": true
                    },
                    "includeDiscography": {
                        "title": "Walk the discography",
                        "type": "boolean",
                        "description": "Fetch every album in the artist's discography (up to 'Max albums per artist').",
                        "default": true
                    },
                    "includeTrackDetails": {
                        "title": "Include per-track records",
                        "type": "boolean",
                        "description": "Push one record per track inside each album (with ISRC, duration, lyrics, play count, preview MP3).",
                        "default": true
                    },
                    "includeMerch": {
                        "title": "Include merch release variants",
                        "type": "boolean",
                        "description": "Keep CD/Vinyl/Cassette/T-shirt variants in the album's releaseVariants array.",
                        "default": true
                    },
                    "fetchAlbumDetailsForDiscovery": {
                        "title": "Walk into discovered items",
                        "type": "boolean",
                        "description": "For tag/search results, fetch each item's full album page (richer data but more requests).",
                        "default": false
                    },
                    "tagsInclude": {
                        "title": "[Filters — applied before billing] Must include tags",
                        "type": "array",
                        "description": "Drop albums whose tag list doesn't include at least one of these (case-insensitive). Filter applied BEFORE billing — no charge for filtered entities.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "tagsExclude": {
                        "title": "Must NOT include tags",
                        "type": "array",
                        "description": "Drop albums whose tag list includes any of these.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "minPrice": {
                        "title": "Min album price",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Minimum 'minimum price' for paid albums. 0 = include free / name-your-price.",
                        "default": 0
                    },
                    "maxPrice": {
                        "title": "Max album price (0 = unlimited)",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Maximum 'minimum price'. 0 means no upper limit.",
                        "default": 0
                    },
                    "freeOnly": {
                        "title": "Free releases only",
                        "type": "boolean",
                        "description": "Keep only releases with free downloads available.",
                        "default": false
                    },
                    "paidOnly": {
                        "title": "Paid releases only",
                        "type": "boolean",
                        "description": "Keep only releases with a non-zero minimum price.",
                        "default": false
                    },
                    "releaseDateFrom": {
                        "title": "Earliest release date",
                        "type": "string",
                        "description": "ISO date or YYYY-MM-DD. Drop albums released before this."
                    },
                    "releaseDateTo": {
                        "title": "Latest release date",
                        "type": "string",
                        "description": "ISO date or YYYY-MM-DD. Drop albums released after this."
                    },
                    "formats": {
                        "title": "Format allow-list",
                        "type": "array",
                        "description": "Keep only releases offered in any of these formats (e.g. 'vinyl', 'cassette', 'cd'). Substring match.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "minSupporters": {
                        "title": "Min supporters per album",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Drop albums below this supporter count. Bandcamp's unique 'supported by' signal.",
                        "default": 0
                    },
                    "minTracks": {
                        "title": "Min tracks per album",
                        "minimum": 0,
                        "type": "integer",
                        "description": "Drop albums with fewer tracks than this (e.g. filter out singles).",
                        "default": 0
                    },
                    "keywordsInclude": {
                        "title": "Must contain keywords",
                        "type": "array",
                        "description": "Drop albums whose title + description + tags don't include at least one of these (case-insensitive).",
                        "items": {
                            "type": "string"
                        }
                    },
                    "keywordsExclude": {
                        "title": "Must NOT contain keywords",
                        "type": "array",
                        "description": "Drop albums whose text contains any of these keywords.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "location": {
                        "title": "Artist location filter",
                        "type": "string",
                        "description": "Substring match on the artist's location (e.g. 'Berlin', 'Brooklyn'). Case-insensitive."
                    },
                    "labelsOnly": {
                        "title": "Labels only (skip individual artists)",
                        "type": "boolean",
                        "description": "Keep only Bandcamp pages flagged as labels.",
                        "default": false
                    },
                    "signedToLabelOnly": {
                        "title": "Artists signed to a label only",
                        "type": "boolean",
                        "description": "Drop unsigned/self-released artists.",
                        "default": false
                    },
                    "hasContact": {
                        "title": "Has extractable contact info only",
                        "type": "boolean",
                        "description": "Drop artists without any extractable email, website or social handle (useful for lead-gen runs).",
                        "default": false
                    },
                    "includeContactInfo": {
                        "title": "Extract contact info (lead-gen)",
                        "type": "boolean",
                        "description": "Parse emails, websites and canonical socials from bio + sites array.",
                        "default": true
                    },
                    "includeAnalytics": {
                        "title": "Compute analytics block",
                        "type": "boolean",
                        "description": "Per-artist average album price, release cadence, top tags, total supporters, dominant formats, label list, paid-vs-free ratio.",
                        "default": true
                    },
                    "includeTagClustering": {
                        "title": "Compute tag co-occurrence clusters",
                        "type": "boolean",
                        "description": "Add a tagClusters block listing which tags co-occur across the artist's catalog (useful for genre micro-niche discovery).",
                        "default": false
                    },
                    "enableHistory": {
                        "title": "Enable historical tracking (supporter + release-velocity deltas)",
                        "type": "boolean",
                        "description": "Persist a timestamped snapshot every run into a named store, then compute deltas (releases per month, supporter growth per day, price drift, trend) vs the previous run. Schedule daily/weekly to build a time-series.",
                        "default": false
                    },
                    "historyDatasetName": {
                        "title": "History store name",
                        "type": "string",
                        "description": "Named key-value store to persist snapshots into. Reused across scheduled runs.",
                        "default": "bandcamp-history"
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Optional. Proxies are included and configured automatically — leave empty unless overriding."
                    },
                    "maxConcurrency": {
                        "title": "Max concurrency",
                        "minimum": 1,
                        "maximum": 20,
                        "type": "integer",
                        "description": "Parallel workers.",
                        "default": 5
                    },
                    "minDelayMs": {
                        "title": "Min delay between requests (ms)",
                        "minimum": 200,
                        "maximum": 10000,
                        "type": "integer",
                        "description": "Per-request throttle.",
                        "default": 1000
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
