Aller au contenu
API HILLS v1 Accès anticipé

Guides

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 :

  1. Lors de la première exécution, lisez toutes les pages. Notez l’heure de début, en UTC.
  2. Lors de l’exécution suivante, envoyez updated_since avec cette heure, et paginez comme d’habitude.
  3. 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.

Prêt à vous intégrer ?

Demandez l’accès à l’API : nous vous répondons sous 1–3 business days.