Pagination
A list returns one page and says whether more follow. Send the cursor of one page to get the next, and stop when has_more is false.
A list endpoint returns one page of results and tells you whether there are more. To read on, send the page's next_cursor back as cursor. There are no page numbers and no total count.
curl "https://api.useinfrared.com/v1/webhooks/events?limit=2" \
-H "Authorization: Bearer $INFRARED_KEY"const response = await fetch("https://api.useinfrared.com/v1/webhooks/events?limit=2", {
headers: { Authorization: `Bearer ${process.env.INFRARED_KEY}` },
});
const page = await response.json();import os
import requests
response = requests.get(
"https://api.useinfrared.com/v1/webhooks/events",
headers={"Authorization": f"Bearer {os.environ['INFRARED_KEY']}"},
params={
"limit": 2,
},
)
page = response.json(){"object": "list","data": [{ "object": "webhook_event", "id": "evt_3JBjspfu9XetX1AI2E8K6G", "type": "moderation.completed", "created_at": "2026-10-08T08:55:24.332Z" },{ "object": "webhook_event", "id": "evt_5KRfjncmk4C517vNiS950P", "type": "queue_item.created", "created_at": "2026-10-08T08:55:24.071Z" }],"has_more": true,"next_cursor": "WyIyMDI2LTEwLTA4VDA4OjU1OjI0LjA3MVoiLCJhZjFlYWI2MS1iYzg3LTQ1ZGUtYmI1OS0wNzA1YTNiOTk0NjUiXQ"}The next page is the same request with that cursor:
curl "https://api.useinfrared.com/v1/webhooks/events?limit=2&cursor=WyIyMDI2LTEwLTA4VDA4OjU1OjI0LjA3MVoiLCJhZjFlYWI2MS1iYzg3LTQ1ZGUtYmI1OS0wNzA1YTNiOTk0NjUiXQ" \
-H "Authorization: Bearer $INFRARED_KEY"const response = await fetch("https://api.useinfrared.com/v1/webhooks/events?limit=2&cursor=WyIyMDI2LTEwLTA4VDA4OjU1OjI0LjA3MVoiLCJhZjFlYWI2MS1iYzg3LTQ1ZGUtYmI1OS0wNzA1YTNiOTk0NjUiXQ", {
headers: { Authorization: `Bearer ${process.env.INFRARED_KEY}` },
});
const page = await response.json();import os
import requests
response = requests.get(
"https://api.useinfrared.com/v1/webhooks/events",
headers={"Authorization": f"Bearer {os.environ['INFRARED_KEY']}"},
params={
"limit": 2,
"cursor": "WyIyMDI2LTEwLTA4VDA4OjU1OjI0LjA3MVoiLCJhZjFlYWI2MS1iYzg3LTQ1ZGUtYmI1OS0wNzA1YTNiOTk0NjUiXQ",
},
)
page = response.json()Parameters and the answer
limitintegerDefault20How many items a page holds, from 1 to 100. A value outside that range answers 422
validation_error.cursorstringThe
next_cursorof the page before. Leave it out for the first page.
Every list answers with the same four fields.
objectstringAlways
list.dataarrayThe items of this page.
has_morebooleantruewhen another page follows.next_cursorstringWhat to send as
cursorto get the next page.nullon the last page.
The cursor is opaque: pass it back as you received it, and do not build one or read anything from it. A value the API did not issue answers 400 invalid_cursor.
Read every page
Loop until has_more is false.
const events = [];
let cursor;
do {
const url = new URL("https://api.useinfrared.com/v1/webhooks/events");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const response = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.INFRARED_KEY}` },
});
const page = await response.json();
events.push(...page.data);
cursor = page.has_more ? page.next_cursor : null;
} while (cursor);import os
import requests
events = []
params = {"limit": 100}
while True:
response = requests.get(
"https://api.useinfrared.com/v1/webhooks/events",
headers={"Authorization": f"Bearer {os.environ['INFRARED_KEY']}"},
params=params,
)
page = response.json()
events.extend(page["data"])
if not page["has_more"]:
break
params["cursor"] = page["next_cursor"]Each page is a request, and each request counts against the key's rate limit.
Order
A list has a fixed order, and a page continues exactly after the last item of the one before. Items created while you are paging do not shift the pages or repeat an item.
Keep the filters and the sort the same from the first page to the last. A cursor marks a position in one ordering: with another filter or another sort it points nowhere useful.
| List | Order | Its own parameters |
|---|---|---|
GET /v1/moderations | Newest first | Filters such as template, status and author_id |
GET /v1/authors | By sort: activity (the default), trust, risk or created. order is desc (the default) or asc | status, trust_level |
GET /v1/authors/{external_id}/moderations | Newest first | None |
GET /v1/queue | Oldest first. sort=desc turns it around | state, template, language, severity, priority |
GET /v1/wordlists/{id}/terms | Oldest first | limit goes up to 500 here, and its default is 100 |
GET /v1/webhooks/events | Newest first | type |
GET /v1/webhooks/{id}/deliveries | Newest first | status |
Authors sorted by activity or risk are ordered by a value that changes as they post. An author whose value changes between two of your requests can appear twice or be skipped. Sort by created when you need every author exactly once.
Lists that return everything
Six lists are short by nature and always come back whole, with has_more: false and next_cursor: null: GET /v1/templates, GET /v1/policies, GET /v1/guidelines, GET /v1/wordlists, GET /v1/actions and GET /v1/webhooks.
The answer of a batch has the same shape, with one entry per item you sent.
Next
- ErrorsEvery error has one JSON shape. Branch on its code, show its details and quote its request id. The table lists each code the API returns and when.
- Rate limitsEach API key may send a set number of requests a minute, by plan. Every response says where the key stands, and a 429 says how long to wait.
- Live consoleEvery moderation of a project as it arrives, with its verdict, scores, evidence and media. Open one to see what your integration received and why.
Something missing or unclear on this page? Write to support@useinfrared.com.