# Spotify Scraper - Play Counts, Monthly Listeners & Playlists (`lukehunter/spotify-scraper`) Actor

Scrape public Spotify pages for artists, albums, playlists and tracks: monthly listeners, followers, play counts, playlist track lists and album metadata. No login, no Spotify API keys.

- **URL**: https://apify.com/lukehunter/spotify-scraper.md
- **Developed by:** [Luke Hunter](https://apify.com/lukehunter) (community)
- **Categories:** Social media, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 result rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Spotify Scraper — Play Counts, Monthly Listeners & Playlists

**For music marketers, labels, managers and playlist curators who need Spotify numbers without a
Spotify developer account.** Give it artist, album, playlist or track links from open.spotify.com
and get back one row per entity — monthly listeners, followers, play counts, track lists — sourced
from Spotify's own public pages, no login and no Spotify Web API keys required.

Pay-per-result: **$0.001 per result — 1,000 results = $1.**
Try it free with Apify's monthly platform credit.

### Quick start (2 minutes)

1. Copy an artist, album, playlist or track link from open.spotify.com (or its `spotify:type:id`
   URI — right-click → Share → Copy Spotify URI).
2. Open the **Input** tab and use this prefill (swap in your own links):

```json
{
  "urls": ["https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg"],
  "maxItems": 20
}
```

3. Click **Start**. Export the resulting dataset to CSV/Excel/JSON, or pull it through the API
   shown below.

### Use cases

- **Artist managers and labels** tracking monthly listeners and follower growth over time.
- **Playlist pitching and curation** — pull a playlist's current tracklist, owner and play counts
  before pitching a placement, or to monitor a playlist you're already on.
- **Music marketers** comparing play counts across an artist's catalogue or a competitor's.
- **Researchers and journalists** building a dataset of public Spotify catalogue metadata.

### Run it weekly

1. Set your artist/playlist/album URLs, then click **Schedule** on the run page (or create one
   under **Schedules** in the Apify Console).
2. Run it weekly to build a monthly-listeners/followers/play-count history.
3. Use `uri` to match entities across runs, and compare `monthlyListeners`/`followers`/`playCount`
   to spot movement since the last run.

### Input

```json
{
  "urls": ["https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg"],
  "includeTracks": true,
  "maxTracksPerEntity": 50,
  "maxItems": 100
}
```

| Field | Type | Default | Description |
|---|---|---:|---|
| `urls` | string\[] | required | 1–200 open.spotify.com links or `spotify:type:id` URIs for artists, playlists, albums or tracks |
| `includeTracks` | boolean | `true` | For artist/playlist/album links, also emit one row per track |
| `maxTracksPerEntity` | integer | 50 | 1–500. Caps per-track rows per entity — never fetches more than one page already embeds (see "Important limitations") |
| `maxItems` | integer | 100 | 1–5000. Hard cap on total rows delivered across the whole run (entity rows + track rows) |

For most users, the only setting that matters is **URLs**.

#### Supported link formats

- A full page link: `https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg`
- A locale-prefixed link: `https://open.spotify.com/intl-de/playlist/37i9dQZF1DXcBWIGoYBM5M`
- A Spotify URI: `spotify:album:4aawyAB9vmqN3uQ7FjRGTy`

`/embed/`, `/local/` and `/download/` links (the "Embed" share option, not the normal "Share" link)
are rejected — see "Access and compliance" below.

### Spotify data fields

| Category | Fields |
|---|---|
| Identity | `type`, `id`, `uri`, `url`, `name`, `parent` |
| Credits | `artists`, `album`, `releaseDate` |
| Popularity | `monthlyListeners`, `followers`, `playCount`, `worldRank`, `popularity` |
| Track detail | `durationMs`, `explicit`, `isrc`, `position` |
| Playlist/album | `trackCount`, `owner`, `description` |
| Media | `imageUrl` |
| Freshness | `sourceUrl`, `scrapedAt` |

Missing values are returned as `null`. The Actor does not invent missing data — `worldRank`,
`popularity` and `isrc` were not found published on any page this Actor reads and are always
`null` in practice; kept in the schema for forward-compatibility.

### Spotify output example

Real output for the artist Pitbull (`https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg`,
trimmed to one artist row and one of its five top-track rows):

```json
{
  "type": "artist",
  "id": "0TnOYISbd1XYRBk9myaseg",
  "uri": "spotify:artist:0TnOYISbd1XYRBk9myaseg",
  "url": "https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg",
  "name": "Pitbull",
  "monthlyListeners": 80263692,
  "followers": 13450676,
  "description": "Armando Christian Pérez, globally known as Pitbull, invites disruption on a global scale...",
  "imageUrl": "https://i.scdn.co/image/ab6761610000e5ebe75db75543a89589514259b2",
  "scrapedAt": "2026-09-27T00:00:00.000Z"
}
```

```json
{
  "type": "track",
  "id": "3C0nOe05EIt1390bVABLyN",
  "uri": "spotify:track:3C0nOe05EIt1390bVABLyN",
  "url": "https://open.spotify.com/track/3C0nOe05EIt1390bVABLyN",
  "name": "On The Floor",
  "parent": "0TnOYISbd1XYRBk9myaseg",
  "artists": ["Jennifer Lopez", "Pitbull"],
  "album": "Love?",
  "playCount": 1270815660,
  "explicit": false,
  "position": 1,
  "sourceUrl": "https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg",
  "scrapedAt": "2026-09-27T00:00:00.000Z"
}
```

| Field | Value |
|---|---|
| `monthlyListeners` / `followers` | `80,263,692` / `13,450,676` |
| Top track / `playCount` | `On The Floor` / `1,270,815,660` |

### Use it as a Spotify API

The Actor can be called from your own application through the Apify API. No Spotify account,
login or Web API client credentials are required.

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")

run = client.actor("lukehunter/spotify-scraper").call(
    run_input={
        "urls": ["https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg"],
        "maxItems": 50,
    }
)

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["type"], row["name"], row.get("playCount"))
```

Use Apify schedules for recurring snapshots, and webhooks or integrations to send completed
datasets into the rest of your workflow.

### Pricing and cost control

This Actor uses **pay per delivered result row** pricing.

**Current configured rate: $0.001 per result.** Check the Apify **Pricing** tab for the latest
published rate.

| Results delivered | Cost at $0.001/result |
|---:|---:|
| 100 | $0.10 |
| 1,000 | $1.00 |
| 5,000 | $5.00 |

There is no charge for merely starting a run, and an entity that returns HTTP 404 (not found) is
never charged. `maxItems` gives you a clear upper bound on the number of billable rows.

### Access and compliance

- Honest, self-identifying User-Agent — no browser, no proxy, no CAPTCHA bypass, no User-Agent
  rotation.
- 1 request per 2 seconds.
- `https://open.spotify.com/robots.txt` is checked in code before every request (not just by
  convention): this Actor's User-Agent disallows exactly `/local/`, `/download/` and `/embed/`,
  and there is no code path anywhere in this Actor that builds a URL under any of those three
  paths. A pasted "Embed" link is rejected at input validation with an explanation — use the normal
  "Share" link instead.
- No Spotify Web API, no OAuth client credentials, no authenticated access of any kind — only the
  same public pages a logged-out visitor's browser would load.
- A block or empty/unparseable page is detected and reported per-URL; nothing is charged for it.

### Important limitations

- **Play counts are not on every page.** Verified against real Spotify pages: a play count is
  embedded for an artist's top tracks and for playlist tracks, but is genuinely **absent** from
  album track listings and from a standalone track page (`https://open.spotify.com/track/<id>`) —
  `playCount` is honestly `null` for those rows, not an unimplemented extraction.
- **A playlist/album/artist page only embeds part of its track list**, and this Actor never makes
  an extra request to fetch more: an artist page embeds up to 5 top tracks, a playlist page embeds
  only its first batch of tracks (its `trackCount` field can be larger than the number of track
  rows delivered), and an album page embeds all of its tracks. `maxTracksPerEntity` can only trim
  this down, never fetch further.
- **Track duration (`durationMs`) is not embedded on artist top-track rows or standalone track
  pages** — verified against real fixtures — but is present on playlist- and album-track rows.
- **`isrc`, `worldRank` and `popularity`** were not found published on any page this Actor reads;
  they are always `null` in practice and kept in the schema for forward-compatibility only.
- **Album and track rows have no `description`** — Spotify does not publish one on those page
  types (only playlists and artists do).
- Monthly listeners, followers and play counts can change after the run. `scrapedAt` records the
  observation time.
- This Actor does not use CAPTCHA bypass, proxy rotation, credential capture or impersonation. It
  identifies itself honestly and respects a 1-request-per-2-seconds pace.
- Public catalogue data only — artist biographies, playlist descriptions and a playlist owner's
  public display name (often a brand, curator or Spotify itself), not private user data.
- This is an independent tool and is **not affiliated with, endorsed by, or connected to Spotify
  AB.** Spotify is a trademark of its owner. You are responsible for using the data in accordance
  with applicable terms and laws.

### FAQ

#### Do I need a Spotify developer account or API key?

No. This Actor reads the same public pages a logged-out browser sees at open.spotify.com. No
Spotify account, login, or Web API client credentials are used or required.

#### Why is `playCount` null for some tracks?

Spotify's own pages only embed a play count for an artist's top tracks and for playlist tracks —
verified against real captured pages. Album track listings and standalone track pages do not embed
one at all, so this Actor honestly returns `null` there rather than guessing.

#### Why don't I get every track in a large playlist or artist discography?

This Actor only reads what a single page load already embeds — it makes no extra requests to
paginate further. A playlist's `trackCount` can be larger than the number of track rows you
receive; use `maxTracksPerEntity` to control how many of the embedded tracks are delivered.

#### Is it legal to scrape public Spotify pages?

This Actor only collects public catalogue data already shown on Spotify's own pages: names, play
counts, monthly listeners, playlist contents, and similar fields. It does not bypass any login,
CAPTCHA or paywall, and it honours robots.txt. It is an independent tool, not affiliated with or
endorsed by Spotify AB, and you are responsible for using the collected data in accordance with
Spotify's terms of service and the laws that apply to you.

#### How do I find a Spotify URI?

In the Spotify app or open.spotify.com, click the "..." menu on an artist, album, playlist or
track, then **Share → Copy Spotify URI** (or **Copy link to song/album/playlist/artist** for the
regular web link).

### Related Actors

Other data tools from the same developer, built to the same standard: official or public sources, hard cost caps, and honest documentation of limits.

- **[Google Play App Scraper](https://apify.com/lukehunter/google-play-scraper)**: ratings, installs, developer contact info and pricing for any Google Play app.
- **[Apple App Store Reviews Scraper](https://apify.com/lukehunter/app-store-reviews-scraper)**: Apple App Store reviews for any iOS app, across countries, with rating, version and date.
- **[Walmart Category Scraper](https://apify.com/lukehunter/walmart-category-scraper)**: product names, prices, was-prices and ratings from Walmart category pages.
- **[Shopify Store Products Scraper](https://apify.com/lukehunter/shopify-store-products-scraper)**: full product catalogues from any Shopify store, with prices, sale prices, variants and stock.
- **[Vinted Scraper](https://apify.com/lukehunter/vinted-scraper)**: Vinted search results with prices, brands, sizes and favourites, across any Vinted country.
- **[AliExpress Search Scraper](https://apify.com/lukehunter/aliexpress-scraper)**: AliExpress search results with prices, discounts, ratings and sold counts, by keyword.
- **[Hospital Price Transparency Enforcement Leads](https://apify.com/lukehunter/hospital-price-transparency-enforcement-leads)**: hospitals with recent CMS price transparency warning notices, CAP requests and CMP notices.
- **[Hospital Ownership Change Radar](https://apify.com/lukehunter/hospital-chow-radar)**: hospitals that just changed owner, with buyer, seller and effective date from CMS filings.

# Actor input Schema

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

open.spotify.com links or spotify:type:id URIs for artists, playlists, albums or tracks (e.g. https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg or spotify:artist:0TnOYISbd1XYRBk9myaseg). Maximum 200 per run.

## `includeTracks` (type: `boolean`):

For artist, playlist and album URLs, also emit one row per track (with play count, when Spotify's page shows one) in addition to the entity's own row. Standalone track URLs are unaffected — they always produce one row. Default true.

## `maxTracksPerEntity` (type: `integer`):

Cap on per-track rows emitted for each artist/playlist/album, 1-500, default 50. This Actor never makes extra requests to fetch more tracks than a single page already embeds (an artist's page embeds up to 5 top tracks; a playlist page embeds its first batch of tracks; an album page embeds all of its tracks) — this only trims down, it never fetches further.

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

Hard cap on the total number of rows delivered across the whole run (entity rows + track rows combined), 1-5000, default 100. You are charged per delivered row, so this is your cost cap.

## Actor input object example

```json
{
  "urls": [
    "https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg"
  ],
  "includeTracks": true,
  "maxTracksPerEntity": 50,
  "maxItems": 20
}
```

# Actor output Schema

## `results` (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 = {
    "urls": [
        "https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg"
    ],
    "maxTracksPerEntity": 50,
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("lukehunter/spotify-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 = {
    "urls": ["https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg"],
    "maxTracksPerEntity": 50,
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("lukehunter/spotify-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 '{
  "urls": [
    "https://open.spotify.com/artist/0TnOYISbd1XYRBk9myaseg"
  ],
  "maxTracksPerEntity": 50,
  "maxItems": 20
}' |
apify call lukehunter/spotify-scraper --silent --output-dataset

```

## MCP server setup

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