Pagination
Parcourez les sorties avec cursor et next_cursor, les livraisons de webhooks avec before et has_more, et ne synchronisez que ce qui a changé.
Mis à jour le
La plupart des listes de l’API sont courtes et reviennent en entier. Deux listes peuvent être longues et sont paginées : les sorties et les livraisons de webhooks.
Les listes paginées
| Endpoint | Pagination | Taille de page |
|---|---|---|
GET /releases |
cursor → next_cursor |
limit de 1 à 100, 50 par défaut |
GET /webhooks/{id}/deliveries |
before → has_more |
limit de 1 à 100, 50 par défaut |
GET /ddex |
Non paginée. Vos 100 dernières livraisons. | 100 |
GET /members |
Non paginée. Tous les artistes et labels. | Tout |
GET /links |
Non paginée. Tous les smart links et pré-sauvegardes. | Tout |
GET /payouts |
Non paginée. Toutes les demandes de retrait. | Tout |
GET /webhooks |
Non paginée. 20 endpoints au maximum. | Tout |
GET /earnings |
Non paginée. Regroupée selon by sur months (1 à 60). |
Tout |
Sorties : curseurs
GET /releases renvoie les sorties de la plus récente à la plus ancienne. Chaque réponse contient next_cursor. Renvoyez-le dans cursor pour obtenir la page suivante. Sur la dernière page, next_cursor vaut 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"
Traitez le curseur comme une chaîne opaque. Ne le construisez pas et ne le modifiez pas vous-même.
Gardez les autres paramètres identiques d’une page à l’autre. Si vous filtrez la première page avec status=approved, envoyez aussi status=approved avec le curseur. Si vous changez un filtre, recommencez sans curseur.
Toutes les sorties, en 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);
}
Utilisez limit=100 pour les lectures en masse : vous divisez par deux le nombre de requêtes décomptées de votre limite de débit.
Pages et nouvelles sorties
Les pages sont découpées selon l’identifiant des sorties. Une sortie créée pendant que vous paginez a un identifiant plus élevé que celles que vous avez déjà lues : elle n’apparaît donc que sur une nouvelle première page. Elle ne crée ni doublon ni trou.
Une sortie qui change de statut pendant que vous paginez garde sa place. Si vous filtrez par status, une sortie peut sortir du filtre entre deux pages. Pour ne rien manquer, synchronisez plutôt par date.
Synchroniser les changements
Pour tenir à jour votre propre copie du catalogue, ne relisez pas la liste complète à chaque fois. Utilisez updated_since :
- Lors de la première exécution, lisez toutes les pages. Notez l’heure de début, en UTC.
- Lors de l’exécution suivante, envoyez
updated_sinceavec cette heure, et paginez comme d’habitude. - Enregistrez la nouvelle heure de début pour l’exécution d’après.
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 retient les sorties modifiées à l’heure envoyée ou après : une sortie modifiée pile à la limite revient donc deux fois. Rendez vos écritures idempotentes sur l’id de la sortie.
Utiliser l’heure de début, et non l’heure de fin, garantit qu’une sortie modifiée pendant l’exécution sera relue la fois suivante. Avec les webhooks, vous pouvez réagir aux changements au moment où ils se produisent, et garder updated_since pour un rattrapage nocturne.
Livraisons de webhooks : before
GET /webhooks/{id}/deliveries renvoie ce qui a été envoyé à un endpoint au cours des 30 derniers jours, du plus récent au plus ancien. La réponse contient has_more. Quand il vaut true, passez l’identifiant de la dernière livraison de la page dans 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;
}
}
Vous pouvez filtrer par status (pending, succeeded ou failed) et par event. Gardez les mêmes filtres d’une page à l’autre.
Erreurs
Un curseur qui ne vient pas de l’API renvoie 400 invalid avec field: "cursor". Une valeur de limit hors de l’intervalle 1–100 n’est pas refusée : elle est ramenée dans l’intervalle, si bien que limit=500 donne des pages de 100, et une valeur de limit non numérique donne la valeur par défaut de 50. Voir Erreurs.