# Paginação > Pagine lançamentos com cursor e next_cursor, pagine entregas de webhook com before e has_more, e sincronize só o que mudou. A maioria das listas da API é curta e vem inteira. Duas listas podem ser longas e são paginadas: lançamentos e entregas de webhook. ## Quais listas são paginadas | Endpoint | Como pagina | Tamanho da página | | --- | --- | --- | | `GET /releases` | `cursor` → `next_cursor` | `limit` de 1 a 100, padrão 50 | | `GET /webhooks/{id}/deliveries` | `before` → `has_more` | `limit` de 1 a 100, padrão 50 | | `GET /ddex` | Não pagina. Suas últimas 100 entregas. | 100 | | `GET /members` | Não pagina. Todos os artistas e gravadoras. | Tudo | | `GET /links` | Não pagina. Todos os smartlinks e pré-saves. | Tudo | | `GET /payouts` | Não pagina. Todas as solicitações de saque. | Tudo | | `GET /webhooks` | Não pagina. No máximo 20 endpoints. | Tudo | | `GET /earnings` | Não pagina. Agrupado por `by` ao longo de `months` (1 a 60). | Tudo | ## Lançamentos: cursores `GET /releases` retorna os lançamentos dos mais recentes para os mais antigos. Cada resposta tem `next_cursor`. Envie-o de volta como `cursor` para obter a próxima página. Na última página, `next_cursor` é `null`. ```bash 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" ``` Trate o cursor como uma string opaca. Não o monte nem o altere por conta própria. Mantenha os outros parâmetros iguais em todas as páginas. Se você filtrou a primeira página com `status=approved`, envie `status=approved` junto com o cursor também. Se mudar um filtro, recomece sem cursor. ### Todos os lançamentos, em Node ```js 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); } ``` Use `limit=100` para leituras em massa. Isso reduz pela metade o número de requisições contadas no seu [limite de requisições](https://developer.hillsmusic.com/pt/guides/rate-limits/). ### Páginas e lançamentos novos As páginas são divididas pelo id do lançamento. Um lançamento criado enquanto você pagina tem um id maior do que os que você já leu, então ele só aparece em uma nova primeira página. Isso não gera duplicatas nem lacunas. Um lançamento que muda de status enquanto você pagina continua no mesmo lugar. Se você filtra por `status`, um lançamento pode sair do filtro entre uma página e outra. Para não perder nada, sincronize por horário. ## Sincronização de mudanças Para manter a sua própria cópia do catálogo atualizada, não leia a lista inteira toda vez. Use `updated_since`: 1. Na primeira execução, leia todas as páginas. Anote o horário em que começou, em UTC. 2. Na execução seguinte, envie `updated_since` com esse horário e pagine normalmente. 3. Guarde o novo horário de início para a próxima execução. ```bash curl "https://api.hillsmusic.com/v1/releases?updated_since=2026-10-18T06:00:00Z&limit=100" \ -H "Authorization: Bearer $HILLS_API_KEY" ``` ```js const startedAt = new Date().toISOString(); for await (const r of allReleases({ updated_since: lastRun })) await upsert(r); lastRun = startedAt; // store it ``` `updated_since` retorna os lançamentos alterados no horário enviado ou depois dele, então um lançamento alterado exatamente no limite volta duas vezes. Torne suas gravações idempotentes pelo `id` do lançamento. Usar o horário de início, e não o de término, faz com que um lançamento alterado durante a execução seja lido de novo na próxima. Com [webhooks](https://developer.hillsmusic.com/pt/guides/webhooks/) você reage às mudanças assim que elas acontecem e mantém `updated_since` como uma conferência noturna. ## Entregas de webhook: before `GET /webhooks/{id}/deliveries` retorna o que foi enviado a um endpoint nos últimos 30 dias, das mais recentes para as mais antigas. A resposta traz `has_more`. Quando ele é `true`, envie o id da última entrega da página como `before`: ```bash 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" ``` ```js 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; } } ``` Você pode filtrar por `status` (`pending`, `succeeded` ou `failed`) e por `event`. Mantenha os filtros iguais em todas as páginas. ## Erros Um cursor que não foi gerado pela API responde `400 invalid` com `field: "cursor"`. Um `limit` fora do intervalo de 1 a 100 não é recusado: ele é ajustado ao intervalo, então `limit=500` gera páginas de 100, e um `limit` que não é número usa o padrão de 50. Veja [Erros](https://developer.hillsmusic.com/pt/guides/errors/). Fonte: https://developer.hillsmusic.com/pt/guides/pagination/