Tuneping API v1
Un'API JSON per sapere cosa trasmettono le web radio in questo momento e cosa hanno trasmesso. URL di base https://api.tuneping.com/v1. Solo metadati: il servizio legge la riga pubblica del titolo di ogni stream e non tocca mai l'audio.
Guida rapida
Chi sta trasmettendo Madonna in questo momento?
curl "https://api.tuneping.com/v1/now-playing/search?q=madonna&limit=3"
Cosa è in onda su una stazione di cui conosci l'id (gli id sono gli UUID delle stazioni di Radio Browser):
curl "https://api.tuneping.com/v1/stations/159824f0-5475-4263-bcc8-59a48a213896/now-playing"
Cosa ha trasmesso una stazione nelle ultime 24 ore (richiede una chiave con storico, piano Developer o superiore):
curl -H "Authorization: Bearer tp_live_…" \
"https://api.tuneping.com/v1/stations/159824f0-5475-4263-bcc8-59a48a213896/history"
Ogni risposta è in JSON. Gli errori sono application/problem+json (vedi Errori). I parametri di query sconosciuti vengono rifiutati con 400 invece di essere ignorati, così gli errori di battitura emergono subito.
Ottenere una chiave
In self-service su tuneping.com/signup: inserisci un indirizzo e-mail, apri entro 60 minuti il link ricevuto e la chiave viene mostrata una sola volta. Gli stessi due passaggi sono disponibili come API: POST /v1/signup con {"email": "…", "plan": "free|developer|pro"} risponde 202 e invia l'e-mail; POST /v1/signup/verify con il {"token": "…"} del link risponde 201 con la chiave. Free non richiede altro. Developer e Pro proseguono su Stripe Checkout dal link nella risposta; il piano del titolare cambia entro un minuto dal pagamento e tutte le chiavi del titolare si adeguano.
Hai perso una chiave? Registrati di nuovo con lo stesso indirizzo: la chiave viene ruotata e la vecchia continua a funzionare per 24 ore. Una chiave live con Free e Developer, cinque con Pro, venti con Business; in più una chiave tp_test_ su ogni piano, emessa su richiesta finché il portale non sarà disponibile.
Autenticazione
Invia la chiave come bearer token, oppure in X-Api-Key:
Authorization: Bearer tp_live_a1b2c3…
X-Api-Key: tp_live_a1b2c3…
Le chiavi iniziano con tp_live_ o tp_test_ seguiti da 43 caratteri. Una richiesta senza chiave è considerata anonima e limitata per indirizzo IP. Una chiave malformata, sconosciuta o revocata risponde subito 401, anche sugli endpoint che avrebbero funzionato in modo anonimo. Gli endpoint contrassegnati con chiave qui sotto rispondono 401 unauthenticated senza chiave.
Scope. Le chiavi hanno tutti gli scope (read, exports, webhooks, wishlists) a meno che non siano state emesse con un elenco più ristretto, per esempio di sola lettura; GET /v1/me elenca gli scope di una chiave. Wishlist e webhook richiedono gli scope wishlists e webhooks (una wishlist con consegna webhook li richiede entrambi, verificati prima del piano). Una chiave senza lo scope risponde 403 forbidden_scope con required_scope, e WWW-Authenticate contiene error="insufficient_scope".
Chiavi di test. Una chiave tp_test_ legge gli stessi dati in tempo reale con i limiti di Free, e ha le proprie wishlist, i propri match e i propri endpoint webhook (livemode: false). Non vede né modifica mai quelli live, quindi una suite di test può fare PUT di un insieme vuoto di wishlist senza toccare la produzione. I webhook in modalità test possono puntare solo a webhook.site.
Ripetizioni. POST, PATCH e DELETE sugli endpoint che richiedono una chiave o un token partner accettano un header Idempotency-Key (un UUID per ogni richiesta logica); gli endpoint di registrazione non lo accettano. Una ripetizione con la stessa chiave e la stessa richiesta riceve di nuovo la prima risposta riuscita, contrassegnata da Idempotent-Replayed: true, invece di creare una seconda wishlist o un secondo endpoint. La chiave viene ricordata per 24 ore; riusarla per una richiesta diversa dà 422 idempotency_key_reused, e una ripetizione mentre la prima richiesta è ancora in esecuzione dà 409 idempotency_in_progress. Gli errori non vengono ricordati, quindi una ripetizione dopo un errore viene eseguita di nuovo. Una connessione interrotta non annulla una richiesta che porta l'header: la richiesta termina e una risposta riuscita viene ricordata, così la tua ripetizione la riceve. Una richiesta ancora in esecuzione dopo 20 secondi viene annullata senza modificare nulla e risponde 503 unavailable con Retry-After: 1; ripetila con la stessa chiave.
Le app partner (attualmente Tapelark) inviano un entitlement token firmato invece di una chiave: Authorization: Bearer TPE1-…. Il token viene verificato offline con le chiavi Ed25519 del partner e corrisponde al piano partner nella tabella qui sotto; l'API non viene mai a sapere chi è l'utente finale. Un token malformato o sconosciuto risponde 401 token_invalid, uno scaduto 401 token_expired (rinnovalo), un token per un'altra audience 401 token_audience, e un entitlement scaduto o revocato 403 entitlement_revoked.
Rate limit e quote
| Piano | Frequenza | Quota | Id per batch | Storico |
|---|---|---|---|---|
| Anonimo (per IP) | 2 req/s, burst 5 | 1.000 / giorno | 20 | nessuno |
| Free | 2 req/s, burst 5 | 1.000 / giorno | 20 | nessuno |
| Developer | 10 req/s, burst 20 | 100.000 / mese | 100 | 7 giorni |
| Pro | 50 req/s, burst 100 | 1.000.000 / mese | 100 | 12 mesi |
| Business | personalizzata | personalizzata | 100 | 12 mesi |
| Tapelark (entitlement token, per licenza) | 5 req/s, burst 10 | 2.000 / giorno | 50 | 7 giorni |
Il limite al secondo è un token bucket. Quando è vuoto ricevi 429 rate_limited con Retry-After. Quando la quota è esaurita ricevi 429 quota_exceeded con Retry-After impostato ai secondi che mancano all'azzeramento della finestra.
Le risposte autenticate che non sono memorizzabili in cache condivisa riportano i campi di rate limit IETF:
RateLimit-Policy: "burst";q=10;w=1, "monthly";q=100000;w=2592000
RateLimit: "burst";r=7;t=1
Le risposte memorizzabili in cache condivisa (ricerca, batch, now playing di una stazione, stazioni, statistiche, serie di stato) non riportano header di rate limit, perché l'edge può servire lo stesso body a un altro client. Il budget rimanente è sempre disponibile su GET /v1/me.
Caching
Le risposte in tempo reale sono memorizzabili in cache per 5 secondi (Cache-Control: public, max-age=5, s-maxage=5) e riportano un ETag debole. Invia If-None-Match per ricevere 304 quando non è cambiato nulla; all'origine un 304 non viene conteggiato nella tua quota. L'elenco delle stazioni resta in cache per 60 secondi, una singola stazione per 300. Lo storico è private, max-age=30. Gli endpoint di stato e delle chiavi sono no-store.
Ogni risposta riporta X-Request-Id. Indicalo quando scrivi al supporto.
Errori
Gli errori seguono i problem details RFC 9457 con due membri di estensione: code, una stringa stabile leggibile dalle macchine, e campi aggiuntivi specifici dell'endpoint come errors[] negli errori di validazione o required_tier nei 403.
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." } ]
}
L'elenco completo dei codici con il loro significato è nel catalogo degli errori. Quelli che incontrerai per primi:
| HTTP | code | Quando |
|---|---|---|
| 400 | invalid_request | Parametro di query sconosciuto o richiesta malformata |
| 400 | invalid_cursor | Cursore malformato, scaduto (24 h; 30 giorni per un since_cursor del feed dei match) o usato con filtri diversi |
| 401 | unauthenticated · invalid_api_key · key_revoked | Chiave mancante, sconosciuta o revocata |
| 403 | forbidden_tier | L'endpoint o il parametro richiede un piano superiore; required_tier dice quale |
| 403 | forbidden_scope | La chiave è stata emessa senza lo scope; required_scope dice quale |
| 404 | not_found · station_opted_out | Id sconosciuto, oppure la stazione ha chiesto di non essere indicizzata |
| 422 | validation_failed | Errori dei campi in errors[] |
| 429 | rate_limited · quota_exceeded | Rallenta; Retry-After in secondi |
| 503 | unavailable | Database non disponibile, oppure una richiesta con un Idempotency-Key annullata dopo 20 secondi; riprova dopo Retry-After |
GET/v1/now-playing/search senza chiave public, max-age=5 · ETag
Ricerca in tempo reale
Quali stazioni stanno trasmettendo q in questo momento. Ricerca approssimata e insensibile agli accenti: gronemeyer manner trova Grönemeyer – Männer. Le righe sono ordinate per somiglianza, poi per quanto di recente è apparso il titolo. Nessuna paginazione: l'indice cambia ogni pochi secondi, quindi le pagine salterebbero o ripeterebbero righe. Aumenta invece limit.
| Parametro | Tipo | Predefinito | Significato |
|---|---|---|---|
q | stringa, 2–120 caratteri | obbligatorio | Artista, titolo o entrambi. Da tre caratteri in su si usa il matching a trigrammi; le query più corte cercano una sottostringa. |
country | ISO 3166-1 alpha-2, ripetibile | qualsiasi | Solo stazioni di questi paesi. |
codec | stringa | qualsiasi | per es. MP3, AAC. |
min_bitrate | intero | 0 | kbit/s. |
classification | enum | song | song, ident, programme, ad_break, empty, junk, unknown o any. |
max_age_seconds | 30–900 | 180 | Solo righe la cui stazione è stata interrogata con successo entro questo numero di secondi. |
limit | 1–100 | 20 | Righe da restituire; truncated: true indica che ce n'erano altre. |
Risposta · 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 }
}
Come leggere una riga
- started_at RFC 3339 · stima
- Inizio stimato del titolo in onda, ±
start_uncertainty_secondsin entrambe le direzioni. - observed_at RFC 3339
- Quando Tuneping ha visto questo titolo per la prima volta.
- confirmed_at RFC 3339
- L'ultima interrogazione riuscita della stazione: "ancora in onda alle". Usalo per valutare quanto è aggiornato il dato.
- track oggetto · facoltativo
- Presente quando il titolo è stato collegato a una registrazione MusicBrainz.
- similarity 0–1
- Applica la tua soglia.
GET/v1/now-playing senza chiave public, max-age=5 · ETag
Now playing in batch
Il titolo in onda per un elenco di stazioni in una sola chiamata, per le schermate dei preferiti.
| Parametro | Tipo | Predefinito | Significato |
|---|---|---|---|
station_ids | UUID separati da virgola | obbligatorio | Fino a 20 in modo anonimo o con Free, 100 da Developer in su. Gli id vengono ordinati e deduplicati prima del caching, così due client con gli stessi preferiti condividono un unico body in cache. |
max_age_seconds | 30–86400 | 900 | Le righe più vecchie di questo valore vengono comunque restituite, ma contrassegnate con 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 senza chiave
Now playing di una stazione
Una riga per una stazione. 404 not_found quando non è ancora stato osservato alcun titolo, 404 station_opted_out quando la stazione ha chiesto di non essere indicizzata.
GET/v1/stations GET/v1/stations/{id} senza chiave max-age=60 · 300
Stazioni
L'elenco, costruito a partire dall'elenco Radio Browser mantenuto dalla comunità e arricchito con il modo in cui Tuneping legge ogni stazione.
| Parametro | Tipo | Predefinito | Significato |
|---|---|---|---|
q | stringa | — | Nome della stazione, ricerca approssimata. |
country | alpha-2, ripetibile | qualsiasi | |
tag | stringa, ripetibile | qualsiasi | Devono corrispondere tutti i tag indicati. |
codec, min_bitrate | Come nella ricerca. | ||
has_now_playing | booleano | false | Solo le stazioni da cui Tuneping legge attualmente i titoli. |
sort | enum | clickcount | clickcount (popolarità, decrescente), name, bitrate. |
limit | 1–200 | 50 | |
cursor | stringa opaca | — | Da next_cursor della pagina precedente; valido 24 ore e solo con gli stessi filtri. |
{ "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 viene riportato come testo dall'elenco; Tuneping non copia né distribuisce mai loghi.
GET/v1/stations/{id}/history chiave · developer + private, max-age=30
Storico dei passaggi
Le canzoni trasmesse da una stazione, dalla più recente, con inizio e fine stimati. La profondità dipende dal piano: 7 giorni con Developer, 12 mesi con Pro e Business. Chiedere un from più vecchio risponde 403 forbidden_tier con required_tier, mai con una finestra ridotta in silenzio.
| Parametro | Tipo | Predefinito | Significato |
|---|---|---|---|
from, to | RFC 3339 | to = adesso, from = to − 24 h | Al massimo 31 giorni per chiamata. |
classification | enum | song | Come nella ricerca. |
limit | 1–200 | 50 | |
cursor | stringa opaca | — | La finestra viene fissata nel cursore. |
{ "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 chiave no-store
Chiavi e utilizzo
/v1/me restituisce la chiave, il piano e lo stato del suo titolare, i limiti applicati e l'utilizzo finora nella finestra corrente. /v1/me/usage?from=&to= restituisce le richieste al giorno (solo le richieste all'origine; i cache hit sull'edge non vengono conteggiati).
{ "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 senza chiave
Stato e statistiche
/v1/health risponde 200 con status ok o degraded, 503 quando il database non è raggiungibile; puntaci il tuo monitor di uptime. /v1/health/series?minutes=60 (5–1440) restituisce i valori del poller al minuto: polls, ok, titles_changed, songs, bytes_in, bytes_out, latency_p50_ms, latency_p90_ms, inflight_max, ips_paused, queue_lag_seconds e una ripartizione by_method. /v1/stats è l'istantanea pubblica della copertura mostrata nella pagina di stato: stazioni totali, interrogate, con metadati in tempo reale, cambi di titolo al minuto, quota di canzoni, per metodo, per paese.
POST/v1/wishlists PUT/v1/wishlists GET/v1/wishlists/matches chiave · developer + no-store
Wishlist
Indica una canzone, un artista o un titolo in testo libero e scopri quando una stazione inizia a trasmetterlo. Ogni cambio di titolo viene confrontato con le tue wishlist nel momento in cui viene letto, e una nuova wishlist è attiva entro 30 secondi. Developer conserva 10 wishlist, Pro 100, Business 1.000. I match vengono conservati per 30 giorni.
| Campo | Tipo | Significato |
|---|---|---|
kind | artist · track · query | artist e track accettano un id ottenuto dalla ricerca (art_…, trk_…) e intercettano anche le grafie che il normalizzatore collega a quell'id. query accetta testo libero, di solito Artist - Title. |
value | stringa, ≤ 240 | L'id o il testo. |
match | exact · fuzzy | Solo per query. Exact confronta il testo normalizzato; fuzzy accetta una somiglianza a trigrammi di 0,6 o superiore. |
station_filter | oggetto | station_ids (≤ 500) e/o countries (alpha-2 maiuscolo o minuscolo, restituito in maiuscolo). Un titolo corrisponde quando la sua stazione è nell'elenco o si trova in un paese dell'elenco. Nessun filtro significa tutte le stazioni. |
delivery | array | poll (predefinito), sse, webhook. La consegna via webhook è disponibile da Pro in su e richiede webhook_id e una chiave con lo scope webhooks. |
description | stringa, ≤ 200 | Libera, per te. |
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
Un elenco esplicito station_ids fa sì che Tuneping legga quelle stazioni ogni 30 secondi invece che ogni 60 finché la wishlist è attiva, e la risposta lo segnala con tightens: true. L'unione delle stazioni che elenchi ha un tetto per piano (limits.tightened_stations in /v1/me); una wishlist che lo supererebbe risponde 422 validation_failed con una voce in errors[] il cui code è tightened_stations_exceeded (campo station_filter.station_ids, oppure data con PUT). I filtri per paese continuano a funzionare, all'intervallo normale di ogni stazione.
GET /v1/wishlists/{id}, PATCH (filtro delle stazioni, match, consegna, webhook, descrizione, status: enabled | paused) e DELETE funzionano come ci si aspetta. PUT /v1/wishlists con {"data": [ … ]} sostituisce l'intero insieme in una sola chiamata: le voci invariate mantengono il loro id e il conteggio dei match, le altre vengono create o eliminate. È il modo più semplice per sincronizzare un elenco che la tua app tiene in locale. Il suo body può arrivare a 1 MiB; gli altri body delle richieste sono limitati a 64 KiB (413 payload_too_large).
I match sono un feed incrementale. GET /v1/wishlists/matches copre tutte le tue wishlist e GET /v1/wishlists/{id}/matches una sola. Le righe arrivano dalla più vecchia. Conserva il next_cursor di ogni risposta e rimandalo come since_cursor: non è mai null, resta valido per i 30 giorni di conservazione e sopravvive ai riavvii. Senza cursore ricevi i match più recenti. limit va da 1 a 500, predefinito 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 è già corretto in base al momento in cui il titolo è stato visto, così un registratore può mostrare "iniziato circa N ± U secondi fa". expected_end_hint_at è started_at più 222 secondi, la durata mediana di una canzone: un'indicazione, non una previsione. confidence è high quando il match è passato da un id di brano o di artista collegato, medium per artista e titolo normalizzati, low per le query testuali.
POST/v1/webhooks GET/v1/webhooks/{id}/deliveries chiave · pro + no-store
Webhook
Tuneping invia gli eventi al tuo endpoint HTTPS nel momento in cui avvengono. Pro ha 5 endpoint, Business 20.
| Evento | Quando | Con Pro richiede |
|---|---|---|
wishlist.matched | Una wishlist che indica questo endpoint in webhook_id ha trovato un match. | — |
station.title_changed | La riga del titolo di una stazione è cambiata. | filters.station_ids (≤ 200) |
track.started | È iniziato un brano collegato. | filters.track_ids o artist_ids (≤ 200) |
artist.started | È iniziato un artista collegato. | filters.track_ids o artist_ids (≤ 200) |
I filtri possono indicare anche countries e classification. I filtri solo per paese e i flussi non filtrati di cambi di titolo sono riservati a Business. Ogni endpoint ha un budget giornaliero di 50.000 eventi con Pro e 500.000 con Business; oltre quella soglia l'endpoint passa a paused_budget fino al giorno UTC successivo e ricevi un'e-mail.
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
La risposta contiene il secret di firma (whsec_…) una sola volta. Le consegne seguono Standard Webhooks: header webhook-id, webhook-timestamp e webhook-signature: v1,<base64>, dove la firma è un HMAC-SHA256 calcolato con i byte del secret su {webhook-id}.{webhook-timestamp}.{body}. Rifiuta i timestamp che si discostano di più di 5 minuti e usa webhook-id per scartare i duplicati: resta lo stesso tra un tentativo e l'altro.
{ "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_…" } }
Rispondi con un qualsiasi 2xx entro 10 secondi. I redirect non vengono seguiti. Le consegne non riuscite vengono ritentate dopo 5 s, 5 min, 30 min, 2 h, 5 h, 10 h, 14 h, 20 h e 24 h, circa tre giorni in tutto, una alla volta per endpoint. Un endpoint che risponde 410, o che fallisce per cinque giorni di fila, viene disattivato e ricevi un'e-mail; quando è sistemato, reimposta status su enabled con PATCH.
POST /v1/webhooks/{id}/rotate-secret restituisce un nuovo secret; per 24 ore ogni consegna porta due firme, una per secret. POST /v1/webhooks/{id}/test invia un ping. GET /v1/webhooks/{id}/deliveries?status=pending|succeeded|failed elenca 30 giorni di consegne, e POST …/deliveries/{dl_id}/redeliver ne invia di nuovo una. Le chiavi di test (tp_test_…) possono puntare solo a webhook.site.
GET/v1/stream chiave · developer + text/event-stream
Stream di eventi
Gli stessi eventi su un'unica risposta HTTP di lunga durata nel formato server-sent events, per server e app native. Passa station_ids (fino a 100) oppure wishlist_id (all, o un singolo id wl_…).
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
| Parametro | Predefinito | Significato |
|---|---|---|
station_ids | — | Id di stazioni separati da virgola, al massimo 100. |
wishlist_id | — | all o l'id di una wishlist. Invia wishlist.matched. |
events | station.title_changed | Per gli stream di stazioni, uno o più tra station.title_changed, track.started, artist.started. |
classification | song | song o any, per gli stream di stazioni. |
data è lo stesso JSON che riceve un webhook. Un commento : ping arriva dopo 20 secondi senza eventi, quindi imposta il timeout di inattività del tuo client a 60 secondi o più. Quando la connessione cade, riconnettiti con l'header Last-Event-ID impostato sull'ultimo id che hai visto: gli eventi degli ultimi 10 minuti vengono ritrasmessi. Una posizione più vecchia riceve un event: gap con il primo id disponibile; colma il buco dallo storico o dal feed dei match. Stream aperti per chiave: Developer 1, Pro 5, Business 20, Tapelark 1 per dispositivo. Uno in più risponde 429 too_many_connections.
I browser non possono usare EventSource qui, perché non può inviare l'header Authorization e le chiavi non vengono mai accettate nell'URL. Leggi invece lo stream con fetch().
Campi ed enum
- classification enum
song·ident(nome o slogan della stazione) ·programme·ad_break·empty·junk·unknown. Solo le canzoni entrano nello storico dei passaggi; nella riga in tempo reale viene conservato tutto.- match_grade enum
high·medium·none. Quanto bene il titolo è stato collegato a una registrazione MusicBrainz.- confidence enum
high·medium·low. Calcolato per ogni riga a partire da classificazione, grado di match, quanto di recente la stazione è stata confermata e affidabilità storica della stazione. Documentato perché tu possa verificarlo.- method enum
- Come Tuneping legge la stazione:
icy(metadati nello stream),icecast_json(JSON di stato del server),shoutcast_stats(XML di stato),shoutcast_currentsong,seven_html. Identificatori interni, non raccomandazioni; se li mostri, visualizzali come "nello stream" e "pagina di stato". - recordability enum
- Se lo stream della stazione è uno stream HTTP semplice e registrabile dal punto di vista di un client di registrazione. Informativo.
- Id
- Le stazioni usano il loro UUID di Radio Browser. I brani sono
trk_…, gli artistiart_…, gli eventi di passaggiope_…, le chiavikey_…, i titolariown_…, le wishlistwl_…, i matchwm_…, i webhookwh_…, le consegnedl_…, gli eventievt_…. Trattali tutti come stringhe opache. - Timestamp
- RFC 3339, UTC, con precisione al millisecondo dove la fonte ce l'ha.
OpenAPI
La descrizione leggibile dalle macchine di questa API si trova in /docs/openapi-v1.yaml (OpenAPI 3.1). È la fonte per generare i client; gli operation id sono stabili.
Attribuzione e uso corretto
L'uso con il piano Free e quello anonimo mostrano un link visibile “Data by Tuneping” dove vengono visualizzati i dati (badge). I passaggi sono rilevati dai metadati delle stazioni e non costituiscono un monitoraggio certificato; scrivi “rilevato dai metadati della stazione” quando li presenti come prova. I nomi delle stazioni sono testo; Tuneping non fornisce loghi né copertine. Vedi i termini.