# Discogs Scraper - Vinyl Records, Prices & Price Stats (`abotapi/discogs-scraper`) Actor

Scrape the Discogs catalogue by keyword, filter or link: releases, masters, artists and labels with tracklists, credits, matrix numbers, images and videos. Cross-pressing price statistics: lowest, median, highest and average asking price across an album's pressings, plus want/have demand.

- **URL**: https://apify.com/abotapi/discogs-scraper.md
- **Developed by:** [Abot API](https://apify.com/abotapi) (community)
- **Categories:** E-commerce
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 records

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

## Discogs Scraper: Records, Marketplace Prices & Price Statistics

Discogs Scraper turns the Discogs catalogue and marketplace into structured data. Search by keyword or filter, or paste release, master, artist and label links, and get releases, masters, artists and labels with tracklists, credits, images, videos and live marketplace prices. Turn on Price statistics and every release or master also carries the lowest, median, highest and average asking price across the album's own pressings, not just one pressing's price. Export to JSON, CSV or Excel, or pull results straight into your app through the API.

### Why This Scraper?

- **Cross-pressing price statistics.** The same album can exist as dozens of pressings with prices that differ by an order of magnitude or more. Turn on Price statistics and a record carries the lowest, median, highest and average asking price sampled across the album's pressings, not just one pressing's number.
- **Four record kinds.** Release (a specific pressing), master (an album across every pressing), artist and label, all sharing one flat schema.
- **Nine search filters.** Artist, label, genre, style, format, country, year, catalog number and barcode. They combine with a keyword, and they also work completely on their own.
- **Full sort control.** Order results by relevance, year, title, format, label, catalog number, country, most collected or most wanted, the same sort keys the catalogue itself offers.
- **No account required.** Every route this actor reads is the public, anonymous catalogue API. Adding your own free Discogs token is optional and only speeds up large pulls and unlocks per-condition valuation.
- **Every column always present.** Fields a run does not fill ship as `null` rather than being dropped, so repeated runs join cleanly in a warehouse or spreadsheet.
- **Built for monitoring.** Incremental mode returns only new and changed records on a recurring schedule, and a blocked run fails loudly instead of quietly returning an empty dataset.

### Use Cases

- **Collectors and sellers pricing a pressing:** compare an album's own pressings by asking price, total copies for sale and demand, before buying or listing.
- **Price tracking over time:** schedule the actor with incremental mode on to watch an album's price statistics or a search result move, and get only what changed.
- **Music data and analytics projects:** build a catalogue dataset of releases, artists and labels with tracklists, credits and identifiers for research or cataloguing tools.
- **Marketplace and reselling tools:** feed live marketplace numbers (copies for sale, lowest price, demand ratio) into your own pricing or alerting app.
- **Label and artist research:** pull an artist's discography or a label's release history and sub-labels in one structured run.

### Data You Get

> Sample shape: values are illustrative placeholders, not from a live record.

| Field | Example |
| --- | --- |
| `kind` | `"release"` (also `"master"`, `"artist"`, `"label"`) |
| `recordId` / `entityId` | `"release:1000001"` / `1000001` |
| `sourceMode` / `searchQuery` | `"search"` / `"sample album"` |
| `title` / `name` | `"Sample Album"` (release, master); `name` on artist and label |
| `artistName` / `artistNames` | `"Sample Artist"` / `["Sample Artist"]` |
| `year` / `country` | `"1991"` / `"UK"` |
| `primaryGenre` / `primaryStyle` | `"Rock"` / `"Grunge"` |
| `primaryFormat` / `primaryLabel` | `"Vinyl"` / `"Sample Records"` |
| `catalogNumber` / `barcodes` | `"SR-1234"` / `["0000000000000"]` |
| `numForSale` / `lowestPrice` / `currency` | `42` / `12.5` / `"USD"` (one pressing) |
| `priceLowest` / `priceMedian` / `priceHighest` / `priceAverage` | `8.0` / `24.99` / `89.0` / `30.15` (across pressings, when Price statistics is on) |
| `priceVersionsSampled` / `masterVersionsTotal` / `totalForSaleAcrossVersions` | `5` / `120` / `61` |
| `haveCount` / `wantCount` / `wantToHaveRatio` | `3400` / `480` / `0.1412` |
| `ratingAverage` / `ratingCount` | `4.1` / `210` |
| `priceSuggestionMint` ... `priceSuggestionPoor` | per-condition valuation, only with your own Discogs token |
| `tracklist` / `trackCount` | full tracklist / `8` (with Read the full record on) |
| `images` / `coverImageUrl` | image list / cover URL |
| `url` / `marketplaceUrl` | catalogue page / marketplace listing page |
| `detailLoaded` / `priceStatsLoaded` | `true` / `true` |
| `scrapedAt` | `"2026-09-30T12:00:00+00:00"` |
| `changeType` / `changedFields` / `firstSeenAt` / `lastSeenAt` | incremental mode: `"UPDATED"` / `["lowestPrice"]` / timestamps |

Artist records also carry `realName`, `profile`, `nameVariations`, `aliases`, `members`, `groups`, `externalUrls`, `contactEmails` and `contactPhones`. Label records also carry `profile`, `contactInfo`, `parentLabelId`, `parentLabelName`, `subLabels`, `subLabelCount`, `contactEmails` and `contactPhones`, all taken from the label's or artist's own published profile block.

### How to Use

1. Pick a **mode**: `search` (keyword and filter searches across the catalogue) or `url` (paste release, master, artist or label links or ids).
2. Search mode: add `searchQueries`, choose `searchType`, and narrow with any of the filters (`artist`, `label`, `genre`, `style`, `format`, `country`, `year`, `catalogNumber`, `barcode`). Filters work with no keyword at all. Order results with `sortBy` and `sortOrder`.
3. Set **Max records** (`maxItems`) to control run size and cost, then click Start.
4. Turn on `priceStats` for the cross-pressing price distribution, and `fetchDetails` for the full catalogue entry (tracklist, credits, images). Download the dataset as JSON, CSV or Excel, or read it through the API.

**Search by keyword, with cross-pressing price statistics:**

```json
{
  "mode": "search",
  "searchQueries": ["nirvana nevermind"],
  "searchType": "release",
  "priceStats": true,
  "maxPriceVersions": 5,
  "currency": "USD",
  "maxItems": 10
}
```

**Filter-only search, no keyword:**

```json
{
  "mode": "search",
  "searchQueries": [],
  "searchType": "release",
  "label": "Sub Pop",
  "format": "Vinyl",
  "country": "Japan",
  "sortBy": "want",
  "sortOrder": "desc",
  "maxItems": 25
}
```

**Paste catalogue links or ids (release, master, artist, label):**

```json
{
  "mode": "url",
  "startUrls": [
    "https://www.discogs.com/release/249504",
    "m13814",
    "a72872"
  ],
  "fetchDetails": true
}
```

**Daily price monitoring for a search, only what changed:**

```json
{
  "mode": "search",
  "searchQueries": ["blue note 1568"],
  "priceStats": true,
  "incrementalMode": true,
  "stateKey": "blue-note-watch",
  "maxItems": 0
}
```

#### Run it from your code

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("abotapi/discogs-scraper").call(run_input={
    "mode": "search", "searchQueries": ["nirvana nevermind"], "maxItems": 10
})
for record in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(record["title"], record.get("lowestPrice"))
```

JavaScript:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('abotapi/discogs-scraper').call({
    mode: 'search', searchQueries: ['nirvana nevermind'], maxItems: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.

#### URL mode link types

Running URL mode with the link field left untouched now scrapes one example of each link type below instead of nothing. List and search links are walked page by page and share what is left of Max records after the single-record links, split evenly. An entry that matches none of these is skipped with a warning naming it; if nothing usable remains the run stops with a clear message.

| Link | Example | Result |
|---|---|---|
| Release | `https://api.discogs.com/releases/249504` or `https://www.discogs.com/release/249504` | One full release record |
| Master | `https://api.discogs.com/masters/13814` | One full master record |
| Artist | `https://api.discogs.com/artists/72872` | One full artist record |
| Label | `https://api.discogs.com/labels/895` | One full label record |
| Keyword search | `https://api.discogs.com/database/search?q=nirvana&type=release` or `https://www.discogs.com/search/?q=nirvana&type=release` | The search results, with the link's `q`, `type`, filters and sort applied |
| Master versions | `https://api.discogs.com/masters/13814/versions` | Every pressing of the master, up to Max records |
| Artist releases | `https://api.discogs.com/artists/72872/releases` | The artist's releases and masters, up to Max records |
| Label releases | `https://api.discogs.com/labels/895/releases` | The label's releases, up to Max records |
| Marketplace page | `https://www.discogs.com/sell/release/249504` | The release it lists |

A list link (a master's versions, an artist's or a label's releases) returns the list rows, not the parent master, artist or label record that older versions of this actor returned for the same link; paste the plain master, artist or label link for the parent record. Site links are mapped to the catalogue id they name; the site page itself is never fetched. Search keywords and the artist, label, genre, style, format and country filters are matched as plain text names (no ids needed); a search that matches nothing logs a warning.

#### Search mode, URL mode and price statistics

Search mode results already carry title, artist, year, country, format, labels, genres, styles, barcode and the have/want counts, at no extra charge. Turn on `fetchDetails` to also read each result's full catalogue entry (tracklist, credits, matrix numbers, images, videos, notes, rating); in URL mode a single release, master, artist or label link is always read in full and never charged for it, so that toggle changes nothing for those links. List links (a master's versions, an artist's or a label's releases) and search links behave like search mode: base rows by default, and with `fetchDetails` or `priceStats` on, each row that is enriched carries one Detail enrichment charge. Turn on `priceStats` to also walk the album's other pressings and publish the price distribution; this is separate from `fetchDetails` and can be used alone or together with it. Each result for which either optional lookup succeeds carries one Detail enrichment charge on top of the per-record charge; a lookup that fails still ships the base record, uncharged.

The Results tab has extra views: Price statistics, Release & master details, Artists & labels and Changes (incremental mode), each showing the columns that matter for that task.

#### Resume and recurring updates

- **Resume** (`resumeFromRunId`) continues one interrupted run: paste its run or dataset id and the actor skips every record already collected there, so you are not charged twice for the same record.
- **Incremental mode** (`incrementalMode`) is for running the same search or url set again and again (for example daily, to watch a price move). Each record is classified `NEW`, `UPDATED` (with `changedFields`), `UNCHANGED` (suppressed and not billed unless `emitUnchanged` is on), `REAPPEARED` or `EXPIRED` (only with `emitExpired`, and only after a run whose scan was not stopped early; see the note below). `stateKey` names or shares the stored state; leave it empty to let the actor derive one from the search, filter and lookup setup. Every returned field counts as a change by default, including prices, `numForSale` and the price statistics (`priceLowest`, `priceMedian`, `priceHighest`, `priceAverage`), because a price move is what monitoring is for; only `scrapedAt` and the derived alias columns (`_index`, `thumb`, `community`, the `*_flat` text columns, `format`, `catno`, `barcode` and the snake_case copies) are excluded. List fields in `ignoreFieldsForChanges` to stop a noisy statistic from marking a record `UPDATED`. Changing that list on a campaign with saved state can change which records read as `UPDATED` on the next run.

**A note on Emit expired records and Max records.** `emitExpired` is only produced after a run's live scan reaches a natural end (not stopped early by `maxItems` or `maxPages`, and not a resumed run; with several keywords, see the next note). When that condition is met, every tracked record from the state that the live scan did not see this run is returned as `EXPIRED`, and this batch is not itself limited by `maxItems`: a small `maxItems` bounds the live scan, not the size of the EXPIRED batch it can trigger. On a large, long-running monitoring campaign where most of the tracked scope has genuinely disappeared, a capped run can therefore return far more rows, and cost more, than `maxItems` suggests. If you monitor a large scope with `emitExpired` on, watch the first few scheduled runs' item counts, or keep `maxItems` at `0` so the run's own size is the only limit in play.

**A note on Emit expired records with several search keywords.** With more than one keyword, each keyword gets its own share of `maxItems`. If one keyword is stopped by its share, the scan counts as stopped early and no `EXPIRED` rows are produced that run. Use `maxItems` `0` to scan every keyword fully.

### Send results into your apps (MCP connectors)

Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step **after** the scrape: the Apify dataset is never changed.

**What gets written to the connector:** a condensed, human-readable **summary** of each record, not the full JSON. Each item becomes one entry with a **title** and its key fields flattened to plain text. The **complete record always stays in the Apify dataset**.

1. Authorize a connector once under **Apify, Settings, Integrations** (Notion, Linear, Airtable, or Apify).
2. Select it in the **"Pipe results into your apps"** input field. (If the picker is empty, you have not authorized a connector yet.)
3. For **Notion**, also set `notionParentPageUrl` to the page where records should be created.

The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.

### Input Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mode` | string | `search` | `search` (catalogue search) or `url` (paste catalogue links or ids). |
| `searchQueries` | array | (none) | Search mode. One or more keywords, each searched separately and given an equal share of Max records. Leave empty for a filter-only search. |
| `searchType` | string | `release` | Search mode. `release`, `master`, `artist`, `label` or `all`. |
| `artist` | string | (none) | Search mode filter, restricts results to this artist. |
| `label` | string | (none) | Search mode filter, restricts results to this record label. |
| `genre` | string | (none) | Search mode filter, one Discogs genre. |
| `style` | string | (none) | Search mode filter, one Discogs style. |
| `format` | string | (none) | Search mode filter, one physical or digital format. |
| `country` | string | (none) | Search mode filter, the country of the pressing. |
| `year` | string | (none) | Search mode filter, a release year or a range such as `1990-1995`. |
| `catalogNumber` | string | (none) | Search mode filter, the label catalog number printed on the spine and label. |
| `barcode` | string | (none) | Search mode filter, a barcode or UPC/EAN. |
| `sortBy` | string | `relevance` | Search mode. `relevance`, `year`, `title`, `format`, `label`, `catno`, `country`, `have` (most collected) or `want` (most wanted). |
| `sortOrder` | string | `desc` | Search mode. `desc` or `asc`, ignored when `sortBy` is `relevance`. |
| `startUrls` | array | one example of each link type | URL mode. Catalogue links (`/release/<id>`, `/master/<id>`, `/artist/<id>`, `/label/<id>`, `/sell/release/<id>`), api.discogs.com resource links, list links and search links (see the table below), or `r<id>` / `m<id>` / `a<id>` / `l<id>` shorthand. A plain number is read as a release id. Left untouched, URL mode scrapes one example of each link type instead of nothing. |
| `urls` | array | (none) | Alias for `startUrls`, entries from both fields are read in order and de-duplicated. |
| `maxItems` | integer | `20` | Stop after this many records across the whole run. `0` for no limit. |
| `maxPages` | integer | (none, no page limit) | Search mode. Each page holds up to 100 records. A hard cap of 10000 pages per keyword or list link applies even when this is empty. |
| `fetchDetails` | boolean | `false` | Read each result's full catalogue entry. Single release, master, artist and label links are always read in full and never charged for it; list and search links behave like search mode (one Detail enrichment charge per enriched row). |
| `priceStats` | boolean | `false` | Build the price distribution (lowest, median, highest, average) across an album's pressings, for releases and masters. |
| `maxPriceVersions` | integer | `5` | Only used when Price statistics is on. How many of an album's pressings to sample (maximum 25). |
| `currency` | string | `USD` | One of 12 currencies for every marketplace price and price statistic. |
| `resumeFromRunId` | string | (none) | Continue one interrupted run without re-collecting or re-charging for records it already returned. |
| `incrementalMode` | boolean | `false` | Return only new and changed records on a recurring run of the same search or url set. |
| `stateKey` | string | (none) | Name a monitoring campaign, or deliberately share state across differently configured runs. |
| `emitUnchanged` | boolean | `false` | Also return (and bill) `UNCHANGED` records. |
| `emitExpired` | boolean | `false` | Also return (and bill) `EXPIRED` records, only after a run whose scan was not stopped early, including a keyword stopped by its share of `maxItems`. |
| `ignoreFieldsForChanges` | array | `[]` | Field names left out when deciding whether a record changed, for example `priceMedian` or `numForSale`. `scrapedAt` and the derived alias columns (`_index`, `thumb`, `community`, the `*_flat` columns, `format`, `catno`, `barcode` and the snake_case copies) are ignored by default. Ignored fields are still returned on every row. |
| `query` | array | unset | Hidden alias of `searchQueries`, so an input written for other Discogs scrapers runs unchanged. The native field wins when both are set. |
| `type` | string | unset | Hidden alias of `searchType`. A native `searchType` other than the default `release` wins when both are set. |
| `maxResults` | integer | unset | Hidden alias of `maxItems`: one cap for the whole run, no minimum (`maxResults: 5` returns at most 5 records). A native `maxItems` other than the default 20 wins when both are set. |
| `getDetails` | boolean | unset | Hidden alias of `fetchDetails`. A native `fetchDetails` set to true wins when both are set. |
| `yearMin` | integer | unset | Hidden alias that builds the start of the `year` range. |
| `yearMax` | integer | unset | Hidden alias that builds the end of the `year` range. |
| `proxyConfig` | object | unset | Hidden alias of `proxyConfiguration`. A native `proxyConfiguration` other than the default Apify Proxy setting wins when both are set; your own proxy choice is always honoured. |
| `mcpConnectors` | array | (none) | Optional: send a summary of each record to apps you authorized under Integrations. |
| `notionParentPageUrl` | string | (none) | Notion connector only: page under which records are created. |
| `maxNotifyListings` | integer | `50` | Cap on records written to each connector per run. Does not affect the dataset. |
| `proxyConfiguration` | object | Apify Proxy | Connection settings. The default is Apify datacenter proxy and needs no configuration; it needs a paid Apify plan (see Plan Requirement). |
| `discogsToken` | string | (none) | Optional. Your own free Discogs personal access token, see below. |

### Output Example

> Sample shape: values are illustrative placeholders, not from a live record.

```json
{
  "kind": "release",
  "recordId": "release:1000001",
  "entityId": 1000001,
  "sourceMode": "search",
  "searchQuery": "sample album",
  "title": "Sample Album",
  "artistName": "Sample Artist",
  "artistNames": ["Sample Artist"],
  "year": "1991",
  "country": "UK",
  "primaryGenre": "Rock",
  "primaryStyle": "Grunge",
  "primaryFormat": "Vinyl",
  "primaryLabel": "Sample Records",
  "labelNames": ["Sample Records"],
  "catalogNumber": "SR-1234",
  "barcodes": ["0000000000000"],
  "genres": ["Rock"],
  "styles": ["Grunge"],
  "formatDescriptions": ["Vinyl", "LP", "Album"],
  "numForSale": 42,
  "lowestPrice": 12.5,
  "currency": "USD",
  "blockedFromSale": false,
  "priceLowest": 8.0,
  "priceMedian": 24.99,
  "priceHighest": 89.0,
  "priceAverage": 30.15,
  "priceCurrency": "USD",
  "priceVersionsSampled": 5,
  "priceVersionsPriced": 5,
  "masterVersionsTotal": 120,
  "totalForSaleAcrossVersions": 61,
  "haveCount": 3400,
  "wantCount": 480,
  "wantToHaveRatio": 0.1412,
  "ratingAverage": 4.1,
  "ratingCount": 210,
  "trackCount": 8,
  "url": "https://www.discogs.com/release/1000001",
  "marketplaceUrl": "https://www.discogs.com/sell/release/1000001",
  "detailLoaded": true,
  "priceStatsLoaded": true,
  "scrapedAt": "2026-09-30T12:00:00+00:00",
  "changeType": "NEW"
}
```

### Plan Requirement

The default proxy setting uses Apify datacenter proxy, which is enough for this actor: no residential proxy is needed. A paid Apify plan is required because the free plan includes no Apify proxy: on a free plan every connection is refused, no backup connection can help, and the run stops with "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required". If a run ends with "Could not load any results page (the requests failed)", that is a connection problem, not an empty search: try again later. When the default connection is unavailable but the backup connection serves pages, the log says "Served over the backup connection (the default connection was unavailable)". If the backup connection cannot load the requested pages either, the run ends with "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search. Please try again later". If you enter your own proxy URLs they are used as given, with no backup connection.

Adding your own free Discogs personal access token is optional: it raises the catalogue's own per-address pacing budget so large pulls finish faster, and it unlocks the per-condition valuation fields that Discogs shows only to a signed-in member.

### FAQ

#### How much does it cost?

Three events exist: **Record** (one per record returned to the dataset), **Detail enrichment** (one per record whose optional extras, the full record read via `fetchDetails` or the price statistics via `priceStats`, succeeded) and **Actor Start** (one per run). Unchanged rows suppressed by Incremental mode, failed lookups and records skipped on resume are not charged. The Pricing tab shows the current rates. Use Max records to cap the cost of any run.

#### Is it legal to scrape Discogs?

This actor collects only publicly available catalogue and marketplace data through Discogs' own documented, anonymous API. You are responsible for how you use the output: follow Discogs' terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Contact details in `contactEmails` and `contactPhones` come only from a label's or artist's own public profile block, where they published them themselves.

#### Can I get only new or changed records on a schedule?

Yes. Schedule the actor from the Schedules tab and turn on Incremental mode. Each run then returns only new, updated and reappeared records, and unchanged ones are not billed unless you turn on Emit unchanged.

#### What is the difference between a pressing's price and Price statistics?

`lowestPrice` and `numForSale` describe ONE pressing, the exact release you searched or pasted. `priceLowest`, `priceMedian`, `priceHighest` and `priceAverage` describe the DISTRIBUTION across an album's other pressings, sampled with Price statistics on. A single pressing's lowest price can look cheap or expensive purely by chance; the distribution across pressings is the number that tells you whether that price is actually low or high for the album.

#### Why did my run fail instead of returning an empty dataset?

If Discogs refuses every request the run stops with a clear message, so "no results matched" is never confused with "nothing could be read". Run it again in a few minutes. A search that genuinely matches nothing, or an incremental run whose records were all unchanged, still succeeds with zero new rows.

#### Can I use it with AI agents or MCP?

Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.

### 🔗 Want more e-commerce data?

Pair this actor with these related scrapers from the same team:

<table>
<tr><td>🛒 <a href="https://apify.com/abotapi/seatgeek-events-scraper"><b>SeatGeek Scraper</b></a><br>Scrape SeatGeek events with ticket price ranges (lowest, highest, average, median)...</td><td>👗 <a href="https://apify.com/abotapi/goat-sneaker-scraper"><b>GOAT Scraper</b></a><br>Scrape GOAT (goat.com), the sneaker and streetwear resale marketplace. Search or paste...</td></tr>
<tr><td>👗 <a href="https://apify.com/abotapi/kream-scraper"><b>KREAM Scraper</b></a><br>Scrape KREAM (kream.co.kr) sneaker and fashion resale data by keyword search, filters...</td><td>🧩 <a href="https://apify.com/abotapi/tidal-catalog-scraper"><b>TIDAL Scraper</b></a><br>Scrape TIDAL tracks, albums, artists, playlists and public mixes by search phrase or URL...</td></tr>
<tr><td>🧩 <a href="https://apify.com/abotapi/deezer-com-scraper"><b>Deezer Scraper</b></a><br>Scrape Deezer by keyword or URL. Extract tracks, albums, artists, playlists, lyrics...</td><td>🧩 <a href="https://apify.com/abotapi/anghami-catalog-scraper"><b>Anghami Scraper</b></a><br>Scrape Anghami's public catalog by keyword or player URL. Rows carry their kind (song...</td></tr>
</table>

👉 [Browse all abotapi scrapers](https://apify.com/abotapi)

### 💬 Support

- 🐞 **Found a bug or a missing field?** Open a ticket on the [Issues tab](https://apify.com/abotapi/discogs-scraper/issues/open). We usually reply within hours.
- 🛠️ **Need another site, extra fields or a private build?** Email <contact@abotapi.com>.
- ⭐ **Enjoying it?** A quick review on the actor page helps other users find it.

# Actor input Schema

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

Search runs keyword and filter searches across the Discogs catalogue. URL mode reads only the links you paste: single releases, masters, artists and labels, plus version, release and search lists walked page by page.

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

Search mode only, ignored in URL mode. One or more keywords, for example "nirvana nevermind" or "blue note 1568". Each keyword is searched separately and gets an equal share of Max records. Leave empty to run a filter only search using the filters below.

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

Search mode only. Which kind of catalogue entry the search returns. Releases are individual pressings and are the only kind with marketplace prices. Masters are albums across all their pressings.

## `artist` (type: `string`):

Search mode only. Restrict results to this artist, for example "Nirvana". Works with or without a keyword.

## `label` (type: `string`):

Search mode only. Restrict results to this record label, for example "Sub Pop" or "Blue Note".

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

Search mode only. Restrict results to one Discogs genre, for example "Rock", "Jazz", "Electronic", "Hip Hop", "Funk / Soul", "Classical".

## `style` (type: `string`):

Search mode only. Restrict results to one Discogs style, the finer grained tag under a genre, for example "Grunge", "Hard Bop", "Deep House", "Post Punk".

## `format` (type: `string`):

Search mode only. Restrict results to one physical or digital format, for example "Vinyl", "LP", "CD", "Cassette", "7"", "Album", "File".

## `country` (type: `string`):

Search mode only. Restrict results to the country of the pressing, for example "UK", "US", "Japan", "Germany", "Europe".

## `year` (type: `string`):

Search mode only. Restrict results to a release year, for example "1991". A range such as "1990-1995" is also accepted by Discogs.

## `catalogNumber` (type: `string`):

Search mode only. Restrict results to a label catalog number, for example "DGC2-4425" or "BLP 1568". This is the number printed on the spine and label.

## `barcode` (type: `string`):

Search mode only. Restrict results to a barcode or UPC/EAN, for example "720642442517". The fastest way to identify an exact pressing you are holding.

## `sortBy` (type: `string`):

Search mode only. Discogs applies this order to the search itself, so it changes which results the record budget keeps. "Most wanted" and "Most collected" are the two orders collectors use to find the desirable pressings first.

## `sortOrder` (type: `string`):

Search mode only. Direction for the sort above. Ignored when Sort results by is set to best match.

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

URL mode only, ignored in search mode. Paste Discogs release, master, artist or label links (https://www.discogs.com/release/249504-... , /master/13814, /artist/72872, /label/895), api.discogs.com resource links, a master's versions list (/masters/13814/versions), an artist's or label's releases list (/artists/72872/releases, /labels/895/releases) or a keyword search link (/database/search?q=nirvana\&type=release, /search/?q=nirvana). List and search links are walked page by page up to Max records. Bare ids in the r249504 / m13814 / a72872 / l895 shorthand are accepted too, and a plain number is read as a release id. Left untouched, this field scrapes one example of each link type.

## `urls` (type: `array`):

Alias for Catalogue links or ids, accepted under the name other Apify actors use for URL mode. Entries from both fields are read in order and de-duplicated.

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

Stop after this many records across the whole run. This is the run's cap. Use 0 for no limit, the run then stops when the searches run out of results.

## `maxPages` (type: `integer`):

Search mode only. Leave empty for no page limit, the run then stops at Max records or when Discogs runs out of results. Each page holds up to 100 records. Whatever you enter, a hard cap of 10000 pages per keyword or list link is enforced in code.

## `fetchDetails` (type: `boolean`):

Search mode: off by default, because search results already carry title, artist, year, country, format, labels, genres, styles, barcode and the have/want counts. Turn on to also read every result's full catalogue entry (tracklist, credits, images, videos, identifiers, matrix numbers, notes, rating) plus its live marketplace price. Each result read this way adds one Detail enrichment event. In URL mode a single release, master, artist or label link is always read in full and never charged for it, so this toggle changes nothing for those links; list links (versions or releases) and search links behave like search mode, with one Detail enrichment event per enriched row.

## `priceStats` (type: `boolean`):

Off by default. The headline feature: instead of one pressing's lowest asking price, this walks the album's other pressings and returns the lowest, median, highest and average asking price across them, how many copies are for sale in total, and the want to have demand ratio for the whole album. Applies to releases and masters in both modes. Costs a handful of extra lookups per record and adds one Detail enrichment event.

## `maxPriceVersions` (type: `integer`):

Only used when Price statistics is on. How many of an album's pressings to ask the marketplace about, taken in descending order of how many collectors own them, so the sample follows the pressings with a live market. Higher values give a better distribution and cost more lookups per record.

## `currency` (type: `string`):

Currency for every marketplace price and price statistic. Discogs converts from the seller's own currency.

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

Apify Proxy settings. The default works on every plan, including the free tier, and needs no configuration.

## `discogsToken` (type: `string`):

Optional and never required. Create a free token at Discogs, Settings, Developers. It lets large pulls finish faster, and it unlocks the per condition valuation fields (Mint through Poor) that Discogs shows only to a signed in member. Leave empty to run fully anonymously.

## `resumeFromRunId` (type: `string`):

Paste a previous run ID or dataset ID to continue a large pull without returning records already collected there.

## `incrementalMode` (type: `boolean`):

Turn this on for daily or recurring monitoring, for example to watch an album's price statistics move. The first run returns every matching record as NEW. Later runs normally return only NEW, UPDATED and REAPPEARED records. Turn on Emit unchanged or Emit expired only when you also want those rows returned (and billed). State is kept separately for each keyword, entity kind, filter, sort and lookup setup; use State key to name or deliberately share a monitoring campaign.

## `stateKey` (type: `string`):

Optional. Name this monitoring campaign to keep its state stable, or deliberately share state across differently configured runs. Leave empty to let the actor derive a key automatically from the search, filter and lookup settings.

## `emitUnchanged` (type: `boolean`):

Off by default. Turn on to also return records that have not changed since the last run, marked UNCHANGED. This returns, and bills, extra rows you already have.

## `emitExpired` (type: `boolean`):

Off by default. Turn on to also return records that were present in a previous run but are no longer found, marked EXPIRED. Only produced once a run has fully scanned the tracked search: not when Max records capped it, not when Max result pages capped it, and not when Resume was used. This returns, and bills, extra rows.

## `ignoreFieldsForChanges` (type: `array`):

Optional. Field names to leave out when deciding whether a record changed, for example priceMedian or numForSale if you do not want a price statistic wobble to mark a record UPDATED. The fields are still returned on every row. scrapedAt and the derived alias columns (\_index, thumb, community, the \*\_flat columns, format, catno, barcode and the snake_case copies) are ignored by default. Changing this list on a campaign that already has saved state can mark records UNCHANGED or UPDATED differently on the next run.

## `mcpConnectors` (type: `array`):

Optionally send results into the apps you already use, via Model Context Protocol (MCP) connectors. Authorize one under Apify, Settings, API & Integrations, then select it here. Notion gets a rich page per record export; other connectors get a best effort write or digest. Leave empty to skip; never changes the dataset output.

## `notionParentPageUrl` (type: `string`):

URL or id of the Notion page under which record pages are created. Required to enable the Notion export; ignored by other connectors.

## `maxNotifyListings` (type: `integer`):

Cap on records written to each connector per run. Does not affect the dataset.

## `query` (type: `array`):

Alias of searchQueries. Alias accepted so an input written for another Discogs scraper runs unchanged; the native field wins when both are set.

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

Alias of searchType (release, master, artist, label, all). Alias accepted so an input written for another Discogs scraper runs unchanged; the native field wins when both are set.

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

Alias of maxItems (no minimum of 20). Alias accepted so an input written for another Discogs scraper runs unchanged; the native field wins when both are set.

## `getDetails` (type: `boolean`):

Alias of fetchDetails. Alias accepted so an input written for another Discogs scraper runs unchanged; the native field wins when both are set.

## `yearMin` (type: `integer`):

Earliest release year (with yearMax builds the year range). Alias accepted so an input written for another Discogs scraper runs unchanged; the native field wins when both are set.

## `yearMax` (type: `integer`):

Latest release year (with yearMin builds the year range). Alias accepted so an input written for another Discogs scraper runs unchanged; the native field wins when both are set.

## `proxyConfig` (type: `object`):

Alias of proxyConfiguration. Alias accepted so an input written for another Discogs scraper runs unchanged; the native field wins when both are set.

## Actor input object example

```json
{
  "mode": "search",
  "searchQueries": [
    "nirvana nevermind"
  ],
  "searchType": "release",
  "sortBy": "relevance",
  "sortOrder": "desc",
  "startUrls": [
    "https://api.discogs.com/releases/249504",
    "https://api.discogs.com/masters/13814",
    "https://api.discogs.com/artists/72872",
    "https://api.discogs.com/labels/895",
    "https://api.discogs.com/database/search?q=nirvana&type=release",
    "https://api.discogs.com/masters/13814/versions",
    "https://api.discogs.com/artists/72872/releases",
    "https://api.discogs.com/labels/895/releases"
  ],
  "maxItems": 20,
  "fetchDetails": false,
  "priceStats": false,
  "maxPriceVersions": 5,
  "currency": "USD",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50
}
```

# Actor output Schema

## `overview` (type: `string`):

No description

## `prices` (type: `string`):

No description

## `details` (type: `string`):

No description

## `artists_labels` (type: `string`):

No description

## `changes` (type: `string`):

No description

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

No description

# 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",
    "searchQueries": [
        "nirvana nevermind"
    ],
    "searchType": "release",
    "sortBy": "relevance",
    "sortOrder": "desc",
    "startUrls": [
        "https://api.discogs.com/releases/249504",
        "https://api.discogs.com/masters/13814",
        "https://api.discogs.com/artists/72872",
        "https://api.discogs.com/labels/895",
        "https://api.discogs.com/database/search?q=nirvana&type=release",
        "https://api.discogs.com/masters/13814/versions",
        "https://api.discogs.com/artists/72872/releases",
        "https://api.discogs.com/labels/895/releases"
    ],
    "maxItems": 20,
    "fetchDetails": false,
    "priceStats": false,
    "maxPriceVersions": 5,
    "currency": "USD",
    "proxyConfiguration": {
        "useApifyProxy": true
    },
    "incrementalMode": false,
    "emitUnchanged": false,
    "emitExpired": false,
    "maxNotifyListings": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("abotapi/discogs-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",
    "searchQueries": ["nirvana nevermind"],
    "searchType": "release",
    "sortBy": "relevance",
    "sortOrder": "desc",
    "startUrls": [
        "https://api.discogs.com/releases/249504",
        "https://api.discogs.com/masters/13814",
        "https://api.discogs.com/artists/72872",
        "https://api.discogs.com/labels/895",
        "https://api.discogs.com/database/search?q=nirvana&type=release",
        "https://api.discogs.com/masters/13814/versions",
        "https://api.discogs.com/artists/72872/releases",
        "https://api.discogs.com/labels/895/releases",
    ],
    "maxItems": 20,
    "fetchDetails": False,
    "priceStats": False,
    "maxPriceVersions": 5,
    "currency": "USD",
    "proxyConfiguration": { "useApifyProxy": True },
    "incrementalMode": False,
    "emitUnchanged": False,
    "emitExpired": False,
    "maxNotifyListings": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("abotapi/discogs-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",
  "searchQueries": [
    "nirvana nevermind"
  ],
  "searchType": "release",
  "sortBy": "relevance",
  "sortOrder": "desc",
  "startUrls": [
    "https://api.discogs.com/releases/249504",
    "https://api.discogs.com/masters/13814",
    "https://api.discogs.com/artists/72872",
    "https://api.discogs.com/labels/895",
    "https://api.discogs.com/database/search?q=nirvana&type=release",
    "https://api.discogs.com/masters/13814/versions",
    "https://api.discogs.com/artists/72872/releases",
    "https://api.discogs.com/labels/895/releases"
  ],
  "maxItems": 20,
  "fetchDetails": false,
  "priceStats": false,
  "maxPriceVersions": 5,
  "currency": "USD",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "incrementalMode": false,
  "emitUnchanged": false,
  "emitExpired": false,
  "maxNotifyListings": 50
}' |
apify call abotapi/discogs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,abotapi/discogs-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/Ru9FlpwTLY84PoxwK/builds/UydfWxg9v65zwYdyx/openapi.json
