API と Webhook

ここでの操作はすべて、あなたのアカウントがメンバーになっているアーティストに対して、それぞれのロールの範囲で行われます。リクエストもレスポンスも HTTPS 上の JSON です。

API と Webhook は Label プラン(30日間の無料体験を含む)で使えます。キーと Webhook はダッシュボードの「API」で作成します。

https://listenthru.com/api/v1 · release.created release.live fan.confirmed

認証

ダッシュボードの API で個人用のキーを作成し、読み取りか読み取りと書き込みを選びます。キーは一度だけ表示されます。シークレット管理に保存し、Web ページや配布するアプリには入れないでください。形式は lt_0123456789ab_… です。

すべてのリクエストで Bearer トークンとして送ります。キーはあなたとして動き、あなたがメンバーのアーティストを、それぞれのロールで許される範囲で操作します。キーはダッシュボードからいつでも無効にでき、すぐに使えなくなります。各キーには最後に使われた日時が表示されます。

curl https://listenthru.com/api/v1/artists \
  -H "Authorization: Bearer $LISTENTHRU_KEY"

ロールとスコープ

ロールできること
viewer(閲覧者)リリースと分析を見る。
editor(編集者)加えて、リリースとアーティストページの作成・編集。
admin(管理者)加えて、メンバー、広告の計測、ファンとその書き出し。
owner(オーナー)加えて、請求、リリースの削除、オーナーの引き継ぎ。

読み取りキーで使えるのは GET だけです。変更には読み取りと書き込みキーと、それに見合うロールの両方が必要です。

レート制限

キーごとに1分あたり 60 リクエストまで。すべてのレスポンスに X-RateLimit-Limit、X-RateLimit-Remaining、X-RateLimit-Reset(秒)が付きます。超えると 429 と Retry-After を返します。

エラー

エラーは HTTP ステータスと、分岐に使える変わらない code で返します。message は人が読むためのもので、変わることがあります。原因の入力がわかる場合は field に入ります。

HTTP/1.1 403 Forbidden
{ "error": { "code": "insufficient_scope", "message": "This key can only read. Make a read and write key to change things." } }
コードステータス意味
unauthorized401キーが送られていません。
invalid_key401キーが正しくないか、無効にされています。
plan_required402Label(または体験)が終了しているか、アーティストが Pro ではありません。
insufficient_scope403読み取りキーで変更しようとしました。
forbidden403このアーティストでのロールでは許されていません。
not_found404アーティスト、リリース、エンドポイントがありません。
method_not_allowed405パスはありますが、そのメソッドは使えません。
invalid_request400送った値が正しくありません。field を確認してください。
conflict409その slug は使われています。
unresolvable422リンク、ISRC、UPC が見つかりませんでした。
rate_limited429リクエストが多すぎます。Retry-After まで待ってください。
internal_error500こちら側の問題です。もう一度お試しください。

アーティスト

あなたのアカウントがメンバーのアーティストと、それぞれでのロール。

GET/api/v1/artists読み取りキー · viewer
curl https://listenthru.com/api/v1/artists \
  -H "Authorization: Bearer $LISTENTHRU_KEY"

レスポンス

{
  "data": [
    { "id": "8saidwait", "name": "8saidwait", "role": "owner", "plan": "pro",
      "url": "https://listenthru.com/8saidwait", "createdAt": "2026-10-01T03:00:00.000Z" }
  ]
}
GET/api/v1/artists/{artist}読み取りキー · viewer

リリース

ストアのリンク(Spotify、Apple Music、YouTube、Deezer、プリセーブページ)か、isrc(曲)または upc(アルバム・EP)からリリースページを作ります。タイトル、アートワーク、配信のある各サービスへのリンクが自動で入り、自分で送った値が優先されます。"status": "live" を送らない限り下書きで作成されます。任意項目:title、subtitle、releaseDate(YYYY-MM-DD)、kind(single、ep、album、video)、slug、links、artworkUrl、country(探すストアの国。既定は JP)。

リリース日まではページにプリセーブのボタンが出て、リリース日に listenthru が各サービスのリリースそのものへのリンクに切り替えます(これが release.live の Webhook です)。

GET/api/v1/artists/{artist}/releases読み取りキー · viewer
curl https://listenthru.com/api/v1/artists/8saidwait/releases \
  -H "Authorization: Bearer $LISTENTHRU_KEY"
GET/api/v1/artists/{artist}/releases/{slug}読み取りキー · viewer
curl https://listenthru.com/api/v1/artists/8saidwait/releases/say-it-again \
  -H "Authorization: Bearer $LISTENTHRU_KEY"

レスポンス

{
  "data": {
    "slug": "say-it-again", "url": "https://listenthru.com/8saidwait/say-it-again",
    "title": "say it again", "subtitle": "", "status": "live", "releaseDate": "2026-10-16",
    "kind": "single", "isrc": "JPAB01234567", "upc": "",
    "artwork": "https://listenthru.com/media/a/8saidwait/…webp",
    "links": [{ "id": "a1b2c3d4", "service": "spotify", "label": "Spotify", "url": "https://open.spotify.com/track/…" }],
    "createdAt": "…", "updatedAt": "…", "upgradedAt": ""
  }
}
POST/api/v1/artists/{artist}/releases読み取りと書き込みキー · editor

リンクから

curl -X POST https://listenthru.com/api/v1/artists/8saidwait/releases \
  -H "Authorization: Bearer $LISTENTHRU_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://open.spotify.com/track/…", "releaseDate": "2026-11-20"}'

ISRC から

curl -X POST https://listenthru.com/api/v1/artists/8saidwait/releases \
  -H "Authorization: Bearer $LISTENTHRU_KEY" \
  -H "Content-Type: application/json" \
  -d '{"isrc": "JPAB01234567", "status": "live"}'
PATCH/api/v1/artists/{artist}/releases/{slug}読み取りと書き込みキー · editor
curl -X PATCH https://listenthru.com/api/v1/artists/8saidwait/releases/say-it-again \
  -H "Authorization: Bearer $LISTENTHRU_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "live", "subtitle": "out now"}'

分析

ダッシュボードと同じ数字です。訪問、聴きに行った数(サービスへのタップ)、訪問の流入元、オーディエンス。days は Pro と Label で 1〜90(Free は 7 まで)。

GET/api/v1/artists/{artist}/insights?days=28&release={slug}読み取りキー · viewer
GET/api/v1/artists/{artist}/releases/{slug}/insights?days=28読み取りキー · viewer
curl "https://listenthru.com/api/v1/artists/8saidwait/insights?days=28" \
  -H "Authorization: Bearer $LISTENTHRU_KEY"

curl "https://listenthru.com/api/v1/artists/8saidwait/releases/say-it-again/insights?days=7" \
  -H "Authorization: Bearer $LISTENTHRU_KEY"

レスポンス

{
  "data": {
    "days": 28, "from": "20261001", "to": "20261028",
    "totals": { "views": 1520, "clicks": 610, "rate": 0.401, "presaves": 42, "reminders": 18 },
    "series": [{ "day": "20261001", "views": 40, "clicks": 15 }, …],
    "byService": [{ "service": "spotify", "label": "Spotify", "clicks": 380 }, …],
    "bySource": [{ "source": "instagram", "views": 700, "clicks": 300, "rate": 0.43 }, …],
    "byRelease": […], "byCountry": […], "byDevice": […], "byApp": […], "byCampaign": […],
    "countryEstimated": true
  }
}

ファン

確認済みのファン登録(ダブルオプトイン)を古い順に返します。管理者とオーナーのみ。since で新しい登録だけを取得できます。

GET/api/v1/artists/{artist}/fans?since={ISO time}読み取りキー · admin
curl "https://listenthru.com/api/v1/artists/8saidwait/fans?since=2026-10-01T00:00:00Z" \
  -H "Authorization: Bearer $LISTENTHRU_KEY"

レスポンス

{
  "data": [
    { "email": "fan@example.com", "confirmedAt": "2026-10-02T11:04:00.000Z",
      "source": "instagram", "release": "say-it-again", "lang": "ja" }
  ]
}

Webhook

ダッシュボードの API で https のアドレスを追加し、イベントを選びます。あなたが管理者かオーナーのアーティストでイベントが起きるたびに、JSON を POST します。署名用のシークレットは一度だけ表示されます。

イベントタイミング
release.createdダッシュボードか API でリリースページが作られたとき。
release.liveリリース日、プリセーブのボタンが各サービスのリリースに切り替わったとき。
fan.confirmedファンが登録を確認したとき。メールアドレスを含みます。

署名の検証

X-Listenthru-Signature は t=<UNIX 時刻>,v1=<"t.本文" の HMAC-SHA256(16進)> で、鍵はあなたのシークレットです。受け取ったままの本文で照合し、古いタイムスタンプは拒否してください。10秒以内に 2xx を返し、時間のかかる処理はその後に。同じイベントが二度届くことがあるので、id で重複を除いてください。

POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: listenthru-webhooks/1 (+https://listenthru.com/developers)
X-Listenthru-Event: release.live
X-Listenthru-Delivery: dlv_6f0c…
X-Listenthru-Signature: t=1792900000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

{
  "id": "evt_0b6c…", "type": "release.live", "created": "2026-11-20T00:05:12.000Z",
  "artist": { "id": "8saidwait", "name": "8saidwait" },
  "data": { "release": { "slug": "say-it-again", "status": "live", "links": […], … } }
}

Node.js

import { createHmac, timingSafeEqual } from 'node:crypto';

// raw: the request body exactly as received (a string or Buffer), before any JSON parsing
export function verify(raw, header, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
  const want = createHmac('sha256', secret).update(`${t}.${raw}`).digest();
  const got = Buffer.from(parts.v1 || '', 'hex');
  return got.length === want.length && timingSafeEqual(got, want);
}

再送

2xx が返らない(またはタイムアウトした)配信は、およそ 1、2、4、8、16 時間後に再送します(合計 6 回)。10 回続けて失敗すると Webhook は停止し、ダッシュボードに表示されます。ワンクリックで再開できます。リダイレクトはたどりません。プライベート、ループバック、リンクローカルのアドレスには送れません。