stats.fm Skill
Query Spotify listening data through the stats.fm API. Personal stats, artist deep dives, discovery timelines, discographies, and global charts.
Script: scripts/statsfm.py (Python 3.6+, stdlib only)
Setup
Check memory for a stats.fm username. If you don't have one, ask — all personal commands need --user USERNAME (-u). Public commands (search, album, artist-albums, charts) work without a username.
How to Be Good at This
This skill is worthless if you call one command and dump the output. Music is personal. Your job is to investigate, find the story in the data, and tell it back. You're a music analyst with unlimited API calls — act like it.
Core principles
1. Never stop at one call. ALWAYS check recent first (7d or 4w). Lifetime alone is accumulation, not what's happening now. If the first result doesn't match what the user is saying, check another range before responding.
2. artist-history first for any single-artist question. The monthly breakdown shows when they blew up, when they faded, where they are right now. top artists gives you a rank. artist-history gives you the arc. Never answer a single-artist question with only top artists output.
3. Always check total streams as context. Raw play counts mean nothing without the denominator. An artist dropping from 1,560 to 937 plays looks like a 40% decline, but if total listening also dropped that month, their share barely moved. Run stream-stats for the same period before calling anything a decline or surge.
4. Go wide, then narrow. Always pull more context than you think you need — the monthly arc, the weekly zoom, the daily granularity, the surrounding artists, the track-level breakdown, the total stream context. You can always ignore what's not interesting — you can't find what you didn't pull. When you spot something interesting (a spike, a gap, a transition), zoom in immediately with daily granularity.
5. First play ≠ fandom. The first time someone plays an artist means nothing — it might be a smash hit everyone heard. The real question is always: what happened between the first casual listen and the obsession? Investigate the gap. Find the bridge song. Don't stop at "September 8 was the first play" when the real conversion happened 5 months later.
Note: The top command is unified — use top artists, top tracks, top albums, top genres. For drill-downs, use top tracks --from-artist ID or top tracks --from-album ID. Charts are also unified: charts tracks, charts artists, charts albums.
How to investigate
When someone asks about an artist, a phase, or a discovery moment, think like an investigator:
Start broad: artist-history (defaults to lifetime) gives the monthly arc. Where are the spikes? Where are the gaps? Where did it start, peak, and (if applicable) decline?
Zoom into transitions: The interesting story is always at the inflection points — the week before an explosion, the month an artist went from casual to obsessive, the period where two artists overlapped. Use --granularity daily on these windows.
Get the full context: What else was playing that day? (top artists, top tracks for the same date range.) What tracks appeared as breadcrumbs before the explosion? (top tracks --from-artist for the pre-explosion period.) How big was the total pie? (stream-stats for the same period.)
Track the breadcrumbs: Artists don't go from 0 to obsession overnight. There's usually a gateway track, then a second song from a different album, then a third that triggers the deep dive. Map these out with top tracks --from-artist across the transition period.
Calculate share when comparing periods. Artist plays ÷ total streams = share. Share changes tell you whether someone's listening habits actually shifted or whether total volume just fluctuated.
Workflow patterns
"Tell me about my [artist] phase" — the deep dive
- 1.
artist-history lifetime → find the arc (start, peak, current) - INLINECODE26 this week and this month → where are they right now?
- INLINECODE27 with weekly granularity on the hot period → zoom in on the peak
- INLINECODE28 lifetime → which songs define the phase
- INLINECODE29 this month → which songs are active now vs. then?
- INLINECODE30 → album-level view
- INLINECODE31 for peak month and current month → total context and share comparison
- INLINECODE32 for peak month → who else was competing for attention?
Goal: When did this start, what peaked, what's the signature track, who else was in the picture, is it still going or fading?
"When did I discover [artist]?" — the origin story
- 1.
artist-history lifetime → find first appearance AND explosion month - INLINECODE34 daily granularity on the transition period → find the exact conversion day
- INLINECODE35 for pre-explosion period → what tracks were breadcrumbs
- INLINECODE36 for explosion day/week → what track triggered it
- INLINECODE37 this week → what are they playing now vs. the origin?
- INLINECODE38 for the first day → what world were they listening in when this artist appeared?
- INLINECODE39 for the explosion day → full picture of the conversion moment
- INLINECODE40 on the gateway track lifetime → how did it spread from there?
- INLINECODE41 for the transition month → total listening context
Goal: Find the gateway track, the bridge track, the conversion moment, and what triggered the deep dive. The gap between first listen and obsession IS the story.
"What's my [artist] breakdown look like this year?" — the status check
- 1.
artist-history for this year → monthly totals - INLINECODE43 this week and this month → current trajectory
- INLINECODE44 for this year → current favorites
- INLINECODE45 this week → what's actually playing right now?
- INLINECODE46 lifetime → compare to history
- INLINECODE47 for this month and same month last year → share comparison across time
Goal: Where does this year rank vs. history? Is the artist's share growing, stable, or shrinking?
"How do I listen to [album]?" — the album autopsy
- 1.
album → full tracklist - INLINECODE49 lifetime → total plays and arc
- INLINECODE50 this month → is it still active?
- INLINECODE51 lifetime → all-time track ranking
- INLINECODE52 this month → has the favorite track shifted?
Goal: Which tracks carry the album? Front-to-back or cherry-pick? Still active or nostalgia?
"What have I been into lately?" — the snapshot
- 1.
top artists this week → right now - INLINECODE54 this month → broader view
- INLINECODE55 lifetime → for comparison only
- INLINECODE56 → anchor to what's playing right now
- INLINECODE57 this week and this month → total volume context and trend
Goal: Paint the current moment. What's dominating? What's surprising? Who's rising, who's falling?
Voice and tone
- - Be specific. "You've averaged 4 plays a day of this track for two weeks straight" tells a story. "You really like this artist" tells nothing.
- Notice patterns. Spikes, drop-offs, seasonal rhythms, transitions — call them out.
- Numbers are scaffolding. Don't list every month. Pick the interesting ones and weave them into observations.
- Compare things. A number alone means nothing. 200 plays means different things depending on whether total streams that month were 2,000 or 5,000. Always contextualize.
- Editorialize lightly. You're having a music conversation, not filing a report.
- Don't narrate your process. Never say "I'll now run artist-history." Just do it.
Time Range Translations
| User says | You use |
|---|
| "this year" / "in 2025" | INLINECODE58 |
| "last year" |
--start 2024 --end 2025 |
| "this month" |
--start 2025-03 --end 2025-04 (adjust to current month) |
| "last summer" |
--start 2025-06 --end 2025-09 |
| "lately" / "recently" |
--range 30d (and maybe compare to
--range all) |
| "ever" / "all time" |
--range all |
| "this week" |
--range 7d |
| "when did I start" |
--range all then read the monthly breakdown |
Edge Cases
- - Empty results? Retry with
--range all automatically. If still empty, the profile might be private. - Free (non-Plus) users: Play counts won't appear in top lists. Rankings and monthly breakdowns still work — lead with those.
- Rate limiting: Don't hold back. Deep dives take as many calls as they take. That's what this skill is for.
- Search duplicates: Use the first result unless something looks obviously wrong.
- No username in memory: Ask once, remember it.
CLI Reference
Everything below is command-level documentation. The workflows above are how you should use these — this section is for looking up flags and syntax when you need them.
Command Syntax
All commands: INLINECODE68
Global flags for all personal commands: --user USERNAME / INLINECODE70
Commands
Profile & Activity
| Command | Description |
|---|
| INLINECODE71 | Username, pronouns, bio, Plus status, Spotify sync info |
| INLINECODE72 / INLINECODE73 |
Currently playing track |
|
recent | Recently played tracks (includes now-playing at top if active) |
|
stream-stats | Overall summary: total streams, time, averages, unique counts |
Your Top Lists
| Command | Description | Key flags |
|---|
| INLINECODE76 | Most played artists | INLINECODE77 , --start/--end, INLINECODE79 |
| INLINECODE80 |
Most played tracks |
--range,
--start/--end,
--limit |
|
top albums | Most played albums |
--range,
--start/--end,
--limit |
|
top genres | Top genres |
--range,
--start/--end,
--limit |
History (with breakdowns)
| Command | Description | Key flags |
|---|
| INLINECODE92 | Play count, time, breakdown for an artist | INLINECODE93 , INLINECODE94 |
| INLINECODE95 |
Play count, time, breakdown for a track |
--start/--end,
--granularity |
|
album-history <id> | Play count, time, breakdown for an album |
--start/--end,
--granularity |
|
listening-history | Total listening breakdown over time |
--start/--end,
--granularity |
Note: History commands do NOT support --range. Use --start/--end for custom windows, or omit both to get lifetime. --range produces partial-month data at boundaries that looks misleadingly low.
Lookups (no account needed)
| Command | Description | Key flags |
|---|
| INLINECODE107 | Find artists, tracks, or albums | INLINECODE108 |
| INLINECODE109 |
Artist info, genres, popularity, discography |
--type album\|single\|all,
--limit |
|
track <id> | Track info: name, all artists (with IDs), album (with ID), duration | |
|
album <id> | Album info and full tracklist | |
|
artist-albums <id> | Discography grouped by type, newest first |
--type album\|single\|all,
--limit |
Drill-Down (your stats within an artist/album)
| Command | Description | Key flags |
|---|
| INLINECODE117 | Your most played tracks by this artist | INLINECODE118 , INLINECODE119 |
| INLINECODE120 |
Your most played tracks on this album |
--range,
--limit |
|
top albums --from-artist ID | Your most played albums by this artist |
--range,
--limit |
Discovery
| Command | Description | Key flags |
|---|
| INLINECODE126 / INLINECODE127 | Show first N streams for an artist, track, or album | INLINECODE128 , INLINECODE129 |
Global Charts (no account needed)
| Command | Description | Key flags |
|---|
| INLINECODE130 | Global top tracks | INLINECODE131 , INLINECODE132 |
| INLINECODE133 |
Global top artists |
--range,
--limit |
|
charts albums | Global top albums |
--range,
--limit |
Date Range Flags
Predefined: --range today, 1d, 4w (default), 6m, INLINECODE143
Duration: --range 7d, 14d, 30d, INLINECODE147
Custom: --start YYYY[-MM[-DD]] and INLINECODE149
Granularity
INLINECODE150 (default) | weekly | INLINECODE152
Works with artist-history, track-history, album-history.
Other Flags
| Flag | Description |
|---|
| INLINECODE156 / INLINECODE157 | Limit results (default: 15) |
Finding IDs
CODEBLOCK0
Error Handling
| Scenario | Output | Auto-fix |
|---|
| No user set | INLINECODE158 | Ask for username, store in memory |
| API error (4xx/5xx) |
API Error (code): message | Check if profile is public, ID is valid |
| Empty results | No output | Retry with
--range all |
| Plus-only data |
[Plus required] inline | Work with what's available, don't dwell on it |
API Info
- - Base URL: INLINECODE162
- Auth: None for public profiles
- Response format: JSON with
item/items wrapper
References
stats.fm 技能
通过 stats.fm API 查询 Spotify 收听数据。个人统计、艺术家深度挖掘、发现时间线、作品目录和全球排行榜。
脚本: scripts/statsfm.py(Python 3.6+,仅标准库)
设置
检查记忆中是否有 stats.fm 用户名。如果没有,请询问——所有个人命令都需要 --user USERNAME(-u)。公共命令(搜索、专辑、艺术家专辑、排行榜)无需用户名即可使用。
如何精通此技能
如果你只调用一个命令并输出结果,这个技能就毫无价值。音乐是个人化的。你的工作是调查,在数据中寻找故事,并讲述出来。你是一位拥有无限 API 调用次数的音乐分析师——请拿出专业态度。
核心原则
1. 绝不止步于一次调用。 始终先检查近期数据(7天或4周)。仅看终身数据只是积累,不代表当前状况。如果第一个结果与用户所说的不符,在回复前检查另一个时间范围。
2. 任何关于单一艺术家的问题,先使用 artist-history。 月度细分显示他们何时爆发、何时消退、现在处于什么位置。top artists 给你一个排名。artist-history 给你整个轨迹。永远不要只用 top artists 的输出来回答单一艺术家的问题。
3. 始终将总播放量作为背景参考。 没有分母,原始播放次数毫无意义。一个艺术家从 1,560 次播放下降到 937 次,看起来下降了 40%,但如果那个月的总收听量也下降了,他们的份额几乎没有变化。在判断任何下降或激增之前,先运行同一时期的 stream-stats。
4. 先广泛,再深入。 始终获取比你认为需要的更多的背景信息——月度轨迹、每周缩放、每日粒度、周边艺术家、曲目级细分、总播放量背景。你总是可以忽略不感兴趣的内容——但你无法找到你没有获取的数据。当你发现有趣的内容(峰值、缺口、过渡)时,立即用每日粒度放大查看。
5. 首次播放 ≠ 成为粉丝。 某人第一次播放一位艺术家并不意味着什么——这可能是一首人人都听过的热门歌曲。真正的问题永远是:从第一次随意听到痴迷之间发生了什么?调查这个缺口。找到那首桥梁歌曲。不要停留在9月8日是首次播放,而真正的转变发生在5个月后。
注意: top 命令是统一的——使用 top artists、top tracks、top albums、top genres。进行深入分析时,使用 top tracks --from-artist ID 或 top tracks --from-album ID。排行榜也是统一的:charts tracks、charts artists、charts albums。
如何调查
当有人问起一位艺术家、一个阶段或一个发现时刻时,像调查员一样思考:
从广泛开始: artist-history(默认为终身)给出月度轨迹。峰值在哪里?缺口在哪里?从哪里开始、达到峰值以及(如果适用)下降?
放大到过渡期: 有趣的故事总是在转折点——爆发前的一周、艺术家从随意听到痴迷的月份、两位艺术家重叠的时期。在这些时间窗口使用 --granularity daily。
获取完整背景: 那天还在播放什么?(同一日期范围的 top artists、top tracks。)在爆发前有哪些曲目作为线索出现?(爆发前时期的 top tracks --from-artist。)总播放量有多大?(同一时期的 stream-stats。)
追踪线索: 艺术家不会一夜之间从0到痴迷。通常有一首入门曲目,然后是另一张专辑的第二首歌,然后是触发深入探索的第三首歌。用过渡时期的 top tracks --from-artist 来绘制这些。
在比较时期时计算份额。 艺术家播放量 ÷ 总播放量 = 份额。份额变化告诉你某人的收听习惯是否真的改变了,还是只是总播放量波动了。
工作流程模式
告诉我关于我的[艺术家]阶段 —— 深度挖掘
- 1. artist-history 终身 → 找到轨迹(开始、峰值、当前)
- artist-history 本周和本月 → 他们现在在哪里?
- artist-history 以周粒度查看热门时期 → 放大峰值
- top tracks --from-artist 终身 → 哪些歌曲定义了该阶段
- top tracks --from-artist 本月 → 现在活跃的歌曲与当时相比?
- top albums --from-artist → 专辑级别视图
- stream-stats 峰值月份和当前月份 → 总背景和份额比较
- top artists 峰值月份 → 还有谁在争夺注意力?
目标:这什么时候开始的?什么达到了峰值?标志性曲目是什么?还有谁参与其中?它还在继续还是正在消退?
我是什么时候发现[艺术家]的? —— 起源故事
- 1. artist-history 终身 → 找到首次出现和爆发月份
- artist-history 以日粒度查看过渡期 → 找到确切的转变日期
- top tracks --from-artist 爆发前时期 → 哪些曲目是线索
- top tracks --from-artist 爆发日/周 → 哪首曲目触发了它
- top tracks --from-artist 本周 → 现在播放的与起源相比?
- top artists 第一天 → 这位艺术家出现时,他们在什么收听世界中?
- top tracks 爆发日 → 转变时刻的完整画面
- track-history 入门曲目终身 → 它如何从那里传播?
- stream-stats 过渡月份 → 总收听背景
目标:找到入门曲目、桥梁曲目、转变时刻以及触发深度探索的原因。首次听到和痴迷之间的差距就是故事本身。
我今年对[艺术家]的收听情况如何? —— 状态检查
- 1. artist-history 今年 → 月度总计
- artist-history 本周和本月 → 当前趋势
- top tracks --from-artist 今年 → 当前最爱
- top tracks --from-artist 本周 → 现在实际在播放什么?
- artist-history 终身 → 与历史比较
- stream-stats 本月和去年同月 → 跨时间份额比较
目标:今年与历史相比排名如何?艺术家的份额在增长、稳定还是缩小?
我是怎么听[专辑]的? —— 专辑剖析
- 1. album → 完整曲目列表
- album-history 终身 → 总播放量和轨迹
- album-history 本月 → 它还在活跃吗?
- top tracks --from-album 终身 → 历史曲目排名
- top tracks --from-album 本月 → 最爱曲目改变了吗?
目标:哪些曲目支撑了这张专辑?从头到尾听还是精选?还在活跃还是怀旧?
我最近在听什么? —— 快照
- 1. top artists 本周 → 现在
- top artists 本月 → 更广泛的视图
- top artists 终身 → 仅用于比较
- now-playing → 锚定当前正在播放的内容
- stream-stats 本周和本月 → 总播放量背景和趋势
目标:描绘当前时刻。什么占主导?什么令人惊讶?谁在上升,谁在下降?
语气和风格
- - 要具体。 你连续两周平均每天播放这首曲目4次讲述了一个故事。你真的很喜欢这位艺术家什么也没说。
- 注意模式。 峰值、下降、季节性节奏、过渡——指出它们。
- 数字是框架。 不要列出每个月。挑选有趣的数字,并将它们融入观察中。
- 进行比较。 单独的数字毫无意义。200次播放的意义取决于那个月的总播放量是2,000还是5,000。始终提供背景。
- 适度评论。 你是在进行音乐对话,而不是提交报告。
- 不要叙述你的过程。 永远不要说我现在要运行 artist-history。直接去做。
时间范围转换
| 用户说 | 你使用 |
|---|
| 今年 / 在2025年 | --start 2025 --end 2026 |
| 去年 |
--start 2024 --end 2025 |
| 这个月 | --start 2025-03 --end 2025-04(调整为当前月份) |
| 去年夏天 | --start 2025-06 --end 2025-09 |
| 最近 | --range 30d(并可能与 --range all 比较) |
| 一直以来 / 所有时间 | --range all |
| 这周 | --range 7d |
| 我什么时候开始 |