Spotify GraphQL Connector for Podcast Data
Project description
Spotify GraphQL Connector
An unofficial Python connector for the Spotify Creators GraphQL API - the backend that powers the Spotify Creators dashboard (formerly Anchor).
Disclaimer: This connector uses an undocumented, internal API that may change at any time without notice. Use at your own risk.
Does it work for Anchor? For Spotify?
**Anchor (primary use case): Supported **
Anchor was rebranded to Spotify for Podcasters and then Spotify Creators. The dashboard
at creators.spotify.com is now the only interface for managing Anchor-hosted podcasts.
This connector talks directly to the GraphQL API behind that dashboard - it is the
direct replacement for the anchor-connector, which stopped working when Spotify migrated its backend to GraphQL.
Shows hosted on Anchor/Spotify for Podcasters appear as hostingProvider: "S4P" and
have full analytics access across all endpoints.
Non-Anchor Spotify shows: Partial Support
Shows hosted elsewhere (Apple, Megaphone, RSS feeds, etc.) appear as NonHostedShow
with hostingProvider: "OTHER_THIRD_PARTY" or "MEGAPHONE". These shows are visible
in the dashboard and return data from some endpoints (metadata, impressions, geo stats),
but deep analytics like performance curves and consumption data are only available for
S4P-hosted shows.
In short: if you were using anchor-connector, replace it with this connector.
Features
- Single credential - only
sp_dcandsp_keysession cookies are required. Show URI and station ID are resolved automatically from your account. - Auto-authentication - performs the full PKCE OAuth 2.0 flow used by the Spotify Creators web app and transparently refreshes the bearer token before expiry.
- Auto-pagination -
get_all_episodes()fetches every page automatically. - Retry logic - exponential back-off on transient errors (429, 502, 503, 504) and automatic token refresh on 401.
- Type-safe - fully annotated with a recursive
JsonDicttype alias; zeroAny. - Native response shape - responses are returned exactly as the API sends them so consumers can adapt to the real data without a lossy mapping layer.
uv-powered - dependency management and packaging via uv.- 24 operations covering the full Spotify Creators analytics surface.
Requirements
- Python 3.11+
- uv
Installation
# As a library in your own project
uv add spotifygraphqlconnector
# Clone for development
git clone https://github.com/openpodcast/spotify-connector-graphql
cd spotify-connector-graphql/spotifygraphqlconnector
uv sync --all-groups
Credentials
You need two cookies from an active Spotify session: sp_dc and sp_key.
How to obtain them
- Open https://creators.spotify.com in your browser and log in.
- Open DevTools (
F12/⌘ ⌥ I) → Application → Cookies →https://accounts.spotify.com. - Copy the values of the
sp_dcandsp_keycookies.
These cookies are typically valid for several months. When they expire you will see a
CredentialsExpired exception - just grab fresh cookie values from your browser.
Configuration
All configuration is via environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
SPOTIFY_SP_DC |
Yes | - | sp_dc cookie from a Spotify session |
SPOTIFY_SP_KEY |
Yes | - | sp_key cookie from a Spotify session |
SPOTIFY_SHOW_URI |
No | auto | Spotify show URI, e.g. spotify:show:1HaFboRBVORs2VCpNACYLn. Auto-resolved from the first S4P-hosted show on your account. |
SPOTIFY_STATION_ID |
No | auto | Numeric Anchor/Spotify station ID. Auto-resolved from show metadata. |
SPOTIFY_EPISODE_URI |
No | auto | Episode URI for per-episode analytics in the CLI. Auto-resolved from the first episode. |
SPOTIFY_COUNTRY_CODE |
No | US |
ISO 3166-1 alpha-2 country code for the registration endpoint. |
cp .env.sample .env
# edit .env and fill in SPOTIFY_SP_DC and SPOTIFY_SP_KEY
source .env
Usage
CLI
source .env && uv run spotifygraphqlconnector
Runs every supported endpoint in sequence and logs the full JSON responses via loguru.
Library
from spotifygraphqlconnector import SpotifyGraphQLConnector
connector = SpotifyGraphQLConnector(
sp_dc="your_sp_dc_value",
sp_key="your_sp_key_value",
# Optional - resolved automatically when omitted:
# show_uri="spotify:show:1HaFboRBVORs2VCpNACYLn",
# station_id="6248789",
)
# --- User / account ---
user = connector.get_user_metadata()
shows = connector.get_user_shows() # basic: uri, name, stationId
shows_rich = connector.get_shows_for_user() # + authorName, category, description
user_shows = connector.get_user_and_shows()
reg = connector.get_user_registration(country_code="DE")
# --- Show metadata (show_uri auto-resolved) ---
show_type = connector.get_show_type()
overview = connector.get_show_overview_stats()
clips = connector.get_show_clips()
# --- Show analytics ---
plays = connector.get_show_spotify_stats(date_range_window="WINDOW_LAST_THIRTY_DAYS")
geo = connector.get_show_geo_stats() # country breakdown
geo_city = connector.get_show_geo_stats(result_geo="GEO_CITY")
demo = connector.get_show_demographics_stats() # age + gender
platform = connector.get_show_platform_stats() # app + device
impressions = connector.get_show_impressions_trend() # daily + total
sources = connector.get_show_impressions_sources() # by source
discovery = connector.get_show_audience_discovery() # funnel + audience size
top_ep = connector.get_show_top_episodes() # all-time plays per episode
# --- Episode list (station_id auto-resolved) ---
page = connector.get_episode_list(current_page=1, page_size=25)
all_eps = connector.get_all_episodes() # auto-paginates
# --- Episode analytics ---
ep_uri = "spotify:episode:4fndadZdKayBwmsRQJ8rNR"
meta = connector.get_episode_metadata_for_analytics(ep_uri)
perf = connector.get_episode_performance_all_time(ep_uri)
streams = connector.get_episode_streams_and_downloads(ep_uri)
plays_daily = connector.get_episode_plays_daily(ep_uri)
impressions = connector.get_episode_impressions_faceted(ep_uri)
consumption = connector.get_episode_consumption_all_time(ep_uri)
audience = connector.get_episode_audience_size_all_time(ep_uri)
All methods return dict[str, JsonValue] in the native Spotify API response shape.
Supported Endpoints (24)
User / Account
| Method | Operation | Description |
|---|---|---|
get_user_metadata() |
getUserMetadata |
Authenticated user name and avatar |
get_user_shows() |
WebGetUserShows |
All shows: uri, name, stationId, permissions |
get_shows_for_user() |
getShowsForUser |
All shows with rich metadata: authorName, category, description, hostingProvider |
get_user_and_shows() |
WebGetUserAndShows |
User profile + shows combined |
get_user_registration() |
WebGetUserRegistration |
TOS / onboarding state |
get_external_partner_id() |
WebGetExternalPartnerId |
External partner ID (e.g. for mParticle/Braze) |
Show Metadata
| Method | Operation | Description |
|---|---|---|
get_show_type() |
WebGetShowTypeByUri |
Show type: PODCAST, AUDIOBOOK, ... |
get_show_overview_stats() |
getShowOverviewStatsNRT |
Near-real-time aggregate streams/downloads total |
get_show_clips() |
getShowClips |
Short-form video clips for a show |
get_monetization_lifecycle_state() |
WebGetMonetizationLifecycleState |
Monetisation state (S4P shows only) |
Show Analytics
All analytics methods accept date_range_window:
"WINDOW_LAST_SEVEN_DAYS" · "WINDOW_LAST_THIRTY_DAYS" · "WINDOW_LAST_NINETY_DAYS" · "WINDOW_ALL_TIME"
| Method | Operation | Replaces (anchor-connector) |
|---|---|---|
get_show_spotify_stats() |
getShowOnSpotifyStats |
plays(), total_plays() |
get_show_geo_stats() |
getShowAudienceAllPlatformsGeoStats |
plays_by_geo(), plays_by_geo_city() |
get_show_demographics_stats() |
getShowAudienceDemographicsStats |
plays_by_age_range(), plays_by_gender() |
get_show_platform_stats() |
getShowAudienceAllPlatformsStats |
plays_by_app(), plays_by_device() |
get_show_impressions_trend() |
getShowImpressionsTrendStats |
impressions() |
get_show_impressions_sources() |
getShowImpressionsSourcesStats |
n/a |
get_show_audience_discovery() |
getShowAudienceDiscoveryStats |
audience_size(), unique_listeners() |
get_show_top_episodes() |
getShowTopEpisodes |
total_plays_by_episode() ¹ |
¹ All-time only - Spotify does not expose a time-range parameter for this endpoint.
Per-episode plays within a window: call get_episode_plays_daily(episode_uri) per episode.
Episode List
| Method | Operation | Description |
|---|---|---|
get_episode_list() |
WebGetIndexedEpisodeList |
Paginated, searchable episode list with sort/filter |
get_all_episodes() |
WebGetIndexedEpisodeList |
All episodes auto-paginated |
Episode Analytics
| Method | Operation | Replaces (anchor-connector) |
|---|---|---|
get_episode_metadata_for_analytics() |
getEpisodeMetadataForAnalytics |
episode_metadata() |
get_episode_performance_all_time() |
getEpisodePerformanceAllTime |
episode_performance(), episode_aggregated_performance() |
get_episode_streams_and_downloads() |
getEpisodeStreamsAndDownloads |
episode_plays() |
get_episode_plays_daily() |
getEpisodePlaysDaily |
episode_plays() |
get_episode_impressions_faceted() |
getEpisodeImpressionsFaceted |
n/a |
get_episode_consumption_all_time() |
getEpisodeConsumptionAllTime |
n/a |
get_episode_audience_size_all_time() |
getEpisodeAudienceSizeAllTime |
audience_size() (episode level) |
Error Handling
from spotifygraphqlconnector import (
SpotifyGraphQLConnector,
CredentialsExpired,
AuthenticationError,
MaxRetriesException,
)
try:
data = connector.get_show_spotify_stats()
except CredentialsExpired:
# sp_dc / sp_key have expired - get fresh values from your browser
print("Please update SPOTIFY_SP_DC and SPOTIFY_SP_KEY.")
except AuthenticationError as e:
# Unexpected auth page structure - Spotify may have changed their flow
print(f"Auth error: {e}")
except MaxRetriesException as e:
# All retry attempts exhausted (network issues, persistent 5xx)
print(f"Request failed after all retries: {e}")
Development
make install # uv sync --all-groups
make check # lint + typecheck + test (all at once)
make lint # ruff check
make typecheck # pyright
make test # pytest
make fmt # ruff format + ruff check --fix
make run # run the CLI (requires .env sourced)
Adding a new operation
- Find the
operationNameandsha256Hashin your browser DevTools: DevTools → Network → filter bygraph-pq→ click a request → Payload tab. - Add the entry to
OPERATION_HASHESintypes.py. - Add a method in
connector.pycallingself._query("YourOperationName", ...). - Add a call in
__main__.pyand a test intests/test_connector.py.
Relationship to other connectors
This connector is part of the OpenPodcast project:
| Connector | API | Auth | Status |
|---|---|---|---|
| spotify-connector | Spotify REST (generic.wg.spotify.com) |
PKCE OAuth (sp_dc + sp_key) |
Active |
| anchor-connector | Anchor REST (podcasters.spotify.com) |
Session cookie (anchorpw_s) |
⚠️ Deprecated - Spotify migrated to GraphQL |
| spotify-connector-graphql (this) | Spotify Creators GraphQL (creators-graph.spotify.com) |
PKCE OAuth (sp_dc + sp_key) |
✅ Active - Anchor replacement |
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spotifygraphqlconnector-0.4.0.tar.gz.
File metadata
- Download URL: spotifygraphqlconnector-0.4.0.tar.gz
- Upload date:
- Size: 23.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b50591fca638e4180eefb8a74c44e82f0a128575a8fcbbd772d15c2af5c5b26
|
|
| MD5 |
d162653e7e93eb33c2c9adb965d46a71
|
|
| BLAKE2b-256 |
054f0ab844b236f5849234822b399e7a54448002c7f6a054b3ea67b1dc5d2fd4
|
File details
Details for the file spotifygraphqlconnector-0.4.0-py3-none-any.whl.
File metadata
- Download URL: spotifygraphqlconnector-0.4.0-py3-none-any.whl
- Upload date:
- Size: 26.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.4 {"installer":{"name":"uv","version":"0.11.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d80d6b1e5d95f76c300c7a14d61594b0c5514837e8d79a6ad775558a2d335cd4
|
|
| MD5 |
908cc47b8b641d2d972a9bc31419a9d0
|
|
| BLAKE2b-256 |
8990f7207be14434f0e54edf352c22ec8d7557f87368ecc4be219d539af458ce
|