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:
- Lesen Sie beim ersten Durchlauf alle Seiten. Notieren Sie den Startzeitpunkt in UTC.
- Senden Sie beim nächsten Durchlauf
updated_sincemit diesem Zeitpunkt und blättern Sie wie gewohnt. - 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.