Zum Inhalt springen
HILLS API v1 Early Access

Anleitungen

Paginierung

Releases mit cursor und next_cursor seitenweise lesen, Webhook-Zustellungen mit before und has_more, und nur synchronisieren, was sich geändert hat.

Aktualisiert

Die meisten Listen der API sind kurz und kommen vollständig zurück. Zwei Listen können lang werden und werden seitenweise ausgeliefert: Releases und Webhook-Zustellungen.

Welche Listen paginiert sind

Endpunkt Paginierung Seitengröße
GET /releases cursor → next_cursor limit 1–100, Standard 50
GET /webhooks/{id}/deliveries before → has_more limit 1–100, Standard 50
GET /ddex Keine. Ihre letzten 100 Auslieferungen. 100
GET /members Keine. Alle Artists und Labels. Alle
GET /links Keine. Alle Smart Links und Pre-Saves. Alle
GET /payouts Keine. Alle Auszahlungsanfragen. Alle
GET /webhooks Keine. Höchstens 20 Endpunkte. Alle
GET /earnings Keine. Gruppiert nach by über months (1–60). Alle

Releases: Cursor

GET /releases liefert Releases, die neuesten zuerst. Jede Antwort enthält next_cursor. Geben Sie ihn als cursor zurück, um die nächste Seite zu erhalten. Auf der letzten Seite ist next_cursor gleich null.

curl "https://api.hillsmusic.com/v1/releases?limit=100" \
  -H "Authorization: Bearer $HILLS_API_KEY"
# { "data": [ … ], "next_cursor": "aWQ6ODEy" }

curl "https://api.hillsmusic.com/v1/releases?limit=100&cursor=aWQ6ODEy" \
  -H "Authorization: Bearer $HILLS_API_KEY"

Behandeln Sie den Cursor als undurchsichtige Zeichenkette. Bauen oder verändern Sie ihn nicht selbst.

Lassen Sie die übrigen Parameter auf jeder Seite gleich. Filtern Sie die erste Seite mit status=approved, senden Sie status=approved auch zusammen mit dem Cursor. Ändern Sie einen Filter, beginnen Sie neu ohne Cursor.

Alle Releases in Node

async function* allReleases(params = {}) {
  let cursor = null;
  do {
    const url = new URL("https://api.hillsmusic.com/v1/releases");
    for (const [k, v] of Object.entries({ ...params, limit: 100 })) url.searchParams.set(k, v);
    if (cursor) url.searchParams.set("cursor", cursor);
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.HILLS_API_KEY}` },
    });
    if (!res.ok) throw new Error(`${res.status} ${(await res.json()).code}`);
    const page = await res.json();
    yield* page.data;
    cursor = page.next_cursor;
  } while (cursor);
}

for await (const r of allReleases({ status: "approved" })) {
  console.log(r.id, r.title, r.delivery_state);
}

Verwenden Sie für Massenabfragen limit=100. Das halbiert die Zahl der Anfragen, die auf Ihr Rate Limit angerechnet werden.

Seiten und neue Releases

Seiten werden nach Release-ID geschnitten. Ein Release, das während des Blätterns angelegt wird, hat eine höhere ID als die bereits gelesenen und erscheint daher erst auf einer neuen ersten Seite. Duplikate oder Lücken entstehen dadurch nicht.

Ein Release, dessen Status sich während des Blätterns ändert, bleibt an seiner Stelle. Filtern Sie nach status, kann ein Release zwischen zwei Seiten aus dem Filter herausfallen. Um alles zu erfassen, synchronisieren Sie stattdessen nach Zeit.

Änderungen synchronisieren

Um Ihre eigene Kopie des Katalogs aktuell zu halten, lesen Sie nicht jedes Mal die ganze Liste. Verwenden Sie updated_since:

  1. Lesen Sie beim ersten Durchlauf alle Seiten. Notieren Sie den Startzeitpunkt in UTC.
  2. Senden Sie beim nächsten Durchlauf updated_since mit diesem Zeitpunkt und blättern Sie wie gewohnt.
  3. Speichern Sie den neuen Startzeitpunkt für den darauffolgenden Durchlauf.
curl "https://api.hillsmusic.com/v1/releases?updated_since=2026-10-18T06:00:00Z&limit=100" \
  -H "Authorization: Bearer $HILLS_API_KEY"
const startedAt = new Date().toISOString();
for await (const r of allReleases({ updated_since: lastRun })) await upsert(r);
lastRun = startedAt; // store it

updated_since erfasst Releases, die zum gesendeten Zeitpunkt oder danach geändert wurden. Ein Release, das genau an der Grenze geändert wurde, kommt deshalb zweimal zurück. Gestalten Sie Ihre Schreibvorgänge idempotent anhand der Release-id.

Weil Sie den Start- statt den Endzeitpunkt verwenden, wird ein Release, das sich während des Durchlaufs ändert, beim nächsten Mal erneut gelesen. Mit Webhooks reagieren Sie auf Änderungen, sobald sie passieren, und nutzen updated_since als nächtlichen Abgleich.

Webhook-Zustellungen: before

GET /webhooks/{id}/deliveries liefert, was in den letzten 30 Tagen an einen Endpunkt gesendet wurde, das Neueste zuerst. Die Antwort enthält has_more. Ist es true, übergeben Sie die ID der letzten Zustellung der Seite als before:

curl "https://api.hillsmusic.com/v1/webhooks/7/deliveries?status=failed&limit=100" \
  -H "Authorization: Bearer $HILLS_API_KEY"
# { "data": [ …, { "id": 1001, … } ], "has_more": true }

curl "https://api.hillsmusic.com/v1/webhooks/7/deliveries?status=failed&limit=100&before=1001" \
  -H "Authorization: Bearer $HILLS_API_KEY"
async function failedDeliveries(endpointId) {
  const out = [];
  let before = null;
  for (;;) {
    const url = new URL(`https://api.hillsmusic.com/v1/webhooks/${endpointId}/deliveries`);
    url.searchParams.set("status", "failed");
    url.searchParams.set("limit", "100");
    if (before) url.searchParams.set("before", String(before));
    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.HILLS_API_KEY}` },
    });
    const page = await res.json();
    out.push(...page.data);
    if (!page.has_more || !page.data.length) return out;
    before = page.data.at(-1).id;
  }
}

Sie können nach status (pending, succeeded oder failed) und event filtern. Lassen Sie die Filter auf jeder Seite gleich.

Fehler

Ein Cursor, der nicht von der API stammt, ergibt 400 invalid mit field: "cursor". Ein limit außerhalb von 1–100 wird nicht abgelehnt, sondern in den gültigen Bereich gebracht: limit=500 ergibt Seiten mit 100 Einträgen, und ein limit, das keine Zahl ist, ergibt den Standardwert 50. Siehe Fehler.

Bereit für die Integration?

Beantragen Sie API-Zugang – wir melden uns innerhalb von 1–3 Werktagen bei Ihnen.