Tuneping API v1
一个 JSON API,提供网络电台此刻正在播放的内容,以及它们播放过的内容。Base URL 为 https://api.tuneping.com/v1。只有元数据:服务读取每个流公开的标题行,从不触碰音频。
快速入门
此刻谁在播放 Madonna?
curl "https://api.tuneping.com/v1/now-playing/search?q=madonna&limit=3"
查询一个已知 id 的电台正在播放什么(id 为 Radio Browser 的电台 UUID):
curl "https://api.tuneping.com/v1/stations/159824f0-5475-4263-bcc8-59a48a213896/now-playing"
某个电台在过去 24 小时内播放了什么(需要具有历史记录权限的密钥,Developer 套餐或以上):
curl -H "Authorization: Bearer tp_live_…" \
"https://api.tuneping.com/v1/stations/159824f0-5475-4263-bcc8-59a48a213896/history"
所有响应均为 JSON。错误为 application/problem+json(见错误)。未知的查询参数会以 400 拒绝,而不是被忽略,因此拼写错误会立即暴露。
获取密钥
在 tuneping.com/signup 自助获取:输入电子邮件地址,在 60 分钟内打开邮件中的链接,密钥会显示一次。这两个步骤同样以 API 提供:POST /v1/signup 携带 {"email": "…", "plan": "free|developer|pro"},返回 202 并发送邮件;POST /v1/signup/verify 携带链接中的 {"token": "…"},返回 201 和密钥。Free 无需其他步骤。Developer 和 Pro 通过响应中的链接继续前往 Stripe Checkout;付款后一分钟内,所有者的套餐即会变更,该所有者的所有密钥随之变更。
密钥丢失了?用同一地址重新注册:密钥会被轮换,旧密钥在 24 小时内仍可使用。Free 和 Developer 一个正式密钥,Pro 五个,Business 二十个;此外每个套餐都有一个 tp_test_ 密钥,在管理门户上线之前按申请签发。
身份验证
以 Bearer 令牌形式发送密钥,或放在 X-Api-Key 中:
Authorization: Bearer tp_live_a1b2c3…
X-Api-Key: tp_live_a1b2c3…
密钥以 tp_live_ 或 tp_test_ 开头,后跟 43 个字符。不带密钥的请求视为匿名请求,按 IP 地址限流。格式错误、无法识别或已吊销的密钥会立即返回 401,即使该端点在匿名情况下本可正常使用。下文标有 密钥 的端点,在不带密钥时返回 401 unauthenticated。
作用域。除非签发时指定了更窄的列表(例如只读),密钥默认拥有全部作用域(read、exports、webhooks、wishlists);GET /v1/me 会列出密钥的作用域。心愿单和 Webhook 分别需要 wishlists 和 webhooks 作用域(使用 webhook 投递的心愿单两者都需要,且先于套餐检查)。缺少作用域的密钥返回 403 forbidden_scope 并附带 required_scope,WWW-Authenticate 携带 error="insufficient_scope"。
测试密钥。tp_test_ 密钥读取同样的实时数据,适用 Free 的限制,并拥有独立的心愿单、匹配记录和 Webhook 端点(livemode: false)。它永远看不到也无法更改正式环境的数据,因此测试套件可以 PUT 一个空的心愿单集合而不影响生产环境。测试模式的 Webhook 只能指向 webhook.site。
重试。在需要密钥或合作方令牌的端点上,POST、PATCH 和 DELETE 接受 Idempotency-Key 请求头(每个逻辑请求一个 UUID);注册端点不接受它。使用相同的键和相同的请求重试时,会再次得到第一次成功的响应,并标记 Idempotent-Replayed: true,而不会创建第二个心愿单或端点。该键会保留 24 小时;将它用于不同的请求会返回 422 idempotency_key_reused,在第一个请求仍在执行时重试会返回 409 idempotency_in_progress。错误响应不会被保留,因此出错后的重试会重新执行。连接断开不会取消带有该请求头的请求:请求会执行完毕,成功的响应会被保留,你的重试会得到它。执行超过 20 秒的请求会被取消且不做任何更改,返回 503 unavailable 并附带 Retry-After: 1;请用相同的键重试。
合作方应用(目前为 Tapelark)发送签名的授权令牌而不是密钥:Authorization: Bearer TPE1-…。令牌会依据合作方的 Ed25519 密钥离线验证,并对应到下表中的合作方套餐;API 永远不知道最终用户是谁。格式错误或无法识别的令牌返回 401 token_invalid,过期的令牌返回 401 token_expired(请刷新),面向其他受众的令牌返回 401 token_audience,失效或已吊销的授权返回 403 entitlement_revoked。
速率限制与配额
| 套餐 | 速率 | 配额 | 批量 id 数 | 历史 |
|---|---|---|---|---|
| 匿名(按 IP) | 2 req/s,突发 5 | 1,000 / 天 | 20 | 无 |
| Free | 2 req/s,突发 5 | 1,000 / 天 | 20 | 无 |
| Developer | 10 req/s,突发 20 | 100,000 / 月 | 100 | 7 天 |
| Pro | 50 req/s,突发 100 | 1,000,000 / 月 | 100 | 12 个月 |
| Business | 定制 | 定制 | 100 | 12 个月 |
| Tapelark(授权令牌,按许可证) | 5 req/s,突发 10 | 2,000 / 天 | 50 | 7 天 |
每秒限制采用令牌桶算法。桶空时会返回 429 rate_limited 并附带 Retry-After。配额用完时会返回 429 quota_exceeded,Retry-After 设为距离窗口重置的秒数。
不可共享缓存的已认证响应会携带 IETF 速率限制字段:
RateLimit-Policy: "burst";q=10;w=1, "monthly";q=100000;w=2592000
RateLimit: "burst";r=7;t=1
可共享缓存的响应(搜索、批量、单个电台正在播放、电台、统计、健康序列)不携带速率限制响应头,因为边缘节点可能把同一个响应体提供给其他调用方。你的剩余额度随时可通过 GET /v1/me 查看。
缓存
实时响应可缓存 5 秒(Cache-Control: public, max-age=5, s-maxage=5),并携带弱 ETag。发送 If-None-Match,在内容未变化时会得到 304;304 在源站不计入你的配额。电台目录缓存 60 秒,单个电台缓存 300 秒。历史记录为 private, max-age=30。健康状况和密钥相关端点为 no-store。
每个响应都携带 X-Request-Id。联系支持时请提供它。
错误
错误遵循 RFC 9457 问题详情格式,并带有两类扩展成员:code,一个稳定的机器可读字符串;以及各端点特有的附加字段,例如验证失败时的 errors[] 或 403 时的 required_tier。
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{
"type": "https://tuneping.com/docs/errors/#validation_failed",
"title": "Validation failed",
"status": 422,
"detail": "One or more query parameters are invalid.",
"instance": "urn:tuneping:request:req_01a18837c239727fbacb40d6c488405a",
"code": "validation_failed",
"errors": [ { "field": "q", "code": "length", "message": "`q` is required and must be 2–120 characters." } ]
}
完整的错误码及其含义见错误目录。你最先会遇到的是这些:
| HTTP | code | 何时出现 |
|---|---|---|
| 400 | invalid_request | 未知的查询参数或格式错误的请求 |
| 400 | invalid_cursor | 游标格式错误、已过期(24 h;匹配记录流的 since_cursor 为 30 天),或与不同的筛选条件一起使用 |
| 401 | unauthenticated · invalid_api_key · key_revoked | 密钥缺失、无法识别或已吊销 |
| 403 | forbidden_tier | 端点或参数需要更高的套餐;required_tier 给出所需套餐 |
| 403 | forbidden_scope | 密钥签发时未包含该作用域;required_scope 给出所需作用域 |
| 404 | not_found · station_opted_out | 未知的 id,或该电台已要求不被收录 |
| 422 | validation_failed | 字段错误在 errors[] 中 |
| 429 | rate_limited · quota_exceeded | 请放慢速度;Retry-After 以秒为单位 |
| 503 | unavailable | 数据库不可用,或带 Idempotency-Key 的请求在 20 秒后被取消;请在 Retry-After 之后重试 |
GET/v1/now-playing/search 无需密钥 public, max-age=5 · ETag
实时搜索
哪些电台此刻正在播放 q。支持模糊匹配,不区分重音符号:gronemeyer manner 能找到 Grönemeyer – Männer。结果按相似度排序,其次按标题出现的时间由近及远排序。不分页:索引每隔几秒就会变化,分页会导致跳过或重复。请改为调大 limit。
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
q | 字符串,2–120 个字符 | 必填 | 艺人、标题或两者。三个及以上字符使用三元组(trigram)匹配;更短的查询为子串匹配。 |
country | ISO 3166-1 alpha-2,可重复 | 任意 | 只返回这些国家的电台。 |
codec | 字符串 | 任意 | 例如 MP3、AAC。 |
min_bitrate | 整数 | 0 | kbit/s。 |
classification | 枚举 | song | song、ident、programme、ad_break、empty、junk、unknown 或 any。 |
max_age_seconds | 30–900 | 180 | 只返回其电台在这么多秒内被成功轮询过的结果。 |
limit | 1–100 | 20 | 返回的行数;truncated: true 表示还有更多。 |
响应 · 200 · application/json
{
"data": [
{
"station": { "id": "159824f0-5475-4263-bcc8-59a48a213896", "name": "Radio 10 80's Hits", "country": "NL",
"codec": "MP3", "bitrate": 128, "is_hls": false,
"stream_url": "http://…/TLPSTR20.mp3", "homepage": "http://www.radio10.nl/", "recordability": "recordable" },
"raw_title": "Madonna - Holiday",
"artist": "Madonna", "title": "Holiday",
"track": { "id": "trk_c6f", "mbid": "d4708b79-62e7-4ce4-a6ca-247237d4ef4e", "artist_id": "art_fn" },
"classification": "song", "match_grade": "high", "confidence": "high",
"started_at": "2026-10-29T15:08:44.883Z", "start_uncertainty_seconds": 32,
"observed_at": "2026-10-29T15:09:26.631Z", "confirmed_at": "2026-10-29T15:10:30.679Z",
"method": "icy", "poll_interval_seconds": 60, "similarity": 0.5
}
],
"truncated": false,
"query": { "q": "madonna", "normalized": "madonna", "max_age_seconds": 180 }
}
解读一行结果
- started_at RFC 3339 · 估算值
- 当前标题的估算开始时间,前后误差为 ±
start_uncertainty_seconds。 - observed_at RFC 3339
- Tuneping 首次看到该标题的时间。
- confirmed_at RFC 3339
- 该电台最近一次成功轮询的时间:“截至此时仍在播放”。用它判断数据是否新鲜。
- track 对象 · 可选
- 标题已关联到某个 MusicBrainz 录音时才会出现。
- similarity 0–1
- 按你自己的阈值截断。
GET/v1/now-playing 无需密钥 public, max-age=5 · ETag
批量正在播放
一次调用获取一组电台的当前标题,适用于收藏页面。
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
station_ids | 逗号分隔的 UUID | 必填 | 匿名或 Free 最多 20 个,Developer 起最多 100 个。id 在缓存前会排序并去重,因此收藏相同的两个客户端共用同一个缓存响应体。 |
max_age_seconds | 30–86400 | 900 | 早于此值的行仍会返回,但标记为 is_stale: true。 |
{ "data": [ { …same row shape as search, without similarity, plus "is_stale": false… } ],
"missing": [ "ids that are unknown or opted out" ],
"max_age_seconds": 900 }
GET/v1/stations/{id}/now-playing 无需密钥
单个电台正在播放
一个电台的一行结果。尚未观察到标题时返回 404 not_found,电台已要求不被收录时返回 404 station_opted_out。
GET/v1/stations GET/v1/stations/{id} 无需密钥 max-age=60 · 300
电台
电台目录,基于社区维护的 Radio Browser 目录构建,并补充了 Tuneping 读取每个电台的方式。
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
q | 字符串 | — | 电台名称,模糊匹配。 |
country | alpha-2,可重复 | 任意 | |
tag | 字符串,可重复 | 任意 | 必须匹配所有给定的标签。 |
codec, min_bitrate | 同搜索。 | ||
has_now_playing | 布尔值 | false | 只返回 Tuneping 当前能读取到标题的电台。 |
sort | 枚举 | clickcount | clickcount(热度,降序)、name、bitrate。 |
limit | 1–200 | 50 | |
cursor | 不透明字符串 | — | 取自上一页的 next_cursor;有效期 24 小时,且只能与相同的筛选条件一起使用。 |
{ "data": [ {
"id": "159824f0-5475-4263-bcc8-59a48a213896", "name": "Radio 10 80's Hits", "country": "NL", "state": null,
"language": "dutch", "tags": ["80s", "hits"], "codec": "MP3", "bitrate": 128, "is_hls": false,
"stream_url": "http://…", "homepage": "http://www.radio10.nl/", "favicon_url": "https://…/favicon.png",
"clickcount": 1234, "recordability": "recordable",
"metadata": { "method": "icy", "poll_interval_seconds": 60, "lag_seconds": 9, "last_ok_at": "2026-10-29T15:10:30Z", "coverage": "live" },
"created_at": "2026-10-29T10:12:03Z", "updated_at": "2026-10-29T15:10:30Z" } ],
"next_cursor": "eyJ…" }
favicon_url 以文本形式从目录原样传递;Tuneping 从不复制或提供台标。
GET/v1/stations/{id}/history 密钥 · developer + private, max-age=30
播放历史
某个电台播放过的歌曲,最新的在前,附带估算的开始和结束时间。可查询的深度取决于套餐:Developer 为 7 天,Pro 和 Business 为 12 个月。请求更早的 from 会返回 403 forbidden_tier 并附带 required_tier,绝不会悄悄截断时间窗口。
| 参数 | 类型 | 默认值 | 含义 |
|---|---|---|---|
from, to | RFC 3339 | to = 当前时间,from = to − 24 h | 每次调用最多 31 天。 |
classification | 枚举 | song | 同搜索。 |
limit | 1–200 | 50 | |
cursor | 不透明字符串 | — | 时间窗口固定在游标中。 |
{ "data": [ {
"id": "pe_…", "station": { …station ref… },
"raw_title": "Hecht - Charlotta", "artist": "Hecht", "title": "Charlotta",
"track": { "id": "trk_…", "mbid": "…", "artist_id": "art_…" },
"classification": "song", "match_grade": "high",
"started_at": "2026-10-29T10:31:12Z", "start_uncertainty_seconds": 10,
"ended_at": "2026-10-29T10:34:40Z", "duration_seconds": 208,
"method": "icecast_json", "confidence": "high" } ],
"next_cursor": null,
"window": { "from": "2026-10-28T15:00:00Z", "to": "2026-10-29T15:00:00Z" } }
GET/v1/me GET/v1/me/usage 密钥 no-store
密钥与用量
/v1/me 返回密钥、其所有者的套餐和状态、适用的限制,以及当前窗口内到目前为止的用量。/v1/me/usage?from=&to= 返回每天的请求数(只统计源站请求;边缘缓存命中不计入)。
{ "key": { "id": "key_…", "hint": "tp_live_a1b2c3…", "scopes": ["read", "exports", "webhooks", "wishlists"], "livemode": true,
"created_at": "…", "last_used_at": "…" },
"owner": { "id": "own_…", "tier": "developer", "status": "active",
"current_period_start": "…", "current_period_end": "…" },
"limits": { "requests_per_second": 10, "burst": 20, "requests_per_day": null, "requests_per_month": 100000,
"history_days": 7, "batch_station_ids": 100, "webhooks": 0, "wishlists": 10,
"sse_connections": 1, "tightened_stations": 200 },
"usage": { "period_requests": 41230, "today_requests": 1810, "resets_at": "…" } }
GET/v1/health GET/v1/health/series GET/v1/stats 无需密钥
健康状况与统计
/v1/health 返回 200,status 为 ok 或 degraded;数据库宕机时返回 503。可以把你的可用性监控指向它。/v1/health/series?minutes=60(5–1440)返回每分钟的轮询器指标:polls、ok、titles_changed、songs、bytes_in、bytes_out、latency_p50_ms、latency_p90_ms、inflight_max、ips_paused、queue_lag_seconds,以及 by_method 细分。/v1/stats 是状态页上展示的公开覆盖率快照:电台总数、已轮询数、有实时元数据的电台数、每分钟标题变化数、歌曲占比、按方式细分、按国家细分。
POST/v1/wishlists PUT/v1/wishlists GET/v1/wishlists/matches 密钥 · developer + no-store
心愿单
指定一首歌、一位艺人或一段自由文本标题,在某个电台开始播放时得到通知。每次标题变化在被读取时都会与你的心愿单进行比对,新的心愿单在 30 秒内生效。Developer 可保存 10 个心愿单,Pro 100 个,Business 1,000 个。匹配记录保留 30 天。
| 字段 | 类型 | 含义 |
|---|---|---|
kind | artist · track · query | artist 和 track 接受来自搜索结果的 id(art_…、trk_…),也会匹配规范化程序关联到该 id 的其他拼写。query 接受自由文本,通常为 Artist - Title。 |
value | 字符串,≤ 240 | id 或文本。 |
match | exact · fuzzy | 仅用于 query。exact 比较规范化后的文本;fuzzy 接受三元组相似度 0.6 及以上。 |
station_filter | 对象 | station_ids(≤ 500)和/或 countries(alpha-2,大小写均可,返回时为大写)。标题所属电台在列表中,或位于列出的国家时即为匹配。不设筛选表示所有电台。 |
delivery | 数组 | poll(默认)、sse、webhook。Webhook 投递需要 Pro 及以上套餐,并需要 webhook_id 以及具有 webhooks 作用域的密钥。 |
description | 字符串,≤ 200 | 由你自定。 |
curl -H "Authorization: Bearer tp_live_…" -H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"kind":"query","value":"Daft Punk - One More Time","station_filter":{"countries":["FR","BE"]}}' \
https://api.tuneping.com/v1/wishlists
显式给出 station_ids 列表时,在心愿单启用期间,Tuneping 会把这些电台的读取间隔从 60 秒缩短为 30 秒,响应中会以 tightens: true 注明。你列出的电台的并集按套餐设有上限(/v1/me 中的 limits.tightened_stations);会超出上限的心愿单返回 422 validation_failed,其 errors[] 中有一项的 code 为 tightened_stations_exceeded(字段为 station_filter.station_ids,在 PUT 时为 data)。国家筛选照常匹配,按各电台的正常间隔读取。
GET /v1/wishlists/{id}、PATCH(电台筛选、匹配方式、投递方式、Webhook、描述、status: enabled | paused)和 DELETE 的行为符合预期。PUT /v1/wishlists 携带 {"data": [ … ]} 可一次替换你的整个集合:未变化的条目保留其 id 和匹配计数,其余条目被创建或删除。这是同步应用本地维护的列表最简单的方式。它的请求体最大可为 1 MiB;其他请求体限制为 64 KiB(413 payload_too_large)。
匹配记录是一个追加式记录流。GET /v1/wishlists/matches 涵盖你的所有心愿单,GET /v1/wishlists/{id}/matches 只涵盖其中一个。结果按从旧到新排列。保存每次响应中的 next_cursor,并在下次请求时作为 since_cursor 发回:它永远不会为 null,在 30 天保留期内一直有效,服务重启后依然可用。不带游标时,你会得到最新的匹配记录。limit 为 1–500,默认 100。
{ "data": [ {
"id": "wm_…", "wishlist_id": "wl_…",
"station": { "id": "…", "name": "…", "country": "FR", "codec": "MP3", "bitrate": 128, "is_hls": false,
"stream_url": "https://…", "homepage": "https://…", "recordability": "recordable" },
"raw_title": "Daft Punk - One More Time", "track": { "id": "trk_…", "mbid": "…", "artist_id": "art_…" },
"started_at": "2026-10-30T21:04:10Z", "start_uncertainty_seconds": 10,
"observed_at": "2026-10-30T21:04:20Z", "matched_at": "2026-10-30T21:04:20Z",
"confidence": "high", "expected_end_hint_at": "2026-10-30T21:07:52Z" } ],
"next_cursor": "eyJ0Ijo…" }
started_at 已经根据看到标题的时间做过校正,因此录音工具可以显示“大约 N ± U 秒前开始”。expected_end_hint_at 是 started_at 加上 222 秒(歌曲时长的中位数):只是提示,不是预测。匹配经由已关联的曲目或艺人 id 时,confidence 为 high;基于规范化后的艺人和标题时为 medium;文本查询为 low。
POST/v1/webhooks GET/v1/webhooks/{id}/deliveries 密钥 · pro + no-store
Webhook
事件发生时,Tuneping 会将其 POST 到你的 HTTPS 端点。Pro 可设 5 个端点,Business 20 个。
| 事件 | 何时触发 | Pro 需要 |
|---|---|---|
wishlist.matched | 在 webhook_id 中指定了此端点的心愿单产生了匹配。 | — |
station.title_changed | 某个电台的标题行发生了变化。 | filters.station_ids(≤ 200) |
track.started | 某首已关联的曲目开始播放。 | filters.track_ids 或 artist_ids(≤ 200) |
artist.started | 某位已关联的艺人开始播放。 | filters.track_ids 或 artist_ids(≤ 200) |
筛选条件还可以列出 countries 和 classification。仅按国家筛选,以及不加筛选的标题变化流,需要 Business 套餐。每个端点每天的事件额度在 Pro 上为 50,000 个,在 Business 上为 500,000 个;超出后该端点进入 paused_budget 状态,直到下一个 UTC 日,并且你会收到一封邮件。
curl -H "Authorization: Bearer tp_live_…" -H "Content-Type: application/json" \
-d '{"url":"https://example.com/hooks/tuneping","events":["wishlist.matched"]}' \
https://api.tuneping.com/v1/webhooks
响应中会携带签名用的 secret(whsec_…),仅此一次。投递遵循 Standard Webhooks:请求头为 webhook-id、webhook-timestamp 和 webhook-signature: v1,<base64>,其中签名是以签名密钥的字节对 {webhook-id}.{webhook-timestamp}.{body} 计算的 HMAC-SHA256。请拒绝偏差超过 5 分钟的时间戳,并用 webhook-id 去除重复:它在重试之间保持不变。
{ "id": "evt_…", "type": "wishlist.matched", "created_at": "2026-10-30T21:04:20Z",
"data": { "station_id": "…", "station": { …station ref… }, "wishlist_id": "wl_…", "match_id": "wm_…",
"raw_title": "Daft Punk - One More Time", "artist": "Daft Punk", "title": "One More Time",
"track_id": "trk_…", "artist_id": "art_…", "classification": "song",
"started_at": "…", "start_uncertainty_seconds": 10, "observed_at": "…",
"confidence": "high", "expected_end_hint_at": "…" },
"links": { "station": "/v1/stations/…", "wishlist": "/v1/wishlists/wl_…" } }
请在 10 秒内返回任意 2xx。不会跟随重定向。投递失败后会在 5 s、5 min、30 min、2 h、5 h、10 h、14 h、20 h 和 24 h 后重试,总计约三天,每个端点同一时间只投递一个。返回 410 的端点,或连续五天投递失败的端点,会被停用,并且你会收到一封邮件;修复后用 PATCH 把 status 设回 enabled。
POST /v1/webhooks/{id}/rotate-secret 返回一个新的签名密钥;在 24 小时内,每次投递都携带两个签名,新旧签名密钥各一个。POST /v1/webhooks/{id}/test 发送一个 ping。GET /v1/webhooks/{id}/deliveries?status=pending|succeeded|failed 列出 30 天内的投递记录,POST …/deliveries/{dl_id}/redeliver 重新发送其中一条。测试密钥(tp_test_…)只能指向 webhook.site。
GET/v1/stream 密钥 · developer + text/event-stream
事件流
以服务器发送事件 格式,通过一个长时间保持的 HTTP 响应传送同样的事件,适用于服务器和原生应用。传入 station_ids(最多 100 个)或 wishlist_id(all,或一个 wl_… id)之一。
curl -N -H "Authorization: Bearer tp_live_…" -H "Accept: text/event-stream" \
"https://api.tuneping.com/v1/stream?station_ids=159824f0-5475-4263-bcc8-59a48a213896"
retry: 3000
id: evt_1m675e1tkfdxtn7axmq6qktz8
event: station.title_changed
data: {"id":"evt_1m675e1tkfdxtn7axmq6qktz8","type":"station.title_changed","created_at":"…","data":{…}}
: ping
| 参数 | 默认值 | 含义 |
|---|---|---|
station_ids | — | 逗号分隔的电台 id,最多 100 个。 |
wishlist_id | — | all 或一个心愿单 id。发送 wishlist.matched。 |
events | station.title_changed | 用于电台流,可为 station.title_changed、track.started、artist.started 中的任意几项。 |
classification | song | song 或 any,用于电台流。 |
data 与 Webhook 收到的 JSON 相同。20 秒内没有事件时会收到一条 : ping 注释,因此请把客户端的空闲超时设为 60 秒或更长。连接断开时,重新连接并把 Last-Event-ID 请求头设为你看到的最后一个 id:最近 10 分钟内的事件会被重放。更早的位置会收到一个 event: gap,其中带有最早的可用 id;请从历史记录或匹配记录流中补齐缺口。每个密钥可打开的流数量:Developer 1 个,Pro 5 个,Business 20 个,Tapelark 每台设备 1 个。再多打开一个会返回 429 too_many_connections。
浏览器无法在这里使用 EventSource,因为它不能发送 Authorization 请求头,而密钥绝不接受放在 URL 中。请改用 fetch() 读取事件流。
字段与枚举
- classification 枚举
song·ident(电台名称或口号)·programme·ad_break·empty·junk·unknown。只有歌曲会进入播放历史;所有内容都保留在实时行中。- match_grade 枚举
high·medium·none。标题与 MusicBrainz 录音关联的可靠程度。- confidence 枚举
high·medium·low。按行计算,依据分类、匹配等级、电台最近一次确认的时间以及该电台的历史表现。公开计算方式,便于你审核。- method 枚举
- Tuneping 读取该电台的方式:
icy(流内元数据)、icecast_json(服务器状态 JSON)、shoutcast_stats(状态 XML)、shoutcast_currentsong、seven_html。这些是内部标识符,不代表认可;如需展示,请显示为“流内”和“状态页”。 - recordability 枚举
- 从录音客户端的角度看,该电台的流是否为普通、可录制的 HTTP 流。仅供参考。
- Id
- 电台使用其 Radio Browser UUID。曲目为
trk_…,艺人art_…,播放事件pe_…,密钥key_…,所有者own_…,心愿单wl_…,匹配记录wm_…,Webhookwh_…,投递dl_…,事件evt_…。请将它们全部视为不透明字符串。 - 时间戳
- RFC 3339,UTC,在数据源提供时精确到毫秒。
OpenAPI
本 API 的机器可读描述位于 /docs/openapi-v1.yaml(OpenAPI 3.1)。它是生成客户端的依据;operation id 保持稳定。
署名与合理使用
Free 套餐和匿名使用须在展示数据的位置显示一个可见的“Data by Tuneping”链接(徽标)。播放记录基于电台元数据识别,并非经过认证的监测;作为证据呈现时,请注明“基于电台元数据识别”。电台名称为文本;Tuneping 不提供台标或封面图。详见条款。