Tuneping API v1

İnternet radyosu istasyonlarının şu an ne çaldığını ve daha önce ne çaldığını gösteren bir JSON API. Temel URL https://api.tuneping.com/v1. Yalnızca meta veri: hizmet her akışın herkese açık başlık satırını okur ve sese asla dokunmaz.

Herkese açık betaCanlı uç noktalar (arama, toplu sorgu, istasyonda şu an çalan, istasyon dizini, sağlık ve istatistikler) anahtar olmadan, IP adresi başına sınırlı olarak çalışır. Anahtarlar kayıt sayfasında self servis olarak alınır: Free hemen, Developer ve Pro ise Stripe Checkout'tan sonra. Business için [email protected] adresine yazın.

Hızlı başlangıç

Şu an Madonna'yı kim çalıyor?

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

Kimliğini bildiğiniz bir istasyonda şu an çalan (kimlikler Radio Browser istasyon UUID'leridir):

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

Bir istasyonun son 24 saatte çaldıkları (geçmiş erişimi olan bir anahtar gerekir, Developer planı veya üzeri):

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

Her yanıt JSON'dur. Hatalar application/problem+json biçimindedir (bkz. Hatalar). Bilinmeyen sorgu parametreleri yok sayılmak yerine 400 ile reddedilir; böylece yazım hataları hemen ortaya çıkar.

Anahtar alma

tuneping.com/signup üzerinden self servis: bir e-posta adresi girin, gönderilen bağlantıyı 60 dakika içinde açın; anahtar bir kez gösterilir. Aynı iki adım bir API olarak da vardır: {"email": "…", "plan": "free|developer|pro"} ile POST /v1/signup 202 ile yanıt verir ve e-postayı gönderir; bağlantıdaki {"token": "…"} ile POST /v1/signup/verify anahtarla birlikte 201 ile yanıt verir. Free için başka bir şey gerekmez. Developer ve Pro, yanıttaki bağlantıdan Stripe Checkout ile devam eder; sahibin planı ödemeden sonraki bir dakika içinde değişir ve sahibin tüm anahtarları buna uyar.

Anahtarınızı mı kaybettiniz? Aynı adresle yeniden kaydolun: anahtar yenilenir, eskisi 24 saat daha çalışır. Free ve Developer'da bir canlı anahtar, Pro'da beş, Business'ta yirmi; ayrıca her planda, portal yayına girene kadar talep üzerine verilen bir tp_test_ anahtarı.

Kimlik doğrulama

Anahtarınızı bearer token olarak veya X-Api-Key içinde gönderin:

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

Anahtarlar tp_live_ veya tp_test_ ile başlar ve ardından 43 karakter gelir. Anahtarsız bir istek anonim kabul edilir ve IP adresine göre sınırlanır. Hatalı biçimlendirilmiş, bilinmeyen veya iptal edilmiş bir anahtar, anonim olarak çalışacak uç noktalarda bile hemen 401 ile yanıt alır. Aşağıda anahtar ile işaretlenen uç noktalar anahtar olmadan 401 unauthenticated ile yanıt verir.

Kapsamlar. Anahtarlar, daha dar bir listeyle (örneğin salt okunur) verilmedikçe tüm kapsamları (read, exports, webhooks, wishlists) taşır; GET /v1/me bir anahtarın kapsamlarını listeler. İstek listeleri ve webhook'lar wishlists ve webhooks kapsamlarını gerektirir (webhook teslimatlı bir istek listesi ikisini de gerektirir ve bu, plandan önce kontrol edilir). Kapsamı olmayan bir anahtar required_scope ile 403 forbidden_scope yanıtı alır ve WWW-Authenticate, error="insufficient_scope" taşır.

Test anahtarları. Bir tp_test_ anahtarı aynı canlı verileri Free sınırlarıyla okur ve kendi istek listelerine, eşleşmelerine ve webhook uç noktalarına sahiptir (livemode: false). Canlı olanları asla görmez veya değiştirmez; böylece bir test paketi üretim ortamına dokunmadan boş bir istek listesi kümesini PUT ile gönderebilir. Test modundaki webhook'lar yalnızca webhook.site adresini gösterebilir.

Yeniden denemeler. Anahtar veya iş ortağı token'ı gerektiren uç noktalardaki POST, PATCH ve DELETE, bir Idempotency-Key başlığı kabul eder (mantıksal istek başına bir UUID); kayıt uç noktaları bunu kabul etmez. Aynı anahtar ve aynı istekle yapılan bir yeniden deneme, ikinci bir istek listesi veya uç nokta oluşturmak yerine ilk başarılı yanıtı Idempotent-Replayed: true işaretiyle yeniden alır. Anahtar 24 saat hatırlanır; farklı bir istek için yeniden kullanılması 422 idempotency_key_reused, ilk istek hâlâ çalışırken yapılan yeniden deneme ise 409 idempotency_in_progress ile sonuçlanır. Hatalar hatırlanmaz; bu nedenle bir hatadan sonraki yeniden deneme tekrar çalışır. Kopan bir bağlantı, bu başlığı taşıyan bir isteği iptal etmez: istek tamamlanır ve başarılı yanıt hatırlanır, böylece yeniden denemeniz onu alır. 20 saniye sonra hâlâ çalışan bir istek, hiçbir şey değiştirilmeden iptal edilir ve Retry-After: 1 ile 503 unavailable yanıtı verir; aynı anahtarla yeniden deneyin.

İş ortağı uygulamaları (şu an Tapelark) anahtar yerine imzalı bir kullanım hakkı token'ı gönderir: Authorization: Bearer TPE1-…. Token, iş ortağının Ed25519 anahtarlarına karşı çevrimdışı doğrulanır ve aşağıdaki tablodaki iş ortağı planına karşılık gelir; API son kullanıcının kim olduğunu asla öğrenmez. Hatalı biçimlendirilmiş veya bilinmeyen bir token 401 token_invalid, süresi dolmuş bir token 401 token_expired (yenileyin), başka bir hedef kitle için verilmiş bir token 401 token_audience, süresi dolmuş veya iptal edilmiş bir kullanım hakkı ise 403 entitlement_revoked yanıtı alır.

Hız sınırları ve kotalar

PlanHızKotaToplu kimlikGeçmiş
Anonim (IP başına)2 req/s, anlık en fazla 51.000 / gün20yok
Free2 req/s, anlık en fazla 51.000 / gün20yok
Developer10 req/s, anlık en fazla 20100.000 / ay1007 gün
Pro50 req/s, anlık en fazla 1001.000.000 / ay10012 ay
Businessözelözel10012 ay
Tapelark (kullanım hakkı token'ı, lisans başına)5 req/s, anlık en fazla 102.000 / gün507 gün

Saniye başına sınır bir token kovasıdır. Kova boşaldığında Retry-After ile 429 rate_limited alırsınız. Kota tükendiğinde, Retry-After pencerenin sıfırlanmasına kalan saniyeye ayarlanmış olarak 429 quota_exceeded alırsınız.

Paylaşılan önbelleğe alınamayan kimliği doğrulanmış yanıtlar, IETF hız sınırı alanlarını taşır:

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

Paylaşılan önbelleğe alınabilen yanıtlar (arama, toplu sorgu, istasyonda şu an çalan, istasyonlar, istatistikler, sağlık serileri) hız sınırı başlıkları taşımaz, çünkü uç önbellek aynı gövdeyi başka bir çağırana sunabilir. Kalan bütçeniz her zaman GET /v1/me üzerinden görülebilir.

Önbelleğe alma

Canlı yanıtlar 5 saniye önbelleğe alınabilir (Cache-Control: public, max-age=5, s-maxage=5) ve zayıf bir ETag taşır. Hiçbir şey değişmediğinde 304 almak için If-None-Match gönderin; 304, kaynak sunucuda kotanızdan düşülmez. İstasyon dizini 60 saniye, tek bir istasyon 300 saniye önbellekte tutulur. Geçmiş private, max-age=30 şeklindedir. Sağlık ve anahtar uç noktaları no-store şeklindedir.

Her yanıt X-Request-Id taşır. Destek ekibine yazarken bunu belirtin.

Hatalar

Hatalar, iki uzantı üyesiyle RFC 9457 problem ayrıntılarına uyar: kararlı, makine tarafından okunabilir bir dize olan code ve doğrulama hatalarındaki errors[] veya 403'teki required_tier gibi uç noktaya özgü ek alanlar.

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." } ]
}

Kodların anlamlarıyla birlikte tam listesi hata kataloğunda yer alır. İlk karşılaşacaklarınız:

HTTPcodeNe zaman
400invalid_requestBilinmeyen sorgu parametresi veya hatalı biçimlendirilmiş istek
400invalid_cursorİmleç hatalı biçimlendirilmiş, süresi dolmuş (24 sa; eşleşme akışının since_cursor değeri için 30 gün) veya farklı filtrelerle kullanılmış
401unauthenticated · invalid_api_key · key_revokedAnahtar eksik, bilinmiyor veya iptal edilmiş
403forbidden_tierUç nokta veya parametre daha yüksek bir plan gerektiriyor; hangisi olduğunu required_tier belirtir
403forbidden_scopeAnahtar bu kapsam olmadan verilmiş; hangisi olduğunu required_scope belirtir
404not_found · station_opted_outBilinmeyen kimlik veya istasyon dizine eklenmemeyi istemiş
422validation_failedAlan hataları errors[] içinde
429rate_limited · quota_exceededYavaşlayın; Retry-After saniye cinsinden
503unavailableVeritabanı kullanılamıyor veya Idempotency-Key taşıyan bir istek 20 saniye sonra iptal edildi; Retry-After sonrasında yeniden deneyin

GET/v1/now-playing/search anahtar gerekmez public, max-age=5 · ETag

Şu an hangi istasyonlar q çalıyor. Bulanık eşleşmeli ve aksanlara duyarsız: gronemeyer manner, Grönemeyer – Männer sonucunu bulur. Satırlar benzerliğe, ardından başlığın ne kadar yakın zamanda göründüğüne göre sıralanır. Sayfalama yoktur: dizin birkaç saniyede bir değişir, bu yüzden sayfalar satır atlar veya tekrarlar. Bunun yerine limit değerini yükseltin.

ParametreTürVarsayılanAnlamı
qstring, 2–120 karakterzorunluSanatçı, başlık veya ikisi birden. Üç veya daha fazla karakter trigram eşleştirmesi kullanır; daha kısa sorgular alt dize eşleşmesidir.
countryISO 3166-1 alpha-2, tekrarlanabilirherhangiYalnızca bu ülkelerdeki istasyonlar.
codecstringherhangiörn. MP3, AAC.
min_bitrateinteger0kbit/s.
classificationenumsongsong, ident, programme, ad_break, empty, junk, unknown veya any.
max_age_seconds30–900180Yalnızca istasyonu bu kadar saniye içinde başarıyla sorgulanmış satırlar.
limit1–10020Döndürülecek satır sayısı; truncated: true daha fazlası olduğunu belirtir.

Yanıt · 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 }
}

Bir satırı okumak

started_at RFC 3339 · tahmin
Güncel başlığın tahmini başlangıcı, her iki yönde ± start_uncertainty_seconds.
observed_at RFC 3339
Tuneping'in bu başlığı ilk gördüğü zaman.
confirmed_at RFC 3339
İstasyonun en son başarılı sorgulaması: "şu ana kadar hâlâ çalıyor". Güncelliği ölçmek için bunu kullanın.
track object · isteğe bağlı
Başlık bir MusicBrainz kaydına bağlandığında bulunur.
similarity 0–1
Kendi eşiğinize göre kesin.

GET/v1/now-playing anahtar gerekmez public, max-age=5 · ETag

Toplu şu an çalan

Favori ekranları için, bir istasyon listesinin güncel başlığı tek çağrıda.

ParametreTürVarsayılanAnlamı
station_idsvirgülle ayrılmış UUID'lerzorunluAnonim veya Free için en fazla 20, Developer'dan itibaren 100. Kimlikler önbelleğe almadan önce sıralanır ve yinelenenler ayıklanır; böylece aynı favorilere sahip iki istemci önbellekteki tek bir gövdeyi paylaşır.
max_age_seconds30–86400900Bundan eski satırlar yine döndürülür ancak is_stale: true ile işaretlenir.
{ "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 anahtar gerekmez

İstasyonda şu an çalan

Tek bir istasyon için tek bir satır. Henüz hiçbir başlık görülmediğinde 404 not_found, istasyon dizine eklenmemeyi istediğinde 404 station_opted_out.

GET/v1/stations GET/v1/stations/{id} anahtar gerekmez max-age=60 · 300

İstasyonlar

Topluluk tarafından sürdürülen Radio Browser dizininden oluşturulan ve Tuneping'in her istasyonu nasıl okuduğu bilgisiyle zenginleştirilen dizin.

ParametreTürVarsayılanAnlamı
qstring—İstasyon adı, bulanık eşleşme.
countryalpha-2, tekrarlanabilirherhangi
tagstring, tekrarlanabilirherhangiVerilen tüm etiketler eşleşmelidir.
codec, min_bitrateAramadaki gibi.
has_now_playingbooleanfalseYalnızca Tuneping'in şu anda başlık okuduğu istasyonlar.
sortenumclickcountclickcount (popülerlik, azalan), name, bitrate.
limit1–20050
cursoropak dize—Önceki sayfanın next_cursor değerinden; 24 saat ve yalnızca aynı filtrelerle geçerlidir.
{ "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 dizinden metin olarak aktarılır; Tuneping logoları asla kopyalamaz veya sunmaz.

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

Çalma geçmişi

Bir istasyonun çaldığı şarkılar, en yeniden başlayarak, tahmini başlangıç ve bitişle. Derinlik plana bağlıdır: Developer'da 7 gün, Pro ve Business'ta 12 ay. Daha eski bir from istemek, sessizce daraltılmış bir pencere değil, required_tier ile 403 forbidden_tier yanıtı verir.

ParametreTürVarsayılanAnlamı
from, toRFC 3339to = şimdi, from = to − 24 saÇağrı başına en fazla 31 gün.
classificationenumsongAramadaki gibi.
limit1–20050
cursoropak dize—Pencere imlecin içine sabitlenir.
{ "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 anahtar no-store

Anahtarlar ve kullanım

/v1/me anahtarı, sahibinin planını ve durumunu, geçerli sınırları ve mevcut penceredeki şu ana kadarki kullanımı döndürür. /v1/me/usage?from=&to= gün başına istekleri döndürür (yalnızca kaynak sunucuya ulaşan istekler; uç önbellek isabetleri sayılmaz).

{ "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 anahtar gerekmez

Sağlık ve istatistikler

/v1/health, status değeri ok veya degraded olan bir 200 ile, veritabanı çöktüğünde 503 ile yanıt verir; çalışma süresi izleyicinizi buna yönlendirin. /v1/health/series?minutes=60 (5–1440) dakika başına sorgulayıcı değerlerini döndürür: polls, ok, titles_changed, songs, bytes_in, bytes_out, latency_p50_ms, latency_p90_ms, inflight_max, ips_paused, queue_lag_seconds ve bir by_method dökümü. /v1/stats, durum sayfasında gösterilen herkese açık kapsam anlık görüntüsüdür: toplam istasyon, sorgulanan, canlı meta verili, dakika başına başlık değişikliği, şarkı oranı, yönteme göre, ülkeye göre.

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

İstek listeleri

Bir şarkı, bir sanatçı veya serbest metinli bir başlık belirtin; bir istasyon onu çalmaya başladığında haberiniz olsun. Her başlık değişikliği okunduğu anda istek listelerinizle karşılaştırılır ve yeni bir istek listesi 30 saniye içinde etkin olur. Developer 10, Pro 100, Business 1.000 istek listesi tutar. Eşleşmeler 30 gün saklanır.

AlanTürAnlamı
kindartist · track · queryartist ve track aramadan gelen bir kimlik alır (art_…, trk_…) ve normalleştiricinin o kimliğe bağladığı yazımları da yakalar. query serbest metin alır, normalde Artist - Title.
valuestring, ≤ 240Kimlik veya metin.
matchexact · fuzzyYalnızca query için. Exact normalleştirilmiş metni karşılaştırır; fuzzy 0,6 veya üzeri trigram benzerliğini kabul eder.
station_filterobjectstation_ids (≤ 500) ve/veya countries (herhangi bir harf büyüklüğünde alpha-2, büyük harfle döndürülür). Bir başlık, istasyonu listelendiğinde veya listelenen bir ülkede olduğunda eşleşir. Filtre yoksa tüm istasyonlar demektir.
deliveryarraypoll (varsayılan), sse, webhook. Webhook teslimatı Pro ve üzeri içindir; webhook_id ve webhooks kapsamına sahip bir anahtar gerektirir.
descriptionstring, ≤ 200Size ait.
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

Açık bir station_ids listesi, istek listesi etkin olduğu sürece Tuneping'in bu istasyonları her 60 yerine her 30 saniyede bir okumasını sağlar ve yanıt bunu tightens: true ile belirtir. Listelediğiniz istasyonların birleşimi plana göre sınırlıdır (/v1/me içindeki limits.tightened_stations); bu sınırı aşacak bir istek listesi, code değeri tightened_stations_exceeded olan bir errors[] girdisiyle 422 validation_failed yanıtı verir (alan station_filter.station_ids veya PUT için data). Ülke filtreleri, her istasyonun normal aralığında eşleşmeye devam eder.

GET /v1/wishlists/{id}, PATCH (istasyon filtresi, eşleşme, teslimat, webhook, açıklama, status: enabled | paused) ve DELETE beklendiği gibi çalışır. {"data": [ … ]} ile PUT /v1/wishlists, tüm kümenizi tek çağrıda değiştirir: değişmeyen öğeler kimliklerini ve eşleşme sayılarını korur, geri kalanlar oluşturulur veya silinir. Uygulamanızın yerelde tuttuğu bir listeyi eşitlemenin en kolay yolu budur. Gövdesi 1 MiB'a kadar olabilir; diğer istek gövdeleri 64 KiB ile sınırlıdır (413 payload_too_large).

Eşleşmeler bir kuyruk akışıdır. GET /v1/wishlists/matches tüm istek listelerinizi, GET /v1/wishlists/{id}/matches ise bunlardan birini kapsar. Satırlar en eskiden başlayarak gelir. Her yanıttaki next_cursor değerini saklayın ve since_cursor olarak geri gönderin: asla null değildir, 30 günlük saklama süresi boyunca geçerli kalır ve yeniden başlatmalardan etkilenmez. İmleç olmadan en yeni eşleşmeleri alırsınız. limit 1–500 arasıdır, varsayılan 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, başlığın görüldüğü zamana göre zaten düzeltilmiştir; böylece bir kayıt uygulaması "yaklaşık N ± U saniye önce başladı" gösterebilir. expected_end_hint_at, started_at artı medyan şarkı uzunluğu olan 222 saniyedir: bir tahmin değil, bir ipucu. confidence, eşleşme bağlı bir parça veya sanatçı kimliği üzerinden gerçekleştiğinde high, normalleştirilmiş sanatçı ve başlık için medium, metin sorguları için low olur.

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

Webhook'lar

Tuneping, olayları gerçekleştikleri anda HTTPS uç noktanıza gönderir. Pro'da 5, Business'ta 20 uç nokta vardır.

OlayNe zamanPro'da gerekenler
wishlist.matchedwebhook_id içinde bu uç noktayı belirten bir istek listesi eşleşti.—
station.title_changedBir istasyonun başlık satırı değişti.filters.station_ids (≤ 200)
track.startedBağlı bir parça başladı.filters.track_ids veya artist_ids (≤ 200)
artist.startedBağlı bir sanatçı başladı.filters.track_ids veya artist_ids (≤ 200)

Filtreler ayrıca countries ve classification listeleyebilir. Yalnızca ülke içeren filtreler ve başlık değişikliklerinin filtresiz akışları Business içindir. Her uç noktanın günlük olay bütçesi Pro'da 50.000, Business'ta 500.000'dir; bu aşıldığında uç nokta bir sonraki UTC gününe kadar paused_budget durumuna geçer ve bir e-posta alırsınız.

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

Yanıt, imzalama secret değerini (whsec_…) bir kez içerir. Teslimatlar Standard Webhooks belirtimine uyar: webhook-id, webhook-timestamp ve webhook-signature: v1,<base64> başlıkları; burada imza, gizli anahtarın baytlarıyla {webhook-id}.{webhook-timestamp}.{body} üzerinde hesaplanan HMAC-SHA256'dır. 5 dakikadan fazla sapan zaman damgalarını reddedin ve yinelenenleri ayıklamak için webhook-id kullanın: yeniden denemelerde aynı kalır.

{ "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 saniye içinde herhangi bir 2xx ile yanıt verin. Yönlendirmeler izlenmez. Başarısız teslimatlar 5 s, 5 dk, 30 dk, 2 sa, 5 sa, 10 sa, 14 sa, 20 sa ve 24 sa sonra, toplamda yaklaşık üç gün boyunca, uç nokta başına birer birer yeniden denenir. 410 ile yanıt veren veya art arda beş gün başarısız olan bir uç nokta devre dışı bırakılır ve bir e-posta alırsınız; sorun giderildikten sonra PATCH ile status değerini yeniden enabled yapın.

POST /v1/webhooks/{id}/rotate-secret yeni bir gizli anahtar döndürür; 24 saat boyunca her teslimat, her gizli anahtar için bir tane olmak üzere iki imza taşır. POST /v1/webhooks/{id}/test bir ping gönderir. GET /v1/webhooks/{id}/deliveries?status=pending|succeeded|failed 30 günlük teslimatları listeler ve POST …/deliveries/{dl_id}/redeliver birini yeniden gönderir. Test anahtarları (tp_test_…) yalnızca webhook.site adresini gösterebilir.

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

Olay akışı

Sunucular ve yerel uygulamalar için, aynı olaylar server-sent events biçiminde tek bir uzun HTTP yanıtı üzerinden. station_ids (en fazla 100) veya wishlist_id (all ya da tek bir wl_… kimliği) parametrelerinden birini gönderin.

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
ParametreVarsayılanAnlamı
station_ids—Virgülle ayrılmış istasyon kimlikleri, en fazla 100.
wishlist_id—all veya bir istek listesi kimliği. wishlist.matched gönderir.
eventsstation.title_changedİstasyon akışları için station.title_changed, track.started, artist.started değerlerinden herhangi biri.
classificationsongİstasyon akışları için song veya any.

data, bir webhook'un aldığı JSON'un aynısıdır. Olay olmadan geçen 20 saniyeden sonra bir : ping yorumu gelir; bu yüzden istemcinizin boşta kalma zaman aşımını 60 saniye veya daha fazlasına ayarlayın. Bağlantı koptuğunda, Last-Event-ID başlığını gördüğünüz son id değerine ayarlayarak yeniden bağlanın: son 10 dakikadaki olaylar yeniden oynatılır. Daha eski bir konum, mevcut en eski kimlikle birlikte bir event: gap alır; boşluğu geçmişten veya eşleşme akışından doldurun. Anahtar başına açık akışlar: Developer 1, Pro 5, Business 20, Tapelark cihaz başına 1. Bir fazlası 429 too_many_connections yanıtı alır.

Tarayıcılar burada EventSource kullanamaz, çünkü Authorization başlığını gönderemez ve anahtarlar URL'de asla kabul edilmez. Bunun yerine akışı fetch() ile okuyun.

Alanlar ve enum'lar

classification enum
song · ident (istasyon adı veya sloganı) · programme · ad_break · empty · junk · unknown. Yalnızca şarkılar çalma geçmişine girer; her şey canlı satırda tutulur.
match_grade enum
high · medium · none. Başlığın bir MusicBrainz kaydına ne kadar iyi bağlandığı.
confidence enum
high · medium · low. Her satır için sınıflandırmadan, eşleşme derecesinden, istasyonun ne kadar yakın zamanda doğrulandığından ve istasyonun geçmiş performansından hesaplanır. Denetleyebilmeniz için belgelenmiştir.
method enum
Tuneping'in istasyonu nasıl okuduğu: icy (akış içi meta veri), icecast_json (sunucu durum JSON'u), shoutcast_stats (durum XML'i), shoutcast_currentsong, seven_html. Dahili tanımlayıcılardır, onay anlamına gelmez; gösterecekseniz "akış içi" ve "durum sayfası" olarak gösterin.
recordability enum
İstasyonun akışının, bir kayıt istemcisinin bakış açısından düz, kaydedilebilir bir HTTP akışı olup olmadığı. Bilgi amaçlıdır.
Kimlikler
İstasyonlar Radio Browser UUID'lerini kullanır. Parçalar trk_…, sanatçılar art_…, çalma olayları pe_…, anahtarlar key_…, sahipler own_…, istek listeleri wl_…, eşleşmeler wm_…, webhook'lar wh_…, teslimatlar dl_…, olaylar evt_… biçimindedir. Hepsini opak dizeler olarak ele alın.
Zaman damgaları
RFC 3339, UTC, kaynakta varsa milisaniye hassasiyetinde.

OpenAPI

Bu API'nin makine tarafından okunabilir tanımı /docs/openapi-v1.yaml adresindedir (OpenAPI 3.1). İstemci üretimi için kaynak budur; işlem kimlikleri (operation id) kararlıdır.

Kaynak gösterme ve adil kullanım

Free plan ve anonim kullanımda, verilerin gösterildiği yerde görünür bir "Data by Tuneping" bağlantısı gösterilir (rozet). Çalmalar istasyon meta verisinden algılanır ve onaylı izleme değildir; bunları kanıt olarak sunduğunuz yerde "istasyon meta verisinden algılanmıştır" deyin. İstasyon adları metindir; Tuneping logo veya görsel sağlamaz. Bkz. koşullar.