API do Tuneping v1

Uma API JSON para o que as estações de rádio online estão tocando agora e o que tocaram. URL base https://api.tuneping.com/v1. Só metadados: o serviço lê a linha de título pública de cada stream e nunca toca no áudio.

Beta públicoOs endpoints ao vivo (busca, lote, tocando agora na estação, diretório de estações, saúde e estatísticas) funcionam sem chave, com limite por endereço IP. As chaves são de autoatendimento na página de cadastro: Free na hora, Developer e Pro após o Stripe Checkout. Business: escreva para [email protected].

Início rápido

Quem está tocando Madonna agora?

curl "https://api.tuneping.com/v1/now-playing/search?q=madonna&limit=3"

Tocando agora em uma estação que você conhece pelo id (os ids são UUIDs de estação do Radio Browser):

curl "https://api.tuneping.com/v1/stations/159824f0-5475-4263-bcc8-59a48a213896/now-playing"

O que uma estação tocou nas últimas 24 horas (exige uma chave com histórico, plano Developer ou superior):

curl -H "Authorization: Bearer tp_live_…" \
  "https://api.tuneping.com/v1/stations/159824f0-5475-4263-bcc8-59a48a213896/history"

Toda resposta é JSON. Os erros são application/problem+json (veja Erros). Parâmetros de query desconhecidos são rejeitados com 400 em vez de ignorados, para que erros de digitação apareçam na hora.

Obter uma chave

Autoatendimento em tuneping.com/signup: informe um endereço de e-mail, abra o link enviado em até 60 minutos e a chave é exibida uma única vez. As mesmas duas etapas existem como API: POST /v1/signup com {"email": "…", "plan": "free|developer|pro"} responde 202 e envia o e-mail; POST /v1/signup/verify com o {"token": "…"} do link responde 201 com a chave. O Free não exige mais nada. Developer e Pro seguem para o Stripe Checkout pelo link na resposta; o plano do titular muda em até um minuto após o pagamento, e todas as chaves do titular acompanham a mudança.

Perdeu uma chave? Cadastre-se de novo com o mesmo endereço: a chave é rotacionada e a antiga continua funcionando por 24 horas. Uma chave live no Free e no Developer, cinco no Pro, vinte no Business; mais uma chave tp_test_ em todos os planos, emitida sob pedido até o portal ser lançado.

Autenticação

Envie sua chave como bearer token ou em X-Api-Key:

Authorization: Bearer tp_live_a1b2c3…
X-Api-Key: tp_live_a1b2c3…

As chaves começam com tp_live_ ou tp_test_ seguido de 43 caracteres. Uma requisição sem chave é tratada como anônima e limitada por endereço IP. Uma chave malformada, desconhecida ou revogada responde 401 imediatamente, mesmo em endpoints que teriam funcionado de forma anônima. Os endpoints marcados com chave abaixo respondem 401 unauthenticated sem chave.

Escopos. As chaves têm todos os escopos (read, exports, webhooks, wishlists), a menos que tenham sido emitidas com uma lista mais restrita, por exemplo somente leitura; GET /v1/me lista os escopos de uma chave. Listas de desejos e webhooks exigem os escopos wishlists e webhooks (uma lista de desejos com entrega por webhook exige os dois, verificados antes do plano). Uma chave sem o escopo responde 403 forbidden_scope com required_scope, e WWW-Authenticate traz error="insufficient_scope".

Chaves de teste. Uma chave tp_test_ lê os mesmos dados ao vivo com os limites do Free e tem suas próprias listas de desejos, correspondências e endpoints de webhook (livemode: false). Ela nunca vê nem altera os do modo live, então uma suíte de testes pode fazer PUT de um conjunto vazio de listas de desejos sem afetar a produção. Webhooks do modo de teste só podem apontar para webhook.site.

Novas tentativas. POST, PATCH e DELETE em endpoints que exigem uma chave ou um token de parceiro aceitam um cabeçalho Idempotency-Key (um UUID por requisição lógica); os endpoints de cadastro não o aceitam. Uma nova tentativa com a mesma chave e a mesma requisição recebe de novo a primeira resposta bem-sucedida, marcada com Idempotent-Replayed: true, em vez de criar uma segunda lista de desejos ou um segundo endpoint. A chave fica guardada por 24 horas; reutilizá-la para uma requisição diferente resulta em 422 idempotency_key_reused, e uma nova tentativa enquanto a primeira requisição ainda está em execução resulta em 409 idempotency_in_progress. Erros não são guardados, então uma nova tentativa após um erro é executada de novo. Uma conexão perdida não cancela uma requisição que traz o cabeçalho: ela termina e uma resposta bem-sucedida é guardada, então sua nova tentativa a recebe. Uma requisição ainda em execução após 20 segundos é cancelada sem nenhuma alteração e responde 503 unavailable com Retry-After: 1; tente de novo com a mesma chave.

Apps parceiros (atualmente o Tapelark) enviam um token de direito de acesso assinado em vez de uma chave: Authorization: Bearer TPE1-…. O token é verificado offline com as chaves Ed25519 do parceiro e corresponde ao plano de parceiro na tabela abaixo; a API nunca fica sabendo quem é o usuário final. Um token malformado ou desconhecido responde 401 token_invalid, um expirado 401 token_expired (renove-o), um token para outro público (audience) 401 token_audience, e um direito de acesso expirado ou revogado 403 entitlement_revoked.

Rate limits e cotas

PlanoTaxaCotaIds por loteHistórico
Anônimo (por IP)2 req/s, rajada de 51.000 / dia20nenhum
Free2 req/s, rajada de 51.000 / dia20nenhum
Developer10 req/s, rajada de 20100.000 / mês1007 dias
Pro50 req/s, rajada de 1001.000.000 / mês10012 meses
Businesssob medidasob medida10012 meses
Tapelark (token de direito de acesso, por licença)5 req/s, rajada de 102.000 / dia507 dias

O limite por segundo é um token bucket. Quando ele está vazio, você recebe 429 rate_limited com Retry-After. Quando a cota se esgota, você recebe 429 quota_exceeded com Retry-After definido como os segundos que faltam até a janela ser renovada.

Respostas autenticadas que não podem ir para cache compartilhado trazem os campos de rate limit do IETF:

RateLimit-Policy: "burst";q=10;w=1, "monthly";q=100000;w=2592000
RateLimit: "burst";r=7;t=1

Respostas que podem ir para cache compartilhado (busca, lote, tocando agora na estação, estações, estatísticas, série de saúde) não trazem cabeçalhos de rate limit, porque a borda pode servir o mesmo corpo a outro cliente. Seu saldo restante está sempre disponível em GET /v1/me.

Cache

As respostas ao vivo podem ficar em cache por 5 segundos (Cache-Control: public, max-age=5, s-maxage=5) e trazem um ETag fraco. Envie If-None-Match para receber 304 quando nada mudou; um 304 não conta para a sua cota na origem. O diretório de estações fica em cache por 60 segundos, uma estação individual por 300. O histórico é private, max-age=30. Os endpoints de saúde e de chaves são no-store.

Toda resposta traz X-Request-Id. Informe-o quando escrever para o suporte.

Erros

Os erros seguem os problem details da RFC 9457 com dois membros de extensão: code, uma string estável legível por máquina, e extras específicos de cada endpoint, como errors[] em falhas de validação ou required_tier em 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." } ]
}

A lista completa de códigos com seus significados está no catálogo de erros. Os que você vai encontrar primeiro:

HTTPcodeQuando
400invalid_requestParâmetro de query desconhecido ou requisição malformada
400invalid_cursorCursor malformado, expirado (24 h; 30 dias para um since_cursor do feed de correspondências) ou usado com filtros diferentes
401unauthenticated · invalid_api_key · key_revokedChave ausente, desconhecida ou revogada
403forbidden_tierO endpoint ou o parâmetro exige um plano superior; required_tier diz qual
403forbidden_scopeA chave foi emitida sem o escopo; required_scope diz qual
404not_found · station_opted_outId desconhecido, ou a estação pediu para não ser indexada
422validation_failedErros de campo em errors[]
429rate_limited · quota_exceededVá mais devagar; Retry-After em segundos
503unavailableBanco de dados indisponível, ou uma requisição com Idempotency-Key cancelada após 20 segundos; tente de novo após Retry-After

GET/v1/now-playing/search sem chave public, max-age=5 · ETag

Quais estações estão tocando q agora. Busca aproximada e sem distinção de acentos: gronemeyer manner encontra Grönemeyer – Männer. As linhas são ordenadas por similaridade e depois por quão recentemente o título apareceu. Sem paginação: o índice muda a cada poucos segundos, então as páginas pulariam ou repetiriam linhas. Aumente limit em vez disso.

ParâmetroTipoPadrãoSignificado
qstring, 2–120 caracteresobrigatórioArtista, título ou ambos. Três ou mais caracteres usam correspondência por trigramas; consultas mais curtas são uma correspondência de substring.
countryISO 3166-1 alpha-2, repetívelqualquerSó estações desses países.
codecstringqualquerex.: MP3, AAC.
min_bitrateinteger0kbit/s.
classificationenumsongsong, ident, programme, ad_break, empty, junk, unknown ou any.
max_age_seconds30–900180Só linhas cuja estação foi consultada com sucesso dentro desse número de segundos.
limit1–10020Linhas a retornar; truncated: true indica que havia mais.

Resposta · 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 }
}

Como ler uma linha

started_at RFC 3339 · estimativa
Início estimado do título atual, ± start_uncertainty_seconds para mais ou para menos.
observed_at RFC 3339
Quando o Tuneping viu este título pela primeira vez.
confirmed_at RFC 3339
A consulta bem-sucedida mais recente da estação: "ainda tocando até". Use para avaliar se o dado está atual.
track object · opcional
Presente quando o título foi vinculado a uma gravação do MusicBrainz.
similarity 0–1
Corte no limiar que preferir.

GET/v1/now-playing sem chave public, max-age=5 · ETag

Tocando agora em lote

O título atual de uma lista de estações em uma única chamada, para telas de favoritas.

ParâmetroTipoPadrãoSignificado
station_idsUUIDs separados por vírgulaobrigatórioAté 20 no modo anônimo ou Free, 100 a partir do Developer. Os ids são ordenados e deduplicados antes do cache, então dois clientes com as mesmas favoritas compartilham um único corpo em cache.
max_age_seconds30–86400900Linhas mais antigas que isso ainda são retornadas, mas marcadas com 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 sem chave

Tocando agora na estação

Uma linha para uma estação. 404 not_found quando nenhum título foi observado ainda, 404 station_opted_out quando a estação pediu para não ser indexada.

GET/v1/stations GET/v1/stations/{id} sem chave max-age=60 · 300

Estações

O diretório, construído a partir do diretório Radio Browser, mantido pela comunidade, e enriquecido com a forma como o Tuneping lê cada estação.

ParâmetroTipoPadrãoSignificado
qstring—Nome da estação, busca aproximada.
countryalpha-2, repetívelqualquer
tagstring, repetívelqualquerTodas as tags informadas precisam corresponder.
codec, min_bitrateComo na busca.
has_now_playingbooleanfalseSó estações das quais o Tuneping lê títulos atualmente.
sortenumclickcountclickcount (popularidade, decrescente), name, bitrate.
limit1–20050
cursorstring opaca—Vem do next_cursor da página anterior; vale por 24 horas e só com os mesmos filtros.
{ "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 é repassado como texto a partir do diretório; o Tuneping nunca copia nem serve logos.

GET/v1/stations/{id}/history chave · developer + private, max-age=30

Histórico de execuções

As músicas que uma estação tocou, das mais recentes para as mais antigas, com início e fim estimados. A profundidade depende do plano: 7 dias no Developer, 12 meses no Pro e no Business. Pedir um from mais antigo responde 403 forbidden_tier com required_tier, nunca uma janela cortada silenciosamente.

ParâmetroTipoPadrãoSignificado
from, toRFC 3339to = agora, from = to − 24 hNo máximo 31 dias por chamada.
classificationenumsongComo na busca.
limit1–20050
cursorstring opaca—A janela fica congelada no 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 chave no-store

Chaves e uso

/v1/me retorna a chave, o plano e o status do titular, os limites que se aplicam e o uso até agora na janela atual. /v1/me/usage?from=&to= retorna as requisições por dia (só requisições à origem; acertos no cache da borda não são contados).

{ "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 sem chave

Saúde e estatísticas

/v1/health responde 200 com status ok ou degraded, e 503 quando o banco de dados está fora do ar; aponte seu monitor de disponibilidade para ele. /v1/health/series?minutes=60 (5–1440) retorna os números do coletor por minuto: polls, ok, titles_changed, songs, bytes_in, bytes_out, latency_p50_ms, latency_p90_ms, inflight_max, ips_paused, queue_lag_seconds e um detalhamento by_method. /v1/stats é o retrato público da cobertura exibido na página de status: total de estações, consultadas, com metadados ao vivo, mudanças de título por minuto, fração de músicas, por método, por país.

POST/v1/wishlists PUT/v1/wishlists GET/v1/wishlists/matches chave · developer + no-store

Listas de desejos

Indique uma música, um artista ou um título em texto livre e saiba quando uma estação começar a tocá-lo. Cada mudança de título é comparada com as suas listas de desejos no momento em que é lida, e uma nova lista de desejos entra em vigor em até 30 segundos. O Developer guarda 10 listas de desejos, o Pro 100, o Business 1.000. As correspondências são guardadas por 30 dias.

CampoTipoSignificado
kindartist · track · queryartist e track recebem um id da busca (art_…, trk_…) e também capturam as grafias que o normalizador vincula a ele. query recebe texto livre, normalmente Artist - Title.
valuestring, ≤ 240O id ou o texto.
matchexact · fuzzySó para query. Exact compara o texto normalizado; fuzzy aceita uma similaridade por trigramas de 0,6 ou mais.
station_filterobjectstation_ids (≤ 500) e/ou countries (alpha-2 em maiúsculas ou minúsculas, retornados em maiúsculas). Um título corresponde quando sua estação está listada ou fica em um país listado. Sem filtro significa todas as estações.
deliveryarraypoll (padrão), sse, webhook. A entrega por webhook é do Pro para cima e exige webhook_id e uma chave com o escopo webhooks.
descriptionstring, ≤ 200Texto seu.
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

Uma lista explícita de station_ids faz o Tuneping ler essas estações a cada 30 segundos em vez de a cada 60 enquanto a lista de desejos estiver ativa, e a resposta informa isso com tightens: true. A união das estações que você listou tem um teto por plano (limits.tightened_stations em /v1/me); uma lista de desejos que o ultrapassaria responde 422 validation_failed com uma entrada em errors[] cujo code é tightened_stations_exceeded (campo station_filter.station_ids, ou data no PUT). Os filtros por país continuam valendo, no intervalo normal de cada estação.

GET /v1/wishlists/{id}, PATCH (filtro de estações, match, entrega, webhook, descrição, status: enabled | paused) e DELETE funcionam como esperado. PUT /v1/wishlists com {"data": [ … ]} substitui todo o seu conjunto em uma única chamada: itens inalterados mantêm o id e a contagem de correspondências, os demais são criados ou excluídos. É a forma mais fácil de sincronizar uma lista que seu app mantém localmente. O corpo dessa chamada pode ter até 1 MiB; os demais corpos de requisição são limitados a 64 KiB (413 payload_too_large).

Correspondências são um feed incremental (tail feed). GET /v1/wishlists/matches cobre todas as suas listas de desejos e GET /v1/wishlists/{id}/matches uma delas. As linhas vêm das mais antigas para as mais recentes. Guarde o next_cursor de cada resposta e envie-o de volta como since_cursor: ele nunca é null, continua válido durante a retenção de 30 dias e sobrevive a reinicializações. Sem cursor, você recebe as correspondências mais recentes. limit vai de 1 a 500, padrão 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 já vem corrigido para o momento em que o título foi visto, então um gravador pode mostrar "começou há cerca de N ± U segundos". expected_end_hint_at é started_at mais 222 segundos, a duração mediana de uma música: uma dica, não uma previsão. confidence é high quando a correspondência passou por um id de faixa ou artista vinculado, medium para artista e título normalizados, low para consultas de texto.

POST/v1/webhooks GET/v1/webhooks/{id}/deliveries chave · pro + no-store

Webhooks

O Tuneping envia eventos por POST para o seu endpoint HTTPS à medida que acontecem. O Pro tem 5 endpoints, o Business 20.

EventoQuandoO Pro exige
wishlist.matchedUma lista de desejos que indica este endpoint em webhook_id teve uma correspondência.—
station.title_changedA linha de título de uma estação mudou.filters.station_ids (≤ 200)
track.startedUma faixa vinculada começou.filters.track_ids ou artist_ids (≤ 200)
artist.startedUm artista vinculado começou.filters.track_ids ou artist_ids (≤ 200)

Os filtros também podem listar countries e classification. Filtros só por país e fluxos sem filtro de mudanças de título são do Business. Cada endpoint tem um orçamento diário de 50.000 eventos no Pro e 500.000 no Business; acima disso, o endpoint fica paused_budget até o próximo dia UTC e você recebe um 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

A resposta traz o secret de assinatura (whsec_…) uma única vez. As entregas seguem o Standard Webhooks: cabeçalhos webhook-id, webhook-timestamp e webhook-signature: v1,<base64>, em que a assinatura é um HMAC-SHA256 com os bytes do secret sobre {webhook-id}.{webhook-timestamp}.{body}. Rejeite timestamps com mais de 5 minutos de diferença e use webhook-id para descartar duplicatas: ele permanece o mesmo entre as novas tentativas.

{ "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_…" } }

Responda com qualquer 2xx em até 10 segundos. Redirecionamentos não são seguidos. Entregas que falham são repetidas após 5 s, 5 min, 30 min, 2 h, 5 h, 10 h, 14 h, 20 h e 24 h, cerca de três dias no total, uma de cada vez por endpoint. Um endpoint que responde 410, ou que falha em cinco dias seguidos, é desativado e você recebe um e-mail; defina status de volta como enabled com PATCH depois de corrigi-lo.

POST /v1/webhooks/{id}/rotate-secret retorna um novo secret; durante 24 horas, cada entrega traz duas assinaturas, uma por secret. POST /v1/webhooks/{id}/test envia um ping. GET /v1/webhooks/{id}/deliveries?status=pending|succeeded|failed lista 30 dias de entregas, e POST …/deliveries/{dl_id}/redeliver envia uma delas de novo. Chaves de teste (tp_test_…) só podem apontar para webhook.site.

GET/v1/stream chave · developer + text/event-stream

Stream de eventos

Os mesmos eventos em uma única resposta HTTP longa no formato server-sent events, para servidores e apps nativos. Passe station_ids (até 100) ou wishlist_id (all, ou um 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
ParâmetroPadrãoSignificado
station_ids—Ids de estações separados por vírgula, no máximo 100.
wishlist_id—all ou um id de lista de desejos. Envia wishlist.matched.
eventsstation.title_changedPara streams de estações, qualquer um entre station.title_changed, track.started, artist.started.
classificationsongsong ou any, para streams de estações.

data é o mesmo JSON que um webhook recebe. Um comentário : ping chega após 20 segundos sem eventos, então defina o timeout de inatividade do seu cliente para 60 segundos ou mais. Quando a conexão cair, reconecte com o cabeçalho Last-Event-ID definido como o último id que você viu: os eventos dos últimos 10 minutos são reenviados. Uma posição mais antiga recebe um event: gap com o id mais antigo disponível; preencha a lacuna pelo histórico ou pelo feed de correspondências. Streams abertos por chave: Developer 1, Pro 5, Business 20, Tapelark 1 por dispositivo. Um a mais responde 429 too_many_connections.

Navegadores não podem usar EventSource aqui, porque ele não consegue enviar o cabeçalho Authorization e as chaves nunca são aceitas na URL. Leia o stream com fetch().

Campos e enums

classification enum
song · ident (nome ou slogan da estação) · programme · ad_break · empty · junk · unknown. Só músicas entram no histórico de execuções; tudo é mantido na linha ao vivo.
match_grade enum
high · medium · none. Quão bem o título foi vinculado a uma gravação do MusicBrainz.
confidence enum
high · medium · low. Calculada por linha a partir da classificação, do grau de correspondência, de quão recentemente a estação foi confirmada e do histórico de confiabilidade da estação. Documentada para que você possa auditá-la.
method enum
Como o Tuneping lê a estação: icy (metadados dentro do stream), icecast_json (JSON de status do servidor), shoutcast_stats (XML de status), shoutcast_currentsong, seven_html. Identificadores internos, não recomendações; se for exibi-los, mostre-os como "no stream" e "página de status".
recordability enum
Se o stream da estação é um stream HTTP simples e gravável do ponto de vista de um cliente de gravação. Informativo.
Ids
As estações usam o UUID do Radio Browser. Faixas são trk_…, artistas art_…, eventos de execução pe_…, chaves key_…, titulares own_…, listas de desejos wl_…, correspondências wm_…, webhooks wh_…, entregas dl_…, eventos evt_…. Trate todos como strings opacas.
Timestamps
RFC 3339, UTC, com precisão de milissegundos quando a fonte a fornece.

OpenAPI

A descrição legível por máquina desta API está em /docs/openapi-v1.yaml (OpenAPI 3.1). Ela é a fonte para geração de clientes; os ids de operação são estáveis.

Atribuição e uso justo

O uso no plano Free e anônimo exibe um link visível "Data by Tuneping" onde os dados são mostrados (selo). As execuções são detectadas a partir dos metadados das estações e não são monitoramento certificado; diga "detectado a partir dos metadados da estação" onde você as apresentar como evidência. Os nomes das estações são texto; o Tuneping não fornece logos nem artes. Veja os termos.