
Chartmetric
- 6 installs
- Updated March 24, 2026
- recoupable/chartmetric
Helps with ai & agent building tasks.
About
chartmetric is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- chartmetric
- AI & Agent Building
- AI-coding skill
Chartmetric by the numbers
- 6 all-time installs (skills.sh)
- Ranked #12,825 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/recoupable/chartmetric --skill chartmetricAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 6 |
|---|---|
| Last updated | March 24, 2026 |
| Repository | recoupable/chartmetric ↗ |
What it does
Helps with ai & agent building tasks.
Files
Chartmetric API
Music industry analytics via Python scripts. Get streaming metrics, playlist placements, audience data, and competitive insights.
Setup
With Python (recommended)
cd .recoup/skills/chartmetric
python3 -m venv .venv && source .venv/bin/activate
pip install requests
export CHARTMETRIC_REFRESH_TOKEN="your_token"Without Python (curl fallback)
If Python is unavailable, see references/curl-fallback.md for curl-only patterns. Only requires curl (universal).
Quick Start
# 1. Search for an artist
python scripts/search_artist.py "Drake"
# Returns: Chartmetric ID 3380
# 2. Get profile and stats
python scripts/get_artist.py 3380
python scripts/get_artist_metrics.py 3380 --source spotify
# 3. Get where fans listen
python scripts/get_artist_cities.py 3380---
Critical Gotchas
These are the API traps that cause 401/400 errors. Memorize them:
| Gotcha | Wrong | Correct |
|---|---|---|
| YouTube metrics | --source youtube | --source youtube_channel or youtube_artist |
| Artist playlists | /artist/:id/playlists | /artist/:id/:platform/current/playlists |
| Similar artists | /artist/:id/similar | /artist/:id/relatedartists or similar-artists/by-configurations |
| TikTok charts | With country_code | No country_code parameter |
| Amazon charts | With country_code | No country_code - use genre + type |
| Curator search | By name | By numeric Chartmetric ID only |
---
Scripts Reference
Artist Discovery
# Find US artists with 100K-500K Spotify monthly listeners
python scripts/discover_artists.py --country US --spotify-listeners 100000 500000
# TikTok-famous but weak on Spotify
python scripts/discover_artists.py --tiktok-followers 1000000 10000000 --spotify-listeners 0 100000
# Emerging artists in a genre, sorted by weekly growth
python scripts/discover_artists.py --genre 86 --spotify-listeners 50000 200000 --sort weekly_diff.sp_monthly_listeners
# Solo female artists in Brazil
python scripts/discover_artists.py --country BR --band false --pronoun she/her
# Festival performers with low Spotify (undervalued)
python scripts/discover_artists.py --festival-id 123 --spotify-followers 0 50000Available filters: --country, --genre, --band, --pronoun, --spotify-listeners, --spotify-followers, --tiktok-followers, --instagram-followers, --youtube-subscribers, --cpp, --festival-id
Sort columns: latest.sp_monthly_listeners, weekly_diff.sp_monthly_listeners, monthly_diff.tt_followers, etc.
Search & Lookup
python scripts/search_artist.py "Taylor Swift" # Search artists by name
python scripts/get_artist.py 2762 # Get profile by CM ID
python scripts/get_artist_by_spotify.py 3TVXtAsR... # Lookup by Spotify ID/URL
python scripts/get_track.py 128613854 # Get track metadata
python scripts/get_track_by_spotify.py 0VjIjW4G... # Lookup track by SpotifyArtist Data
python scripts/get_artist_albums.py 3380 # All albums
python scripts/get_artist_tracks.py 3380 # All tracks
python scripts/get_artist_cities.py 3380 # Top cities by listeners
python scripts/get_artist_urls.py 3380 # Social/streaming URLs
python scripts/get_artist_insights.py 3380 # AI-generated insights
python scripts/get_artist_career.py 3380 # Career timelinePlatform Metrics
python scripts/get_artist_metrics.py 3380 --source spotify
python scripts/get_artist_metrics.py 3380 --source instagram
python scripts/get_artist_metrics.py 3380 --source youtube_channelValid sources (14 total): spotify, instagram, tiktok, twitter, facebook, youtube_channel, youtube_artist, soundcloud, deezer, twitch, line, melon, wikipedia, bandsintown
Audience Demographics
python scripts/get_artist_audience.py 3380 # Instagram (default)
python scripts/get_artist_audience.py 3380 --platform tiktok # TikTok
python scripts/get_artist_audience.py 3380 --platform youtube # YouTube
python scripts/get_artist_instagram_posts.py 3380 # Top IG posts/reelsPlaylist Placements
# Basic - current Spotify playlists
python scripts/get_artist_playlists.py 3380
# Other platforms
python scripts/get_artist_playlists.py 3380 --platform applemusic
python scripts/get_artist_playlists.py 3380 --platform deezer
# Past placements
python scripts/get_artist_playlists.py 3380 --status past
# With filters
python scripts/get_artist_playlists.py 3380 --editorial --newMusicFriday
python scripts/get_artist_playlists.py 3380 --indie --majorCurator
# With date range and sorting
python scripts/get_artist_playlists.py 3380 --since 2025-01-01 --sort followers --limit 100Platforms: spotify, applemusic, deezer, amazon, youtube
Spotify filters: --editorial, --personalized, --chart, --thisIs, --newMusicFriday, --radio, --indie, --majorCurator, --popularIndie, --brand
Similar/Related Artists
# Basic related artists
python scripts/get_similar_artists.py 3380 --limit 10
# Advanced with configuration filters
python scripts/get_similar_artists.py 3380 --by-config --audience high --genre high
python scripts/get_similar_artists.py 3380 --by-config --mood medium --musicality highConfig options: --audience, --mood, --genre, --musicality (values: high, medium, low)
Playlists & Curators
python scripts/get_playlist.py spotify 37i9dQZF1DXcBWIGoYBM5M # Playlist metadata
python scripts/get_curator.py 1 # Curator info (numeric ID)Discovery
python scripts/list_genres.py # All Chartmetric genres
python scripts/list_festivals.py # Music festivals---
Workflow Chains
Research an Artist for Playlist Pitching
# 1. Find the artist
python scripts/search_artist.py "Phoebe Bridgers"
# ID: 241089
# 2. Get their current playlist placements
python scripts/get_artist_playlists.py 241089 --editorial --limit 50
# 3. Find similar artists who might share playlists
python scripts/get_similar_artists.py 241089 --by-config --genre high --audience high
# 4. Check where their fans are
python scripts/get_artist_cities.py 241089
python scripts/get_artist_audience.py 241089 --platform instagramCompetitive Analysis
# 1. Get base artist
python scripts/get_artist.py 3380
# 2. Get similar artists with metrics
python scripts/get_similar_artists.py 3380 --by-config --audience high --limit 25
# 3. Compare streaming growth
python scripts/get_artist_metrics.py 3380 --source spotify
python scripts/get_artist_metrics.py <competitor_id> --source spotify
# 4. Compare playlist reach
python scripts/get_artist_playlists.py 3380 --sort followers
python scripts/get_artist_playlists.py <competitor_id> --sort followersFrom Spotify URL to Full Profile
# 1. Convert Spotify URL to Chartmetric ID
python scripts/get_artist_by_spotify.py "https://open.spotify.com/artist/3TVXtAsR1Inumwj472S9r4"
# Returns: cm_artist: 1320
# 2. Get everything
python scripts/get_artist.py 1320
python scripts/get_artist_metrics.py 1320 --source spotify
python scripts/get_artist_cities.py 1320
python scripts/get_artist_playlists.py 1320
python scripts/get_artist_audience.py 1320More Advanced Workflows
See references/advanced-workflows.md for 10 strategic workflow chains including:
- Playlist pitching intelligence
- TikTok-to-Spotify pipeline analysis
- A&R discovery workflow
- Collaboration finder
- Viral song autopsy
---
References
| File | When to Use |
|---|---|
references/parameter-guide.md | Detailed endpoint parameters and gotchas |
references/endpoints.md | All 120 endpoints with status |
references/curl-fallback.md | When Python unavailable - curl patterns for all endpoints |
references/advanced-workflows.md | Strategic insights - 10 multi-step workflow chains |
---
Rate Limits
Chartmetric has rate limits. If you get 429 errors, wait 60 seconds before retrying.
---
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 Unauthorized | Token expired or internal endpoint | Refresh token; check if endpoint is public |
| 402 Payment Required | Subscription expired | Check Chartmetric account |
| 404 Not Found | Invalid ID | Verify Chartmetric ID (not Spotify ID) |
| 400 Bad Request | Wrong parameters | Check parameter-guide.md for correct params |
| "Domain does not exist" | Invalid stat source | Use exact source names (e.g., youtube_channel not youtube) |
Chartmetric
A skill for AI agents to access music industry analytics via the Chartmetric API.
Install
npx skills add recoupable/chartmetricWhat It Does
Query streaming metrics, playlist placements, audience demographics, and competitive insights for any artist, track, or album.
Key Capabilities
- Artist Discovery - Filter artists by Spotify listeners, TikTok followers, geography, genre
- Platform Metrics - Track performance across 14 platforms (Spotify, Instagram, TikTok, YouTube, etc.)
- Playlist Intelligence - Current and historical playlist placements with curator data
- Audience Demographics - Geographic and demographic breakdowns
- Similar Artists - Find related artists by audience, genre, mood, or musicality
- Catalog Analysis - Albums, tracks, career timeline
Quick Start
# Search for an artist
python scripts/search_artist.py "Drake"
# Get artist profile
python scripts/get_artist.py 3380
# Discover artists by metrics
python scripts/discover_artists.py --country US --spotify-listeners 100000 500000Requirements
- Chartmetric API subscription
- Python 3.8+ with
requestspackage - Set
CHARTMETRIC_REFRESH_TOKENenvironment variable
Documentation
See SKILL.md for complete usage documentation, all available scripts, and workflow examples.
Curl Fallback
If Python is unavailable, see references/curl-fallback.md for curl-only patterns.
License
MIT
Advanced Workflow Chains
Multi-step workflows that chain Chartmetric endpoints to unlock powerful insights. Each workflow answers a strategic question.
---
1. Playlist Pitching Intelligence
Question: "Which playlist curators should I pitch to?"
# 1. Find similar artists who are slightly bigger (good benchmarks)
python scripts/get_similar_artists.py 241089 --by-config --audience high --genre high --limit 50
# 2. For each similar artist, get their playlist placements
python scripts/get_artist_playlists.py <similar_artist_id> --editorial --indie --limit 100
# 3. Look for playlist overlap - curators who added multiple similar artists
# These curators are most likely to add your artist
# 4. For promising playlists, get curator details
python scripts/get_playlist.py spotify <playlist_id>
python scripts/get_curator.py <curator_id>
# 5. Check if target artist was ever on these playlists
python scripts/get_artist_playlists.py 241089 --status pastOutput: List of curators who already playlist similar artists but haven't added yours yet.
---
2. TikTok-to-Spotify Pipeline Analysis
Question: "Is TikTok virality translating to Spotify growth?"
# 1. Get TikTok metrics over time
python scripts/get_artist_metrics.py 3380 --source tiktok
# 2. Get Spotify metrics over time (same period)
python scripts/get_artist_metrics.py 3380 --source spotify
# 3. Get TikTok audience demographics
python scripts/get_artist_audience.py 3380 --platform tiktok
# 4. Get Spotify listener cities
python scripts/get_artist_cities.py 3380
# 5. Compare: Are TikTok audience countries matching Spotify growth cities?
# 6. Get top Instagram posts (often cross-posted from TikTok)
python scripts/get_artist_instagram_posts.py 3380Output: Correlation between TikTok spikes and Spotify follower/listener growth. Identify if there's a geographic mismatch (TikTok viral in Brazil but Spotify listeners in US = opportunity).
---
3. Tour Routing Intelligence
Question: "Where should this artist tour next?"
# 1. Get top listener cities
python scripts/get_artist_cities.py 3380
# 2. Get festivals in those regions
python scripts/list_festivals.py
# Filter by country/region from cities
# 3. For each city, find which similar artists are touring there
python scripts/get_similar_artists.py 3380 --by-config --audience high --limit 20
# Check their venues/events (if available)
# 4. Get YouTube audience by region
python scripts/get_artist_audience.py 3380 --platform youtube
# 5. Compare playlist reach by country
python scripts/get_artist_playlists.py 3380 --sort followers
# Group by playlist country codeOutput: Ranked list of cities by streaming engagement, cross-referenced with festival opportunities and market coverage.
---
4. A&R Discovery Workflow
Question: "Find emerging artists in [genre] before they blow up"
# 1. Start with a breakout artist in the genre as anchor
python scripts/search_artist.py "Ice Spice"
# ID: 10574889
# 2. Find artists similar by musicality (not audience - we want undiscovered)
python scripts/get_similar_artists.py 10574889 --by-config --musicality high --genre high --limit 50
# 3. Filter response by career_stage: "undiscovered" or "developing"
# 4. For promising candidates, check their trajectory
python scripts/get_artist_metrics.py <candidate_id> --source spotify
python scripts/get_artist_metrics.py <candidate_id> --source tiktok
# 5. Check playlist traction (editorial = label interest)
python scripts/get_artist_playlists.py <candidate_id> --editorial
# 6. Get their insights for AI-generated summary
python scripts/get_artist_insights.py <candidate_id>Output: List of emerging artists with similar sound but smaller audience, sorted by growth velocity.
---
5. Catalog Optimization
Question: "Which songs should we push and where?"
# 1. Get all artist tracks
python scripts/get_artist_tracks.py 3380
# 2. For each track, check playlist placements
python scripts/get_artist_playlists.py 3380
# Filter by track name to see which songs are playlisted
# 3. Check which tracks are performing on TikTok
# (from artist top tracks TikTok endpoint)
curl -s "https://api.chartmetric.com/api/artist/3380/top-tracks/tiktok" \
-H "Authorization: Bearer $TOKEN"
# 4. Get album performance comparison
python scripts/get_artist_albums.py 3380
# Compare album release dates to metric spikes
# 5. Identify underperforming gems:
# - High playlist reach but low streams = discovery issue
# - Low playlist but high TikTok = pitch opportunity
# - Old songs suddenly playlisted = catalog momentumOutput: Track-by-track analysis showing which songs to push on which platforms.
---
6. Competitive Roster Analysis
Question: "How does our roster compare to competitor label?"
# For each artist on your roster:
# 1. Get their profile and current metrics
python scripts/get_artist.py <artist_id>
python scripts/get_artist_metrics.py <artist_id> --source spotify
# 2. Find their similar artists (potential competitor roster)
python scripts/get_similar_artists.py <artist_id> --by-config --audience high --genre high
# 3. Compare playlist reach
python scripts/get_artist_playlists.py <artist_id> --sort followers
python scripts/get_artist_playlists.py <competitor_artist_id> --sort followers
# 4. Compare audience demographics
python scripts/get_artist_audience.py <artist_id> --platform instagram
python scripts/get_artist_audience.py <competitor_artist_id> --platform instagram
# 5. Compare where fans listen
python scripts/get_artist_cities.py <artist_id>
python scripts/get_artist_cities.py <competitor_artist_id>Output: Side-by-side comparison of roster performance, identifying gaps and opportunities.
---
7. Viral Song Autopsy
Question: "Why did this song go viral? Can we replicate it?"
# 1. Get track details
python scripts/get_track_by_spotify.py "https://open.spotify.com/track/..."
python scripts/get_track.py <track_cm_id>
# 2. Get the artist's metrics around release date
python scripts/get_artist_metrics.py <artist_id> --source spotify
python scripts/get_artist_metrics.py <artist_id> --source tiktok
# 3. Check playlist placements timeline
python scripts/get_artist_playlists.py <artist_id> --since 2025-01-01 --sort added_at
# 4. Get artist insights (may mention the viral moment)
python scripts/get_artist_insights.py <artist_id>
# 5. Find which playlists were most impactful
python scripts/get_artist_playlists.py <artist_id> --editorial --sort followers
# 6. Check if similar artists had similar trajectory
python scripts/get_similar_artists.py <artist_id> --by-config --musicality highOutput: Timeline of the viral moment: What platform first, which playlists amplified, audience demographics that drove sharing.
---
8. Market Expansion Scouting
Question: "Which new markets should we focus on?"
# 1. Current listener geography
python scripts/get_artist_cities.py 3380
# 2. Platform-specific audience breakdown
python scripts/get_artist_audience.py 3380 --platform instagram
python scripts/get_artist_audience.py 3380 --platform youtube
python scripts/get_artist_audience.py 3380 --platform tiktok
# 3. Find similar artists and their top cities
python scripts/get_similar_artists.py 3380 --by-config --genre high --limit 10
# For each:
python scripts/get_artist_cities.py <similar_id>
# 4. Look for cities where similar artists thrive but target artist is weak
# These are expansion opportunities
# 5. Check playlist coverage in target markets
python scripts/get_artist_playlists.py 3380
# Filter by playlist country codesOutput: Ranked list of underserved markets where similar artists succeed.
---
9. Collaboration Finder
Question: "Which artists should we collaborate with?"
# 1. Get similar artists by audience (shared fanbase)
python scripts/get_similar_artists.py 3380 --by-config --audience high --limit 30
# 2. Filter by career stage (slightly bigger = good collab target)
# Look for "mid-level" or "mainstream" in response
# 3. Check genre overlap
python scripts/get_similar_artists.py 3380 --by-config --genre high --musicality high
# 4. Find overlap in playlist placements
python scripts/get_artist_playlists.py 3380 --editorial
python scripts/get_artist_playlists.py <potential_collab_id> --editorial
# Same playlists = easy collab pitch
# 5. Check geographic overlap
python scripts/get_artist_cities.py 3380
python scripts/get_artist_cities.py <potential_collab_id>Output: Ranked collaboration targets by audience overlap, career stage, and playlist synergy.
---
10. Release Strategy Timing
Question: "When should we release, and how should we roll it out?"
# 1. Analyze past releases
python scripts/get_artist_albums.py 3380
python scripts/get_artist_career.py 3380
# 2. Check what worked - playlist adds after releases
python scripts/get_artist_playlists.py 3380 --status past --since 2024-01-01
# 3. Look at similar artists' successful releases
python scripts/get_similar_artists.py 3380 --by-config --audience high --limit 10
python scripts/get_artist_albums.py <similar_id>
python scripts/get_artist_career.py <similar_id>
# 4. Check current playlist momentum
python scripts/get_artist_playlists.py 3380 --editorial --newMusicFriday
# 5. Identify which platforms are hottest right now
python scripts/get_artist_metrics.py 3380 --source spotify
python scripts/get_artist_metrics.py 3380 --source tiktok
python scripts/get_artist_metrics.py 3380 --source youtube_channelOutput: Release timing recommendation based on historical patterns, playlist cycles, and platform momentum.
---
Workflow Tips
1. Cache tokens - Multiple calls need valid auth 2. Rate limit awareness - Add 1s delay between calls if running many 3. Save intermediate results - Pipe to files for analysis: python scripts/... > results.json 4. Cross-reference IDs - Always use Chartmetric IDs, not Spotify IDs for API calls 5. Compare timeframes - Most insights come from comparing metrics over time
---
Building Your Own Workflows
The power is in combining:
| Data Type | Endpoint | Use For |
|---|---|---|
| Who | similar-artists, relatedartists | Finding benchmarks, competitors, collaborators |
| Where | cities, audience | Geographic strategy, tour routing |
| What | playlists, tracks, albums | Content strategy, playlist pitching |
| When | metrics, career | Timing, trajectory analysis |
| Why | insights | AI-generated context |
Chartmetric API - Curl Fallback
Use these patterns when Python is unavailable. Requires curl (universal) and optionally jq (for pretty output).
---
Token Management
Get Token (with jq)
export CHARTMETRIC_TOKEN=$(curl -s -X POST "https://api.chartmetric.com/api/token" \
-H "Content-Type: application/json" \
-d "{\"refreshtoken\":\"$CHARTMETRIC_REFRESH_TOKEN\"}" | jq -r '.token')Get Token (without jq)
export CHARTMETRIC_TOKEN=$(curl -s -X POST "https://api.chartmetric.com/api/token" \
-H "Content-Type: application/json" \
-d "{\"refreshtoken\":\"$CHARTMETRIC_REFRESH_TOKEN\"}" | grep -o '"token":"[^"]*"' | cut -d'"' -f4)Token Caching (optional)
# Cache token to file (expires in ~1 hour)
TOKEN_FILE="/tmp/chartmetric_token"
get_token() {
if [ -f "$TOKEN_FILE" ] && [ $(($(date +%s) - $(stat -f %m "$TOKEN_FILE" 2>/dev/null || stat -c %Y "$TOKEN_FILE"))) -lt 3500 ]; then
cat "$TOKEN_FILE"
else
TOKEN=$(curl -s -X POST "https://api.chartmetric.com/api/token" \
-H "Content-Type: application/json" \
-d "{\"refreshtoken\":\"$CHARTMETRIC_REFRESH_TOKEN\"}" | grep -o '"token":"[^"]*"' | cut -d'"' -f4)
echo "$TOKEN" > "$TOKEN_FILE"
echo "$TOKEN"
fi
}
export CHARTMETRIC_TOKEN=$(get_token)---
Search
Search Artists
curl -s "https://api.chartmetric.com/api/search?q=Drake&type=artists&limit=5" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Search Tracks
curl -s "https://api.chartmetric.com/api/search?q=One%20Dance&type=tracks&limit=5" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Search Albums
curl -s "https://api.chartmetric.com/api/search?q=Views&type=albums&limit=5" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"---
Artist Endpoints
Get Artist Profile
curl -s "https://api.chartmetric.com/api/artist/3380" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Lookup Artist by Spotify ID
# Extract ID from URL if needed: https://open.spotify.com/artist/3TVXtAsR1Inumwj472S9r4
SPOTIFY_ID="3TVXtAsR1Inumwj472S9r4"
curl -s "https://api.chartmetric.com/api/artist/spotify/$SPOTIFY_ID/get-ids" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Get Artist Metrics
# Valid sources: spotify, instagram, tiktok, twitter, facebook, youtube_channel,
# youtube_artist, soundcloud, deezer, twitch, line, melon, wikipedia, bandsintown
# ⚠️ Use youtube_channel or youtube_artist, NOT youtube
curl -s "https://api.chartmetric.com/api/artist/3380/stat/spotify" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"
curl -s "https://api.chartmetric.com/api/artist/3380/stat/youtube_channel" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Get Where People Listen (Cities)
curl -s "https://api.chartmetric.com/api/artist/3380/where-people-listen" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Get Artist URLs
curl -s "https://api.chartmetric.com/api/artist/3380/urls" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Get Artist Albums
curl -s "https://api.chartmetric.com/api/artist/3380/albums" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Get Artist Tracks
curl -s "https://api.chartmetric.com/api/artist/3380/tracks" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Get Artist Insights
curl -s "https://api.chartmetric.com/api/artist/3380/noteworthy-insights" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Get Artist Career
curl -s "https://api.chartmetric.com/api/artist/3380/career" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"---
Playlist Placements
Current Playlists
# Platforms: spotify, applemusic, deezer, amazon, youtube
# Status: current, past
curl -s "https://api.chartmetric.com/api/artist/3380/spotify/current/playlists?limit=50" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"With Filters (Spotify)
# Filters: editorial, personalized, chart, thisIs, newMusicFriday, radio, indie, majorCurator, popularIndie, brand
curl -s "https://api.chartmetric.com/api/artist/3380/spotify/current/playlists?editorial=true&newMusicFriday=true&limit=50" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"With Date Range and Sorting
curl -s "https://api.chartmetric.com/api/artist/3380/spotify/current/playlists?since=2025-01-01&sortColumn=followers&limit=100" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Past Playlists
curl -s "https://api.chartmetric.com/api/artist/3380/spotify/past/playlists?limit=50" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"---
Similar/Related Artists
Basic Related Artists
curl -s "https://api.chartmetric.com/api/artist/3380/relatedartists?limit=10" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Advanced Similar Artists (by Configuration)
# Config options: audience, mood, genre, musicality (values: high, medium, low)
# At least one config required
curl -s "https://api.chartmetric.com/api/artist/3380/similar-artists/by-configurations?audience=high&genre=high&limit=10" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"---
Audience Demographics
Instagram Audience
curl -s "https://api.chartmetric.com/api/artist/3380/instagram-audience-stats" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"TikTok Audience
curl -s "https://api.chartmetric.com/api/artist/3380/tiktok-audience-stats" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"YouTube Audience
curl -s "https://api.chartmetric.com/api/artist/3380/youtube-audience-stats" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Instagram Top Posts
curl -s "https://api.chartmetric.com/api/SNS/deepSocial/cm_artist/3380/instagram" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"---
Track Endpoints
Get Track
curl -s "https://api.chartmetric.com/api/track/128613854" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Lookup Track by Spotify ID
SPOTIFY_ID="0VjIjW4GlUZAMYd2vXMi3b"
curl -s "https://api.chartmetric.com/api/track/spotify/$SPOTIFY_ID/get-ids" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"---
Playlist & Curator
Get Playlist
# Platforms: spotify, apple, deezer, amazon
curl -s "https://api.chartmetric.com/api/playlist/spotify/37i9dQZF1DXcBWIGoYBM5M" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"Get Curator
# ⚠️ Must use numeric Chartmetric curator ID, not name
curl -s "https://api.chartmetric.com/api/curator/spotify/1" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"---
Discovery
List Genres
curl -s "https://api.chartmetric.com/api/genres" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"List Festivals
curl -s "https://api.chartmetric.com/api/festival/list" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN"---
Pretty Output (with jq)
If jq is available, pipe output for readability:
curl -s "https://api.chartmetric.com/api/artist/3380" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN" | jq .
# Extract specific fields
curl -s "https://api.chartmetric.com/api/search?q=Drake&type=artists" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN" | jq '.obj.artists[] | {name, id, spotify_id}'---
Error Handling
Check response status:
response=$(curl -s -w "\n%{http_code}" "https://api.chartmetric.com/api/artist/3380" \
-H "Authorization: Bearer $CHARTMETRIC_TOKEN")
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | sed '$d')
if [ "$http_code" != "200" ]; then
echo "Error: HTTP $http_code"
echo "$body"
else
echo "$body"
fi---
Common Errors
| HTTP Code | Cause | Fix |
|---|---|---|
| 401 | Token expired or invalid | Re-run token command |
| 402 | Subscription issue | Check Chartmetric account |
| 404 | Invalid ID | Verify Chartmetric ID (not Spotify ID) |
| 429 | Rate limited | Wait 60 seconds |
Chartmetric API - Complete Endpoint Reference
Base URL: https://api.chartmetric.comFull docs JSON: references/api_data.jsonLegend
- ✅ Working on your subscription
- 🔒 Locked (401 - needs higher tier)
- ⚠️ Needs specific params (400 with test data)
---
Authorization (1 endpoint)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | POST | /api/token | Get API access token |
---
Album (6 endpoints)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/album/:id | Album metadata |
| ✅ | GET | /api/album/:id/tracks | Album tracks |
| ✅ | GET | /api/album/:id/:platform/:status/playlists | Album playlists |
| ✅ | GET | /api/album/:type/:id/get-ids | Lookup album by platform ID |
| ⚠️ | GET | /api/album/:id/:platform/:stats | Album stats |
| ⚠️ | GET | /api/album/:id/:type/charts | Album charts |
---
Artist (35 endpoints)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/artist/:id | Artist metadata |
| ✅ | GET | /api/artist/:id/albums | Artist albums |
| ✅ | GET | /api/artist/:id/tracks | Artist tracks |
| ✅ | GET | /api/artist/:id/urls | Social/streaming URLs |
| ✅ | GET | /api/artist/:id/milestones | Career milestones |
| ✅ | GET | /api/artist/:id/news | Recent news |
| ✅ | GET | /api/artist/:id/noteworthy-insights | AI insights |
| ✅ | GET | /api/artist/:id/neighboring-artists | Similar artists |
| ✅ | GET | /api/artist/:id/riaa | RIAA certifications |
| ✅ | GET | /api/artist/:id/artist-rank | Artist ranking |
| ✅ | GET | /api/artist/:id/past-artist-rank | Historical ranking |
| ✅ | GET | /api/artist/:id/cmStats | Cached stats & trends |
| ✅ | GET | /api/artist/:id/career | Career history |
| ✅ | GET | /api/artist/:id/stat/:source | Platform stats (spotify, instagram, etc.) |
| ✅ | GET | /api/artist/:id/where-people-listen | Spotify listeners by city |
| ✅ | GET | /api/artist/:id/:platform/:status/playlists | Playlist placements |
| ✅ | GET | /api/artist/:id/venues | Concert venues |
| ✅ | GET | /api/artist/:id/tvmaze | TV appearances |
| ✅ | GET | /api/artist/:id/instagram-audience-stats | Instagram audience demographics |
| ✅ | GET | /api/artist/:id/instagram-audience-stats/dates | IG audience data dates |
| ✅ | GET | /api/artist/:id/tiktok-audience-stats | TikTok audience demographics |
| ✅ | GET | /api/artist/:id/youtube-audience-stats | YouTube audience demographics |
| ✅ | GET | /api/artist/:id/market-coverage-views/youtube | YouTube views by market |
| ✅ | GET | /api/artist/:type/:id/get-ids | Lookup artist by platform ID |
| ✅ | GET | /api/artist/list/filter | Filter/discover artists |
| ⚠️ | GET | /api/artist/anr/by/playlists | ANR by playlists |
| ⚠️ | GET | /api/artist/anr/by/social-index | ANR by social index |
| ⚠️ | GET | /api/artist/:id/cpp | Cross-platform performance |
| ⚠️ | GET | /api/artist/:id/:status/events | Live events |
| ⚠️ | GET | /api/artist/:id/top-tracks/:source | Top tracks by platform |
| ⚠️ | GET | /api/artist/:id/relatedartists | Related artists |
| ⚠️ | GET | /api/artist/:id/similar-artists/by-configurations | Similar artists (configurable) |
| ⚠️ | GET | /api/artist/:id/social-audience-stats | Social audience stats |
| ⚠️ | GET | /api/artist/:type/list | List artists by metric |
---
Brand (3 endpoints)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/brand/list | List all brands |
| ✅ | GET | /api/brand/list/by/interest | Brands by interest |
| ⚠️ | GET | /api/brand/:brandId | Brand info |
---
Charts (32 endpoints)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/charts/shazam/:country_code/cities | Shazam cities |
| 🔒 | GET | /api/charts/ | Charts introduction |
| 🔒 | GET | /api/charts/airplay/:chart_type | Airplay charts |
| 🔒 | GET | /api/charts/amazon/:chart-type | Amazon charts |
| 🔒 | GET | /api/charts/applemusic/:chart-type | Apple Music charts |
| 🔒 | GET | /api/charts/tiktok/:chart-type | TikTok charts |
| 🔒 | GET | /api/charts/youtube/:chart_type | YouTube charts |
| 🔒 | GET | /api/charts/itunes/:chart-type | iTunes charts |
| ⚠️ | GET | /api/charts/anghami/track/:chartType | Anghami track charts |
| ⚠️ | GET | /api/charts/beatport | Beatport charts |
| ⚠️ | GET | /api/charts/:platform/countries | Chart countries |
| ⚠️ | GET | /api/charts/:streamingType/dates | Chart dates |
| ⚠️ | GET | /api/charts/genres/:platform | Chart genres |
| ⚠️ | GET | /api/charts/:type/:type_id/:chart_type/cm-score | Chartmetric score |
| ⚠️ | GET | /api/charts/circle/album/:chartType | Circle album charts |
| ⚠️ | GET | /api/charts/circle/track/:chartType | Circle track charts |
| ⚠️ | GET | /api/charts/deezer/ | Deezer charts |
| ⚠️ | GET | /api/charts/hanteo/album/:chartType | Hanteo album charts |
| ⚠️ | GET | /api/charts/hanteo/track/:chartType | Hanteo track charts |
| ⚠️ | GET | /api/charts/line_music/album/:chartType | Line Music album charts |
| ⚠️ | GET | /api/charts/line_music/track/:chartType | Line Music track charts |
| ⚠️ | GET | /api/charts/melon/track/:chartType | Melon track charts |
| ⚠️ | GET | /api/charts/pandora/track/:chartType | Pandora track charts |
| ⚠️ | GET | /api/charts/qq/ | QQ Music charts |
| ⚠️ | GET | /api/charts/shazam | Shazam charts |
| ⚠️ | GET | /api/charts/soundcloud | SoundCloud (legacy) |
| ⚠️ | GET | /api/charts/soundcloud/track/:chartType | SoundCloud charts |
| ⚠️ | GET | /api/charts/spotify/artists | Spotify artist charts |
| ⚠️ | GET | /api/charts/spotify/freshfind | Spotify Freshfind |
| ⚠️ | GET | /api/charts/spotify | Spotify track charts |
| ⚠️ | GET | /api/charts/tiktok/tracks/:chart-type | TikTok track charts |
| ⚠️ | GET | /api/charts/twitch/users | Twitch charts |
---
City (2 endpoints)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/city/:id/:source/top-artists | Top artists in city |
| ⚠️ | GET | /api/city/:id/:source/top-tracks | Top tracks in city |
---
Curator (5 endpoints)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/curator/:platform/:id/ | Curator metadata |
| ✅ | GET | /api/curator/:platform/:id/playlists | Curator's playlists |
| ✅ | GET | /api/curator/:platform/:id/stat/:source | Curator fan metrics |
| ✅ | GET | /api/curator/:platform/:id/urls | Curator social URLs |
| ✅ | GET | /api/curator/:platform/lists | List curators |
---
Event (1 endpoint)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/event/venue/:venueId | Events at venue |
---
Festival (1 endpoint)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/festival/list | List festivals |
---
Genre (1 endpoint)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/genre | List all genres |
---
Playlist (9 endpoints)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/playlist/:platform/:id | Playlist metadata |
| ✅ | GET | /api/playlist/:platform/:id/stats | Playlist stats over time |
| ✅ | GET | /api/playlist/:platform/:id/updated | Last updated time |
| ✅ | GET | /api/playlist/:platform/lists | List playlists |
| ⚠️ | GET | /api/playlist/by/:type/:id/evolution | Playlist evolution |
| ⚠️ | GET | /api/playlist/by/:type/:id/playlist-evolution | Playlist evolution (alt) |
| ⚠️ | GET | /api/playlist/:platform/:id/journey-progression/:type | Playlist journey |
| ⚠️ | GET | /api/playlist/:platform/:id/snapshot | Playlist snapshot |
| ⚠️ | GET | /api/playlist/:platform/:id/:span/tracks | Playlist tracks |
---
Radio (5 endpoints)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/radio/station-list | Radio station list |
| ⚠️ | GET | /api/radio/:type/:id/airplay-totals | Total airplays |
| ⚠️ | GET | /api/radio/:type/:id/airplay-totals/:entity | Airplays by entity |
| ⚠️ | GET | /api/radio/:type/:id/airplays | Airplay time series |
| ⚠️ | GET | /api/radio/:type/:id/broadcast-markets | Broadcast markets |
---
Recommendation (1 endpoint)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ⚠️ | GET | /api/playlist/:platform/:id/similarplaylists | Similar playlists |
---
SNS (1 endpoint)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/SNS/deepSocial/cm_artist/:id/instagram | Instagram top posts/reels |
---
Search (5 endpoints)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/search | Universal search |
| ✅ | GET | /api/genres | List genre IDs and names |
| ✅ | GET | /api/genres/:id | Get genre by ID |
| ⚠️ | GET | /api/cities | Get city info |
| ⚠️ | GET | /api/search/social | Social search |
---
Track (12 endpoints)
| Status | Method | Endpoint | Description |
|---|---|---|---|
| ✅ | GET | /api/track/:id | Track metadata |
| ✅ | GET | /api/track/:id/milestones | Track milestones |
| ✅ | GET | /api/track/:id/topVideos | Top TikTok videos |
| ✅ | GET | /api/track/youtube/:id/topShorts | Top YouTube Shorts |
| ✅ | GET | /api/track/:id/video-trends | TikTok video trends |
| ✅ | GET | /api/track/:type/:id/get-ids | Lookup track by platform ID |
| ✅ | GET | /api/track/list/filter | Filter/discover tracks |
| ⚠️ | GET | /api/track/:id/:platform/:status/playlists | Track playlists |
| ⚠️ | GET | /api/track/:id/relatedTracks | Related tracks |
| ⚠️ | GET | /api/track/:id/:platform/stats/:mode | Track stats |
| ⚠️ | GET | /api/track/:id/:platform/playlists/snapshot | Playlist snapshot |
| ⚠️ | GET | /api/track/:id/:type/charts | Track charts |
---
Summary
| Category | Total | Working | Locked | Needs Params |
|---|---|---|---|---|
| Authorization | 1 | 1 | 0 | 0 |
| Album | 6 | 4 | 0 | 2 |
| Artist | 35 | 24 | 0 | 11 |
| Brand | 3 | 2 | 0 | 1 |
| Charts | 32 | 1 | 7 | 24 |
| City | 2 | 1 | 0 | 1 |
| Curator | 5 | 5 | 0 | 0 |
| Event | 1 | 1 | 0 | 0 |
| Festival | 1 | 1 | 0 | 0 |
| Genre | 1 | 1 | 0 | 0 |
| Playlist | 9 | 4 | 0 | 5 |
| Radio | 5 | 1 | 0 | 4 |
| Recommendation | 1 | 0 | 0 | 1 |
| SNS | 1 | 1 | 0 | 0 |
| Search | 5 | 3 | 0 | 2 |
| Track | 12 | 7 | 0 | 5 |
| TOTAL | 120 | 55 | 7 | 58 |
Chartmetric API - Parameter Guide
All parameters below have been tested and verified working.
---
Search Endpoints
Universal Search ✅
GET /api/search?q=Drake&type=artists
GET /api/search?q=One%20Dance&type=tracks
GET /api/search?q=Views&type=albumsq: Search querytype:artists|tracks|albums
---
Artist Endpoints
Artist Metadata ✅
GET /api/artist/:id- No params required
Artist by Spotify ID ✅
GET /api/artist/spotify/:spotify_id/get-ids- Returns Chartmetric ID for the artist
Artist Stats ✅
GET /api/artist/:id/stat/:source- Valid sources:
spotify✅instagram✅tiktok✅twitter✅facebook✅youtube_channel✅youtube_artist✅soundcloud✅deezer✅twitch✅line✅melon✅wikipedia✅bandsintown✅- ~~
bilibili~~ ❌ (doesn't exist) - ~~
snap~~ ❌ (doesn't exist)
Artist Where People Listen ✅
GET /api/artist/:id/where-people-listen- Returns top cities by Spotify monthly listeners
Artist URLs ✅
GET /api/artist/:id/urls- Returns social and streaming URLs
Artist Albums ✅
GET /api/artist/:id/albums- Returns all albums
Artist Tracks ✅
GET /api/artist/:id/tracks- Returns all tracks
Artist Top Tracks ✅
GET /api/artist/:id/top-tracks/tiktok- Only `tiktok` works
- ~~
cm~~ and ~~youtubeforartist~~ return "Domain does not exist"
Artist Charts ✅
GET /api/artist/:id/:type/charts- Working types:
shazam✅itunes_top✅itunes_albums✅youtube✅youtube_tracks✅youtube_videos✅youtube_trends✅
- NOT working (return "Streaming type does not exist"):
- ~~
spotify_viral_daily~~ - ~~
spotify_top_daily~~ - ~~
applemusic_top~~ - ~~
beatport~~ - ~~
amazon~~
ANR by Playlists ✅
GET /api/artist/anr/by/playlists?sortBy=followers_total_reach_diff_week_percentsortByis REQUIRED- DO NOT include
streamingType(not allowed)
---
Track Endpoints
Track Metadata ✅
GET /api/track/:id- Use Chartmetric track ID (not Spotify ID)
Track by Spotify ID ✅
GET /api/track/spotify/:spotify_id/get-ids- Returns Chartmetric ID for the track
---
Album Endpoints
Album Metadata ✅
GET /api/album/:id- Use Chartmetric album ID
Album Tracks ✅
GET /api/album/:id/tracksAlbum by Spotify ID ✅
GET /api/album/spotify/:spotify_id/get-ids- Returns list with
cm_albumIDs
---
Chart Endpoints
Spotify Charts ✅
GET /api/charts/spotify?latest=true&country_code=US&interval=daily&type=plays| Param | Required | Values |
|---|---|---|
country_code | ✅ | US, GB, DE, etc. |
interval | ✅ | daily, weekly |
type | ✅ | plays, popularity, playlist_count, playlist_reach, viral, regional |
latest | optional | true |
date | optional | 2026-01-01 |
Apple Music Charts ✅
GET /api/charts/applemusic/tracks?latest=true&country_code=US&type=top
GET /api/charts/applemusic/albums?latest=true&country_code=US| Param | Required | Values |
|---|---|---|
country_code | ✅ | US, GB, etc. |
type (tracks only) | ✅ | top, daily, city |
iTunes Charts ✅
GET /api/charts/itunes/tracks?latest=true&country_code=US
GET /api/charts/itunes/albums?latest=true&country_code=US| Param | Required | Values |
|---|---|---|
country_code | ✅ | US, GB, etc. |
genre | optional | pop, hip-hop, etc. |
YouTube Charts ✅
GET /api/charts/youtube/videos?latest=true&country_code=US| Param | Required | Values |
|---|---|---|
country_code | ✅ | US, GB, etc. |
TikTok Charts ✅
GET /api/charts/tiktok/tracks?latest=true| Param | Required | Values |
|---|---|---|
latest | optional | true |
~~country_code~~ | ❌ | NOT ALLOWED |
Shazam Charts ✅
GET /api/charts/shazam?latest=true&country_code=USDeezer Charts ✅
GET /api/charts/deezer?latest=true&country_code=USAmazon Charts ✅
GET /api/charts/amazon/tracks?latest=true&genre=pop&type=popular_track| Param | Required | Values |
|---|---|---|
type | ✅ | popular_track, new_track |
genre | ✅ | pop, hip-hop, All Genres, etc. |
~~country_code~~ | ❌ | NOT ALLOWED |
SoundCloud Charts ✅
GET /api/charts/soundcloud?latest=true&country_code=US&kind=top&genre=all-music| Param | Required | Values |
|---|---|---|
country_code | ✅ | US, GLOBAL, etc. |
kind | ✅ | top, trending |
genre | ✅ | all-music, specific genres |
Melon Charts (Korea) ✅
GET /api/charts/melon/track/general?duration=daily&genre=All%20Genres| Param | Required | Values |
|---|---|---|
duration | ✅ | daily |
genre | ✅ | All Genres, pop, k-pop, hot |
Hanteo Charts (Korea) ✅
GET /api/charts/hanteo/album/music?duration=daily&latest=true| Param | Required | Values |
|---|---|---|
duration | ✅ | daily |
| Chart type in path | ✅ | music |
Chart Dates ✅
GET /api/charts/spotify_tracks/dates?fromDaysAgo=28| Param | Required | Values |
|---|---|---|
fromDaysAgo | ✅ | Max 28 |
Chart Genres ✅
GET /api/charts/genres/apple_music
GET /api/charts/genres/shazam_genre- Valid platforms:
amazon,apple_music,beatport,itunes,shazam_genre,soundcloud, etc.
---
Other Endpoints
Genre List ✅
GET /api/genre- No params required
---
Artist Playlists ✅
GET /api/artist/:id/:platform/:status/playlistsPath Parameters
| Param | Required | Values |
|---|---|---|
id | ✅ | Chartmetric artist ID |
platform | ✅ | spotify, applemusic, deezer, amazon, youtube |
status | ✅ | current, past |
Query Parameters
| Param | Type | Description |
|---|---|---|
since | date | Start date (YYYY-MM-DD) |
until | date | End date (YYYY-MM-DD) |
limit | integer | Results per page (default: 50) |
offset | integer | Pagination offset |
sortColumn | string | Sort by (see table below) |
Note: API defaults to descending sort order. Use --asc flag in script for ascending.
Valid sortColumn by Platform & Status
| Platform | Status | Valid sortColumn Values |
|---|---|---|
| amazon | current | added_at (default), countries, name, peak_position, track |
| applemusic | current | added_at (default), name, peak_position, position, track |
| applemusic | past | added_at, name, peak_position, position, removed_at (default), track |
| deezer | current | added_at (default), fdiff_month, followers, name, peak_position, track |
| deezer | past | added_at, fdiff_month, followers, name, peak_position, removed_at (default), track |
| spotify | current | added_at, code2, fdiff_month, followers (default), name, peak_position, position, track |
| spotify | past | added_at, code2, fdiff_month, followers (default), name, peak_position, position, removed_at, track |
| youtube | current | added_at (default), name, peak_position, track, vdiff_month, views |
| youtube | past | added_at, name, peak_position, removed_at (default), track, vdiff_month, views |
Playlist Type Filters (Platform-Specific)
| Filter | spotify | applemusic | deezer | amazon |
|---|---|---|---|---|
editorial | ✅ | ✅ | ✅ | |
editorialBrand | ✅ | |||
personalized | ✅ | |||
deezerPartner | ✅ | |||
chart | ✅ | ✅ | ✅ | |
thisIs | ✅ | |||
hundredPercent | ✅ | |||
newMusicFriday | ✅ | |||
radio | ✅ | ✅ | ||
fullyPersonalized | ✅ | |||
brand | ✅ | ✅ | ||
majorCurator | ✅ | ✅ | ||
musicBrand | ✅ | |||
nonMusicBrand | ✅ | |||
popularIndie | ✅ | ✅ | ||
indie | ✅ | ✅ | ✅ | |
audiobook | ✅ | |||
personalityArtist | ✅ |
⚠️ Important: When no filter parameters are specified, the API may return an empty array. Specify filters explicitly to get data.
Example Requests
# Basic - all current Spotify playlists
GET /api/artist/1022311/spotify/current/playlists
# With date range and sorting
GET /api/artist/1022311/spotify/current/playlists?since=2025-01-01&sortColumn=followers&limit=50
# Only New Music Friday playlists
GET /api/artist/1022311/spotify/current/playlists?editorial=true&personalized=true&chart=false&thisIs=false&newMusicFriday=true&radio=false&fullyPersonalized=false&brand=true&majorCurator=true&popularIndie=true&indie=true&audiobook=falseNote: The generic /artist/:id/playlists returns 401 - use platform-specific path instead.
---
Related Artists ✅
Basic Endpoint
GET /api/artist/:id/relatedartists?limit=10| Param | Required | Description |
|---|---|---|
limit | ✅ | Number of artists (1-100) |
Similar Artists by Configurations ✅
GET /api/artist/:id/similar-artists/by-configurations?audience=high&genre=high&limit=10| Param | Required | Values | Description |
|---|---|---|---|
audience | ⚠️ At least one config required | high, medium, low | Audience similarity |
mood | ⚠️ At least one config required | high, medium, low | Mood similarity |
genre | ⚠️ At least one config required | high, medium, low | Genre similarity |
musicality | ⚠️ At least one config required | high, medium, low | Musicality similarity |
limit | optional | integer | Number of results (default: 10) |
offset | optional | integer | Pagination offset (default: 0) |
Response includes:
similarityscore (0-1)career_stage(undiscovered, developing, mid-level, mainstream, superstar, legendary)recent_momentum(decline, gradual decline, steady, growth, explosive growth)- Spotify followers, monthly listeners, playlist reach
- YouTube subscribers, TikTok followers, Instagram followers
Note: /artist/:id/similar returns 401 - use relatedartists or similar-artists/by-configurations instead.
---
Curator Endpoints ✅
GET /api/curator/:platform/:numeric_id| Param | Values |
|---|---|
platform | spotify |
numeric_id | Chartmetric curator ID (integer) |
Example: /api/curator/spotify/1 returns PlayStation curator
Note: /curator/search returns 401 - must have numeric curator ID.
---
Endpoints That Return 401 (Internal Only)
These specific paths are internal and not accessible:
1. /api/charts/ - Charts Introduction (use specific chart endpoints) 2. /api/artist/:id/playlists - Use /artist/:id/:platform/current/playlists 3. /api/artist/:id/similar - Use /artist/:id/relatedartists?limit=N 4. /api/curator/search - Use /curator/:platform/:id with numeric ID 5. /api/charts/tiktok/popular - Use /charts/tiktok/tracks 6. /api/charts/tiktok/viral - Use /charts/tiktok/tracks
---
Summary
Verified Working: 57+ core endpoints
All essential functionality is available:
- ✅ Search (artists, tracks, albums)
- ✅ Artist data (metadata, stats, cities, URLs, albums, tracks, career)
- ✅ Artist playlists (per platform: spotify, applemusic, deezer, amazon, youtube)
- With filter support: editorial, newMusicFriday, indie, chart, etc.
- ✅ Related artists (basic endpoint)
- ✅ Similar artists by configurations (with audience/mood/genre/musicality filters)
- ✅ Platform metrics (14 sources)
- ✅ Track/album lookup by Spotify ID
- ✅ Curator lookup (by numeric ID)
- ✅ Charts (Spotify, Apple Music, iTunes, YouTube, TikTok, Shazam, Deezer, Amazon, SoundCloud, Melon, Hanteo)
- ✅ Genre discovery
- ✅ ANR playlists
---
Quick Reference: Most Used Endpoints
# Search
GET /api/search?q=Drake&type=artists
# Artist profile
GET /api/artist/1320
# Artist by Spotify
GET /api/artist/spotify/3TVXtAsR1Inumwj472S9r4/get-ids
# Artist stats
GET /api/artist/1320/stat/spotify
# Artist cities
GET /api/artist/1320/where-people-listen
# Spotify charts
GET /api/charts/spotify?latest=true&country_code=US&interval=daily&type=plays
# TikTok charts
GET /api/charts/tiktok/tracks?latest=true#!/usr/bin/env python3
"""
Discover artists by filtering on metrics, geography, genre, and more.
This is the POWER endpoint - find artists matching specific criteria.
Usage:
# Find US artists with 100K-500K Spotify monthly listeners
python discover_artists.py --country US --spotify-listeners 100000 500000
# Find TikTok-famous artists weak on Spotify (signing opportunity)
python discover_artists.py --tiktok-followers 1000000 10000000 --spotify-listeners 0 100000
# Find emerging hip-hop artists
python discover_artists.py --genre 86 --spotify-listeners 50000 200000 --sort weekly_diff.sp_monthly_listeners
# Find solo female artists in Brazil
python discover_artists.py --country BR --band false --pronoun she/her
# Find artists at a specific festival
python discover_artists.py --festival-id 123
Metric Filters (use two values for [min, max]):
--spotify-listeners Spotify monthly listeners range
--spotify-followers Spotify followers range
--spotify-popularity Spotify popularity (0-100)
--tiktok-followers TikTok followers range
--tiktok-likes TikTok likes range
--instagram-followers Instagram followers range
--youtube-subscribers YouTube subscribers range
--cpp Cross-Platform Performance score range
Other Filters:
--country 2-letter country code (US, GB, BR, etc.)
--genre Genre ID (use list_genres.py to find IDs)
--subgenre Subgenre ID
--band true/false - filter bands or solo artists
--pronoun he/him, she/her, they/them, any
--first-release-days Artists who debuted within X days
--festival-id Artists playing at specific festival
Sorting:
--sort Sort column (default: latest.sp_monthly_listeners)
--asc Sort ascending (default: descending)
Sort column format: <period>.<stat>
Periods: latest, weekly_diff, monthly_diff
Stats: sp_monthly_listeners, sp_followers, tt_followers, ig_followers, cpp, etc.
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import argparse
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def discover_artists(
country: str = None,
genre_id: int = None,
subgenre_id: int = None,
band: bool = None,
pronoun: str = None,
first_release_days: int = None,
festival_ids: list = None,
spotify_listeners: tuple = None,
spotify_followers: tuple = None,
spotify_popularity: tuple = None,
tiktok_followers: tuple = None,
tiktok_likes: tuple = None,
instagram_followers: tuple = None,
youtube_subscribers: tuple = None,
cpp: tuple = None,
sort_column: str = "latest.sp_monthly_listeners",
sort_desc: bool = True,
limit: int = 50,
offset: int = 0
) -> dict:
"""
Discover artists using Chartmetric's powerful filter endpoint.
Returns artists matching the specified criteria with all their metrics.
"""
token = get_token()
params = {
"limit": limit,
"offset": offset,
"sortColumn": sort_column,
"sortOrderDesc": str(sort_desc).lower()
}
# Basic filters
if country:
params["code2"] = country
if genre_id:
params["tagId"] = genre_id
if subgenre_id:
params["subTagId"] = subgenre_id
if band is not None:
params["band"] = str(band).lower()
if pronoun:
params["pronoun"] = pronoun
if first_release_days:
params["firstReleaseDaysAgo"] = first_release_days
if festival_ids:
params["eventIds[]"] = festival_ids
# Metric range filters (need special handling for arrays)
range_params = []
if spotify_listeners:
range_params.append(("sp_ml[]", spotify_listeners[0]))
range_params.append(("sp_ml[]", spotify_listeners[1]))
if spotify_followers:
range_params.append(("sp_f[]", spotify_followers[0]))
range_params.append(("sp_f[]", spotify_followers[1]))
if spotify_popularity:
range_params.append(("sp_p[]", spotify_popularity[0]))
range_params.append(("sp_p[]", spotify_popularity[1]))
if tiktok_followers:
range_params.append(("tt_f[]", tiktok_followers[0]))
range_params.append(("tt_f[]", tiktok_followers[1]))
if tiktok_likes:
range_params.append(("tt_l[]", tiktok_likes[0]))
range_params.append(("tt_l[]", tiktok_likes[1]))
if instagram_followers:
range_params.append(("ig_f[]", instagram_followers[0]))
range_params.append(("ig_f[]", instagram_followers[1]))
if youtube_subscribers:
range_params.append(("ytc_s[]", youtube_subscribers[0]))
range_params.append(("ytc_s[]", youtube_subscribers[1]))
if cpp:
range_params.append(("cpp[]", cpp[0]))
range_params.append(("cpp[]", cpp[1]))
# Build URL with array params
url = f"{API_BASE}/artist/list/filter"
response = requests.get(
url,
headers={"Authorization": f"Bearer {token}"},
params=params if not range_params else None
)
# If we have range params, we need to build the URL manually
if range_params:
from urllib.parse import urlencode
base_query = urlencode(params)
range_query = "&".join([f"{k}={v}" for k, v in range_params])
full_url = f"{url}?{base_query}&{range_query}"
response = requests.get(
full_url,
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 401:
return {"error": "Unauthorized", "message": "This endpoint may require a higher subscription tier."}
response.raise_for_status()
return response.json()
def main():
parser = argparse.ArgumentParser(
description="Discover artists by filtering on metrics and attributes",
formatter_class=argparse.RawDescriptionHelpFormatter
)
# Basic filters
parser.add_argument("--country", "-c", help="2-letter country code (US, GB, BR)")
parser.add_argument("--genre", "-g", type=int, help="Genre ID")
parser.add_argument("--subgenre", type=int, help="Subgenre ID")
parser.add_argument("--band", choices=["true", "false"], help="Filter bands or solo")
parser.add_argument("--pronoun", choices=["any", "he/him", "she/her", "they/them"])
parser.add_argument("--first-release-days", type=int, help="Debuted within X days")
parser.add_argument("--festival-id", type=int, action="append", help="Festival event ID")
# Metric ranges (2 values each)
parser.add_argument("--spotify-listeners", type=int, nargs=2, metavar=("MIN", "MAX"),
help="Spotify monthly listeners range")
parser.add_argument("--spotify-followers", type=int, nargs=2, metavar=("MIN", "MAX"),
help="Spotify followers range")
parser.add_argument("--spotify-popularity", type=int, nargs=2, metavar=("MIN", "MAX"),
help="Spotify popularity range (0-100)")
parser.add_argument("--tiktok-followers", type=int, nargs=2, metavar=("MIN", "MAX"),
help="TikTok followers range")
parser.add_argument("--tiktok-likes", type=int, nargs=2, metavar=("MIN", "MAX"),
help="TikTok likes range")
parser.add_argument("--instagram-followers", type=int, nargs=2, metavar=("MIN", "MAX"),
help="Instagram followers range")
parser.add_argument("--youtube-subscribers", type=int, nargs=2, metavar=("MIN", "MAX"),
help="YouTube subscribers range")
parser.add_argument("--cpp", type=int, nargs=2, metavar=("MIN", "MAX"),
help="Cross-Platform Performance score range")
# Sorting and pagination
parser.add_argument("--sort", default="latest.sp_monthly_listeners",
help="Sort column (default: latest.sp_monthly_listeners)")
parser.add_argument("--asc", action="store_true", help="Sort ascending")
parser.add_argument("--limit", "-l", type=int, default=50, help="Results limit")
parser.add_argument("--offset", type=int, default=0, help="Pagination offset")
parser.add_argument("--json", action="store_true", help="Output raw JSON")
args = parser.parse_args()
result = discover_artists(
country=args.country,
genre_id=args.genre,
subgenre_id=args.subgenre,
band=args.band == "true" if args.band else None,
pronoun=args.pronoun,
first_release_days=args.first_release_days,
festival_ids=args.festival_id,
spotify_listeners=tuple(args.spotify_listeners) if args.spotify_listeners else None,
spotify_followers=tuple(args.spotify_followers) if args.spotify_followers else None,
spotify_popularity=tuple(args.spotify_popularity) if args.spotify_popularity else None,
tiktok_followers=tuple(args.tiktok_followers) if args.tiktok_followers else None,
tiktok_likes=tuple(args.tiktok_likes) if args.tiktok_likes else None,
instagram_followers=tuple(args.instagram_followers) if args.instagram_followers else None,
youtube_subscribers=tuple(args.youtube_subscribers) if args.youtube_subscribers else None,
cpp=tuple(args.cpp) if args.cpp else None,
sort_column=args.sort,
sort_desc=not args.asc,
limit=args.limit,
offset=args.offset
)
if args.json:
print(json.dumps(result, indent=2))
return
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
artists = result.get("obj", [])
print(f"Found {len(artists)} artists:\n")
for artist in artists[:30]:
print(f"- {artist.get('name')}")
print(f" CM ID: {artist.get('id')}")
# Show key metrics
if artist.get('sp_monthly_listeners'):
print(f" Spotify Listeners: {artist.get('sp_monthly_listeners'):,}")
if artist.get('sp_followers'):
print(f" Spotify Followers: {artist.get('sp_followers'):,}")
if artist.get('tiktok_followers'):
print(f" TikTok Followers: {artist.get('tiktok_followers'):,}")
if artist.get('ins_followers'):
print(f" Instagram Followers: {artist.get('ins_followers'):,}")
if artist.get('code2'):
print(f" Country: {artist.get('code2')}")
print()
if len(artists) > 30:
print(f"... and {len(artists) - 30} more (use --limit to adjust)")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Get artist's albums.
Usage:
python get_artist_albums.py <chartmetric_id>
python get_artist_albums.py 3380
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_artist_albums(cm_id: str) -> dict:
"""Fetch artist's albums from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/artist/{cm_id}/albums",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_artist_albums.py <chartmetric_id>")
sys.exit(1)
result = get_artist_albums(sys.argv[1])
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
albums = result.get("obj", [])
print(f"Found {len(albums)} albums:\n")
for album in albums[:20]: # Limit to first 20
print(f"- {album.get('name')}")
print(f" Chartmetric ID: {album.get('id')}")
print(f" Release: {album.get('release_date', 'N/A')}")
print()
#!/usr/bin/env python3
"""
Get artist audience demographics for a platform.
Usage:
python get_artist_audience.py <chartmetric_id> [--platform instagram]
python get_artist_audience.py 3380
python get_artist_audience.py 3380 --platform tiktok
python get_artist_audience.py 3380 --platform youtube
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import argparse
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_artist_audience(cm_id: str, platform: str = "instagram") -> dict:
"""Fetch artist audience demographics from Chartmetric."""
token = get_token()
endpoint_map = {
"instagram": f"{API_BASE}/artist/{cm_id}/instagram-audience-stats",
"tiktok": f"{API_BASE}/artist/{cm_id}/tiktok-audience-stats",
"youtube": f"{API_BASE}/artist/{cm_id}/youtube-audience-stats",
}
url = endpoint_map.get(platform)
if not url:
return {"error": f"Unknown platform: {platform}", "valid_platforms": list(endpoint_map.keys())}
response = requests.get(url, headers={"Authorization": f"Bearer {token}"})
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Artist not found or no audience data", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
def main():
parser = argparse.ArgumentParser(description="Get artist audience demographics")
parser.add_argument("chartmetric_id", help="Chartmetric artist ID")
parser.add_argument("--platform", "-p", default="instagram",
choices=["instagram", "tiktok", "youtube"],
help="Platform for audience data")
args = parser.parse_args()
result = get_artist_audience(args.chartmetric_id, args.platform)
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Lookup artist by Spotify ID or URL.
Usage:
python get_artist_by_spotify.py <spotify_id>
python get_artist_by_spotify.py 3TVXtAsR1Inumwj472S9r4
python get_artist_by_spotify.py "https://open.spotify.com/artist/3TVXtAsR1Inumwj472S9r4"
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import re
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def extract_spotify_id(url_or_id: str) -> str:
"""Extract Spotify artist ID from URL or return as-is if already an ID."""
# If it looks like a URL, extract the ID
match = re.search(r"artist[/:]([a-zA-Z0-9]+)", url_or_id)
if match:
return match.group(1)
return url_or_id
def get_artist_by_spotify(spotify_id: str) -> dict:
"""Lookup Chartmetric artist by Spotify ID.
Uses the /artist/:type/:id/get-ids endpoint to lookup artist by platform ID.
Returns the Chartmetric artist ID and other platform IDs.
"""
token = get_token()
response = requests.get(
f"{API_BASE}/artist/spotify/{spotify_id}/get-ids",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {
"error": "Payment Required",
"message": "Your Chartmetric API subscription may be expired or this endpoint requires a higher tier. Check your account at chartmetric.com or contact hi@chartmetric.com"
}
if response.status_code == 404:
return {"error": "Artist not found in Chartmetric", "spotify_id": spotify_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_artist_by_spotify.py <spotify_id_or_url>")
sys.exit(1)
spotify_id = extract_spotify_id(sys.argv[1])
result = get_artist_by_spotify(spotify_id)
print(json.dumps(result, indent=2))
#!/usr/bin/env python3
"""
Get artist career history.
Usage:
python get_artist_career.py <chartmetric_id>
python get_artist_career.py 3380
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_artist_career(cm_id: str) -> dict:
"""Fetch artist career history from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/artist/{cm_id}/career",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_artist_career.py <chartmetric_id>")
sys.exit(1)
result = get_artist_career(sys.argv[1])
print(json.dumps(result, indent=2))
#!/usr/bin/env python3
"""
Get cities where people listen to an artist (Spotify data).
Usage:
python get_artist_cities.py <chartmetric_id>
python get_artist_cities.py 3380
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_artist_cities(cm_id: str) -> dict:
"""Fetch where people listen data from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/artist/{cm_id}/where-people-listen",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_artist_cities.py <chartmetric_id>")
sys.exit(1)
result = get_artist_cities(sys.argv[1])
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
cities_data = result.get("obj", {}).get("cities", {})
print(f"Top cities for this artist:\n")
# Get latest listener count for each city
city_latest = []
for city_name, history in cities_data.items():
if history and len(history) > 0:
latest = history[-1] # Most recent data point
city_latest.append({
"name": city_name,
"country": latest.get("code2", ""),
"listeners": latest.get("listeners", 0)
})
# Sort by listeners descending
city_latest.sort(key=lambda x: x.get("listeners", 0), reverse=True)
for city in city_latest[:20]:
print(f"- {city.get('name', 'Unknown')}, {city.get('country', '')}")
print(f" Listeners: {city.get('listeners', 0):,}")
print()
#!/usr/bin/env python3
"""
Get AI-generated noteworthy insights for an artist.
Usage:
python get_artist_insights.py <chartmetric_id>
python get_artist_insights.py 3380
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_artist_insights(cm_id: str) -> dict:
"""Fetch noteworthy insights from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/artist/{cm_id}/noteworthy-insights",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_artist_insights.py <chartmetric_id>")
sys.exit(1)
result = get_artist_insights(sys.argv[1])
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
insights = result.get("obj", [])
print(f"Noteworthy Insights:\n")
for insight in insights:
print(f"• {insight.get('text', insight)}")
print()
#!/usr/bin/env python3
"""
Get artist's top Instagram posts and reels.
Usage:
python get_artist_instagram_posts.py <chartmetric_id>
python get_artist_instagram_posts.py 3380
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_artist_instagram_posts(cm_id: str) -> dict:
"""Fetch artist's top Instagram posts and reels."""
token = get_token()
response = requests.get(
f"{API_BASE}/SNS/deepSocial/cm_artist/{cm_id}/instagram",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Artist not found or no Instagram data", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_artist_instagram_posts.py <chartmetric_id>")
sys.exit(1)
result = get_artist_instagram_posts(sys.argv[1])
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
print(json.dumps(result, indent=2))
#!/usr/bin/env python3
"""
Get artist metrics from a specific platform.
Usage:
python get_artist_metrics.py <chartmetric_id> --source spotify
python get_artist_metrics.py 3380 -s youtube_channel
python get_artist_metrics.py 3380 -s instagram
Platforms (14 total):
spotify, instagram, tiktok, twitter, facebook, youtube_channel,
youtube_artist, soundcloud, deezer, twitch, line, melon,
wikipedia, bandsintown
Note: Use 'youtube_channel' or 'youtube_artist', NOT 'youtube'
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import json
import argparse
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
VALID_SOURCES = [
"spotify",
"instagram",
"tiktok",
"twitter",
"facebook",
"youtube_channel",
"youtube_artist",
"soundcloud",
"deezer",
"twitch",
"line",
"melon",
"wikipedia",
"bandsintown",
]
def get_artist_metrics(cm_id: str, source: str) -> dict:
"""Fetch artist metrics for a specific platform."""
token = get_token()
response = requests.get(
f"{API_BASE}/artist/{cm_id}/stat/{source}",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {
"error": "Payment Required",
"message": "Your Chartmetric API subscription may be expired or this endpoint requires a higher tier. Check your account at chartmetric.com or contact hi@chartmetric.com"
}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
def main():
parser = argparse.ArgumentParser(description="Get artist metrics for a platform")
parser.add_argument("chartmetric_id", help="Chartmetric artist ID")
parser.add_argument(
"--source", "-s",
required=True,
choices=VALID_SOURCES,
help="Platform source"
)
args = parser.parse_args()
result = get_artist_metrics(args.chartmetric_id, args.source)
print(json.dumps(result, indent=2))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Get artist playlist placements.
Usage:
# Basic usage
python get_artist_playlists.py <chartmetric_id>
python get_artist_playlists.py 3380 --platform spotify --status current
python get_artist_playlists.py 3380 --platform applemusic --status past
# With filters (Spotify)
python get_artist_playlists.py 3380 --editorial --newMusicFriday
python get_artist_playlists.py 3380 --chart --indie
# With date range and sorting
python get_artist_playlists.py 3380 --since 2025-01-01 --sort followers --limit 50
Platforms: spotify, applemusic, deezer, amazon, youtube
Status: current, past
Spotify filters: editorial, personalized, chart, thisIs, newMusicFriday, radio,
fullyPersonalized, brand, majorCurator, popularIndie, indie, audiobook
Apple Music filters: editorial, editorialBrand, chart, radio, musicBrand,
nonMusicBrand, indie, personalityArtist
Deezer filters: editorial, deezerPartner, chart, hundredPercent, brand,
majorCurator, popularIndie, indie
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import argparse
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
# Platform-specific filter support
PLATFORM_FILTERS = {
"spotify": ["editorial", "personalized", "chart", "thisIs", "newMusicFriday",
"radio", "fullyPersonalized", "brand", "majorCurator",
"popularIndie", "indie", "audiobook"],
"applemusic": ["editorial", "editorialBrand", "chart", "radio", "musicBrand",
"nonMusicBrand", "indie", "personalityArtist"],
"deezer": ["editorial", "deezerPartner", "chart", "hundredPercent", "brand",
"majorCurator", "popularIndie", "indie"],
"amazon": [], # No filters for Amazon
"youtube": [] # No filters for YouTube
}
def get_artist_playlists(
cm_id: str,
platform: str = "spotify",
status: str = "current",
since: str = None,
until: str = None,
limit: int = 50,
offset: int = 0,
sort_column: str = None,
sort_desc: bool = True,
filters: dict = None
) -> dict:
"""
Fetch artist playlist placements from Chartmetric.
Args:
cm_id: Chartmetric artist ID
platform: spotify, applemusic, deezer, amazon, youtube
status: current or past
since: Start date (YYYY-MM-DD)
until: End date (YYYY-MM-DD)
limit: Results per page
offset: Pagination offset
sort_column: Column to sort by
sort_desc: Sort descending
filters: Dict of boolean filters (editorial, indie, etc.)
"""
token = get_token()
params = {"limit": limit}
if offset > 0:
params["offset"] = offset
if since:
params["since"] = since
if until:
params["until"] = until
if sort_column:
params["sortColumn"] = sort_column
# Only include sortOrderDesc when explicitly sorting
if not sort_desc: # API defaults to descending, so only set when ascending
params["sortOrderDesc"] = "false"
# Add filter parameters
if filters:
for key, value in filters.items():
if key in PLATFORM_FILTERS.get(platform, []):
params[key] = str(value).lower()
response = requests.get(
f"{API_BASE}/artist/{cm_id}/{platform}/{status}/playlists",
headers={"Authorization": f"Bearer {token}"},
params=params
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 401:
return {"error": "Unauthorized", "message": "This endpoint may require a higher subscription tier."}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
def main():
parser = argparse.ArgumentParser(
description="Get artist playlist placements",
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument("chartmetric_id", help="Chartmetric artist ID")
parser.add_argument("--platform", "-p", default="spotify",
choices=["spotify", "applemusic", "deezer", "amazon", "youtube"],
help="Playlist platform (default: spotify)")
parser.add_argument("--status", "-s", default="current",
choices=["current", "past"],
help="Current or past placements (default: current)")
# Date and pagination
parser.add_argument("--since", help="Start date (YYYY-MM-DD)")
parser.add_argument("--until", help="End date (YYYY-MM-DD)")
parser.add_argument("--limit", "-l", type=int, default=50, help="Results limit")
parser.add_argument("--offset", type=int, default=0, help="Pagination offset")
parser.add_argument("--sort", help="Sort column (followers, added_at, name, etc.)")
parser.add_argument("--asc", action="store_true", help="Sort ascending (default: descending)")
# Filter flags (all platforms)
parser.add_argument("--editorial", action="store_true", help="Editorial playlists")
parser.add_argument("--chart", action="store_true", help="Chart playlists")
parser.add_argument("--indie", action="store_true", help="Indie curator playlists")
parser.add_argument("--radio", action="store_true", help="Radio playlists")
parser.add_argument("--brand", action="store_true", help="Brand playlists")
parser.add_argument("--majorCurator", action="store_true", help="Major curator playlists")
parser.add_argument("--popularIndie", action="store_true", help="Popular indie playlists")
# Spotify-specific filters
parser.add_argument("--personalized", action="store_true", help="Personalized (Spotify)")
parser.add_argument("--thisIs", action="store_true", help="This Is playlists (Spotify)")
parser.add_argument("--newMusicFriday", action="store_true", help="New Music Friday (Spotify)")
parser.add_argument("--fullyPersonalized", action="store_true", help="Fully personalized (Spotify)")
parser.add_argument("--audiobook", action="store_true", help="Audiobook playlists (Spotify)")
# Apple Music filters
parser.add_argument("--editorialBrand", action="store_true", help="Editorial brand (Apple)")
parser.add_argument("--musicBrand", action="store_true", help="Music brand (Apple)")
parser.add_argument("--nonMusicBrand", action="store_true", help="Non-music brand (Apple)")
parser.add_argument("--personalityArtist", action="store_true", help="Personality/artist (Apple)")
# Deezer filters
parser.add_argument("--deezerPartner", action="store_true", help="Deezer partner")
parser.add_argument("--hundredPercent", action="store_true", help="100% playlists (Deezer)")
args = parser.parse_args()
# Build filters dict from args
filter_names = ["editorial", "chart", "indie", "radio", "brand", "majorCurator",
"popularIndie", "personalized", "thisIs", "newMusicFriday",
"fullyPersonalized", "audiobook", "editorialBrand", "musicBrand",
"nonMusicBrand", "personalityArtist", "deezerPartner", "hundredPercent"]
filters = {}
for name in filter_names:
if getattr(args, name, False):
filters[name] = True
result = get_artist_playlists(
args.chartmetric_id,
platform=args.platform,
status=args.status,
since=args.since,
until=args.until,
limit=args.limit,
offset=args.offset,
sort_column=args.sort,
sort_desc=not args.asc,
filters=filters if filters else None
)
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
placements = result.get("obj", [])
print(f"Found {len(placements)} playlist placements:\n")
for item in placements[:20]:
pl = item.get("playlist", {})
track = item.get("track", {})
print(f"- {pl.get('name', 'Unknown')}")
print(f" Track: {track.get('name', 'Unknown')}")
position = pl.get('position', 'N/A')
peak = pl.get('peak_position', 'N/A')
print(f" Position: {position} (Peak: {peak})")
followers = pl.get('followers')
if followers:
print(f" Followers: {followers:,}")
print(f" Added: {pl.get('added_at', 'N/A')}")
if args.status == 'past' and pl.get('removed_at'):
print(f" Removed: {pl.get('removed_at')}")
curator = pl.get('curator_name', 'Unknown')
print(f" Owner: {curator}")
print()
if len(placements) > 20:
print(f"... and {len(placements) - 20} more (use --limit to adjust)")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Get artist's tracks.
Usage:
python get_artist_tracks.py <chartmetric_id>
python get_artist_tracks.py 3380
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_artist_tracks(cm_id: str) -> dict:
"""Fetch artist's tracks from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/artist/{cm_id}/tracks",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_artist_tracks.py <chartmetric_id>")
sys.exit(1)
result = get_artist_tracks(sys.argv[1])
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
tracks = result.get("obj", [])
print(f"Found {len(tracks)} tracks:\n")
for track in tracks[:20]: # Limit to first 20
print(f"- {track.get('name')}")
print(f" Chartmetric ID: {track.get('id')}")
print(f" Album: {track.get('album_names', ['N/A'])[0] if track.get('album_names') else 'N/A'}")
print()
#!/usr/bin/env python3
"""
Get artist's social and streaming service URLs.
Usage:
python get_artist_urls.py <chartmetric_id>
python get_artist_urls.py 3380
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_artist_urls(cm_id: str) -> dict:
"""Fetch artist's social/streaming URLs from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/artist/{cm_id}/urls",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_artist_urls.py <chartmetric_id>")
sys.exit(1)
result = get_artist_urls(sys.argv[1])
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
urls = result.get("obj", [])
print("Artist URLs:\n")
# Handle both list and dict formats
if isinstance(urls, list):
for item in urls:
if isinstance(item, dict):
domain = item.get("domain", "unknown")
url = item.get("url", "")
if url:
print(f"- {domain}: {url}")
else:
print(f"- {item}")
elif isinstance(urls, dict):
for platform, url in urls.items():
if url:
print(f"- {platform}: {url}")
#!/usr/bin/env python3
"""
Get artist profile by Chartmetric ID.
Usage:
python get_artist.py <chartmetric_id>
python get_artist.py 1234567
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_artist(cm_id: str) -> dict:
"""Fetch artist profile from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/artist/{cm_id}",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {
"error": "Payment Required",
"message": "Your Chartmetric API subscription may be expired or this endpoint requires a higher tier. Check your account at chartmetric.com or contact hi@chartmetric.com"
}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_artist.py <chartmetric_id>")
sys.exit(1)
result = get_artist(sys.argv[1])
print(json.dumps(result, indent=2))
#!/usr/bin/env python3
"""
Get curator/playlist owner information.
Usage:
python get_curator.py <curator_id> [--platform spotify]
python get_curator.py 1
python get_curator.py 1 --platform spotify
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import argparse
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_curator(curator_id: str, platform: str = "spotify") -> dict:
"""Fetch curator info from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/curator/{platform}/{curator_id}",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 401:
return {"error": "Unauthorized", "message": "This endpoint may require a higher subscription tier."}
if response.status_code == 400:
return {"error": "Bad Request", "message": "Curator ID must be a numeric Chartmetric ID."}
if response.status_code == 404:
return {"error": "Curator not found", "curator_id": curator_id}
response.raise_for_status()
return response.json()
def main():
parser = argparse.ArgumentParser(description="Get curator information")
parser.add_argument("curator_id", help="Chartmetric curator ID (numeric)")
parser.add_argument("--platform", "-p", default="spotify",
choices=["spotify"],
help="Curator platform (currently only spotify supported)")
args = parser.parse_args()
result = get_curator(args.curator_id, args.platform)
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
curator = result.get("obj", {})
print(f"Curator: {curator.get('name', 'Unknown')}")
print(f" Chartmetric ID: {curator.get('id')}")
print(f" User ID: {curator.get('user_id')}")
if curator.get('image_url'):
print(f" Image: {curator.get('image_url')}")
if curator.get('num_playlists'):
print(f" Playlists: {curator.get('num_playlists')}")
if curator.get('followers'):
print(f" Followers: {curator.get('followers'):,}")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Get playlist metadata.
Usage:
python get_playlist.py <platform> <playlist_id>
python get_playlist.py spotify 37i9dQZF1DXcBWIGoYBM5M
python get_playlist.py apple pl.f4d106fed2bd41149aaacabb233eb5eb
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_playlist(platform: str, playlist_id: str) -> dict:
"""Fetch playlist metadata from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/playlist/{platform}/{playlist_id}",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Playlist not found", "platform": platform, "playlist_id": playlist_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python get_playlist.py <platform> <playlist_id>")
print("Platforms: spotify, apple, deezer, amazon")
sys.exit(1)
result = get_playlist(sys.argv[1], sys.argv[2])
print(json.dumps(result, indent=2))
#!/usr/bin/env python3
"""
Get related/similar artists based on streaming data.
Two modes:
1. Basic (default): Uses /relatedartists endpoint
2. Advanced (--by-config): Uses /similar-artists/by-configurations with filters
Usage:
# Basic related artists
python get_similar_artists.py <chartmetric_id> [--limit 10]
python get_similar_artists.py 3380
python get_similar_artists.py 3380 --limit 25
# Advanced with configuration filters
python get_similar_artists.py 2762 --by-config --audience high --genre high
python get_similar_artists.py 2762 --by-config --mood medium --musicality high
Configuration options (at least one required when using --by-config):
--audience: high, medium, low - Similarity of Audience
--mood: high, medium, low - Similarity of Mood
--genre: high, medium, low - Similarity of Genre
--musicality: high, medium, low - Similarity of Musicality
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import argparse
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_related_artists(cm_id: str, limit: int = 10) -> dict:
"""Fetch related artists from Chartmetric using basic endpoint."""
token = get_token()
response = requests.get(
f"{API_BASE}/artist/{cm_id}/relatedartists",
headers={"Authorization": f"Bearer {token}"},
params={"limit": limit}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 401:
return {"error": "Unauthorized", "message": "This endpoint may require authentication."}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
def get_similar_artists_by_config(
cm_id: str,
limit: int = 10,
offset: int = 0,
audience: str = None,
mood: str = None,
genre: str = None,
musicality: str = None
) -> dict:
"""
Fetch similar artists using advanced configuration filters.
At least one of audience, mood, genre, or musicality must be specified.
Valid values: 'high', 'medium', 'low'
"""
token = get_token()
params = {"limit": limit, "offset": offset}
if audience:
params["audience"] = audience
if mood:
params["mood"] = mood
if genre:
params["genre"] = genre
if musicality:
params["musicality"] = musicality
# Validate at least one config is set
if not any([audience, mood, genre, musicality]):
return {"error": "Invalid parameters", "message": "At least one of audience, mood, genre, or musicality is required."}
response = requests.get(
f"{API_BASE}/artist/{cm_id}/similar-artists/by-configurations",
headers={"Authorization": f"Bearer {token}"},
params=params
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 401:
return {"error": "Unauthorized", "message": "This endpoint may require authentication."}
if response.status_code == 404:
return {"error": "Artist not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
def main():
parser = argparse.ArgumentParser(description="Get related/similar artists")
parser.add_argument("chartmetric_id", help="Chartmetric artist ID")
parser.add_argument("--limit", "-l", type=int, default=10,
help="Number of related artists (default: 10)")
parser.add_argument("--offset", type=int, default=0,
help="Offset for pagination (default: 0)")
# Advanced configuration mode
parser.add_argument("--by-config", action="store_true",
help="Use advanced similar-artists/by-configurations endpoint")
parser.add_argument("--audience", choices=["high", "medium", "low"],
help="Similarity of audience (requires --by-config)")
parser.add_argument("--mood", choices=["high", "medium", "low"],
help="Similarity of mood (requires --by-config)")
parser.add_argument("--genre", choices=["high", "medium", "low"],
help="Similarity of genre (requires --by-config)")
parser.add_argument("--musicality", choices=["high", "medium", "low"],
help="Similarity of musicality (requires --by-config)")
args = parser.parse_args()
# Use advanced endpoint if --by-config or any config option is set
use_config = args.by_config or any([args.audience, args.mood, args.genre, args.musicality])
if use_config:
result = get_similar_artists_by_config(
args.chartmetric_id,
limit=args.limit,
offset=args.offset,
audience=args.audience,
mood=args.mood,
genre=args.genre,
musicality=args.musicality
)
else:
result = get_related_artists(args.chartmetric_id, args.limit)
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
sys.exit(1)
# Handle different response structures
if use_config:
obj = result.get("obj", {})
artists = obj.get("data", [])
total = obj.get("total", 0)
print(f"Similar Artists by Configuration ({len(artists)} shown, {total:,} total):\n")
else:
artists = result.get("obj", [])
print(f"Related Artists ({len(artists)} found):\n")
for artist in artists:
print(f"- {artist.get('name')}")
print(f" Chartmetric ID: {artist.get('id')}")
# Show similarity score if available (from by-config endpoint)
if artist.get('similarity') is not None:
print(f" Similarity: {artist.get('similarity'):.2f}")
# Show career stage if available
if artist.get('career_stage'):
print(f" Career Stage: {artist.get('career_stage')}")
# Show rank
rank = artist.get('rank') or artist.get('cm_artist_rank')
if rank:
print(f" CM Rank: {rank:,}")
# Show Spotify data
sp_followers = artist.get('sp_followers') or artist.get('spotify_followers')
if sp_followers:
print(f" Spotify Followers: {sp_followers:,}")
if artist.get('sp_monthly_listeners'):
print(f" Monthly Listeners: {artist.get('sp_monthly_listeners'):,}")
print()
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Get or refresh Chartmetric access token.
Token is cached in /tmp/chartmetric_token.json
Usage:
python get_token.py
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import os
import json
import time
import requests
from pathlib import Path
CACHE_FILE = Path("/tmp/chartmetric_token.json")
API_BASE = "https://api.chartmetric.com/api"
def get_token() -> str:
"""Get a valid access token, refreshing if needed."""
# Check cache
if CACHE_FILE.exists():
try:
cached = json.loads(CACHE_FILE.read_text())
if cached["expires_at"] > time.time() + 60:
return cached["access_token"]
except (json.JSONDecodeError, KeyError):
pass # Cache invalid, refresh
# Refresh token
refresh_token = os.environ.get("CHARTMETRIC_REFRESH_TOKEN")
if not refresh_token:
raise ValueError("CHARTMETRIC_REFRESH_TOKEN environment variable not set")
response = requests.post(
f"{API_BASE}/token",
json={"refreshtoken": refresh_token}
)
response.raise_for_status()
data = response.json()
# Cache it
cached = {
"access_token": data["token"],
"expires_at": time.time() + data.get("expires_in", 3600)
}
CACHE_FILE.write_text(json.dumps(cached))
return cached["access_token"]
if __name__ == "__main__":
token = get_token()
print(token)
#!/usr/bin/env python3
"""
Lookup track by Spotify ID or URL.
Usage:
python get_track_by_spotify.py <spotify_id>
python get_track_by_spotify.py 0VjIjW4GlUZAMYd2vXMi3b
python get_track_by_spotify.py "https://open.spotify.com/track/0VjIjW4GlUZAMYd2vXMi3b"
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import re
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def extract_spotify_id(url_or_id: str) -> str:
"""Extract Spotify track ID from URL or return as-is if already an ID."""
match = re.search(r"track[/:]([a-zA-Z0-9]+)", url_or_id)
if match:
return match.group(1)
return url_or_id
def get_track_by_spotify(spotify_id: str) -> dict:
"""Lookup Chartmetric track by Spotify ID."""
token = get_token()
response = requests.get(
f"{API_BASE}/track/spotify/{spotify_id}/get-ids",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Track not found in Chartmetric", "spotify_id": spotify_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_track_by_spotify.py <spotify_id_or_url>")
sys.exit(1)
spotify_id = extract_spotify_id(sys.argv[1])
result = get_track_by_spotify(spotify_id)
print(json.dumps(result, indent=2))
#!/usr/bin/env python3
"""
Get track metadata by Chartmetric ID.
Usage:
python get_track.py <chartmetric_id>
python get_track.py 128613854
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def get_track(cm_id: str) -> dict:
"""Fetch track metadata from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/track/{cm_id}",
headers={"Authorization": f"Bearer {token}"}
)
if response.status_code == 402:
return {"error": "Payment Required", "message": "Check your Chartmetric subscription."}
if response.status_code == 404:
return {"error": "Track not found", "chartmetric_id": cm_id}
response.raise_for_status()
return response.json()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python get_track.py <chartmetric_id>")
sys.exit(1)
result = get_track(sys.argv[1])
print(json.dumps(result, indent=2))
#!/usr/bin/env python3
"""
List music festivals.
Usage:
python list_festivals.py
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def list_festivals() -> dict:
"""Fetch festival list from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/festival/list",
headers={"Authorization": f"Bearer {token}"}
)
response.raise_for_status()
return response.json()
if __name__ == "__main__":
result = list_festivals()
festivals = result.get("obj", [])
print(f"Found {len(festivals)} festivals:\n")
for fest in festivals[:30]:
print(f"- {fest.get('name')}")
if fest.get('city'):
print(f" Location: {fest.get('city')}, {fest.get('country', '')}")
print()
#!/usr/bin/env python3
"""
List all Chartmetric genres.
Usage:
python list_genres.py
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import json
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def list_genres() -> dict:
"""Fetch all genres from Chartmetric."""
token = get_token()
response = requests.get(
f"{API_BASE}/genres",
headers={"Authorization": f"Bearer {token}"}
)
response.raise_for_status()
return response.json()
if __name__ == "__main__":
result = list_genres()
genres = result.get("obj", [])
print(f"Found {len(genres)} genres:\n")
for genre in genres[:50]:
print(f"- {genre.get('name')} (ID: {genre.get('id')})")
if len(genres) > 50:
print(f"\n... and {len(genres) - 50} more")
#!/usr/bin/env python3
"""
Search for an artist by name in Chartmetric.
Usage:
python search_artist.py "Artist Name"
python search_artist.py "Drake" --limit 10
Environment:
CHARTMETRIC_REFRESH_TOKEN - Your Chartmetric refresh token
"""
import sys
import json
import argparse
import requests
from get_token import get_token
API_BASE = "https://api.chartmetric.com/api"
def search_artist(name: str, limit: int = 5) -> dict:
"""Search for artists by name.
Uses the /search endpoint with type=artists.
"""
token = get_token()
response = requests.get(
f"{API_BASE}/search",
headers={"Authorization": f"Bearer {token}"},
params={"q": name, "type": "artists", "limit": limit}
)
if response.status_code == 402:
return {
"error": "Payment Required",
"message": "Your Chartmetric API subscription may be expired or this endpoint requires a higher tier. Check your account at chartmetric.com or contact hi@chartmetric.com"
}
response.raise_for_status()
return response.json()
def main():
parser = argparse.ArgumentParser(description="Search for an artist by name")
parser.add_argument("name", help="Artist name to search for")
parser.add_argument("--limit", "-l", type=int, default=5, help="Number of results")
parser.add_argument("--json", action="store_true", help="Output raw JSON")
args = parser.parse_args()
result = search_artist(args.name, args.limit)
if args.json:
print(json.dumps(result, indent=2))
return
# Check for error responses
if "error" in result:
print(f"Error: {result.get('error')}")
if "message" in result:
print(result.get('message'))
return
artists = result.get("obj", {}).get("artists", [])
if not artists:
print(f"No artists found for '{args.name}'")
return
print(f"Found {len(artists)} artists:\n")
for artist in artists:
print(f"- {artist.get('name')}")
print(f" Chartmetric ID: {artist.get('id')}")
if artist.get('spotify_id'):
print(f" Spotify ID: {artist.get('spotify_id')}")
print()
if __name__ == "__main__":
main()