How it works
GET /v1/apps, GET /v1/apps/{slug}/companies and GET /v1/events take:
| Parameter | Meaning |
|---|---|
limit |
Rows per page, 1–500, default 100. On billed lists it is also your spend cap per page. |
cursor |
pagination.nextCursor from the previous page, unchanged. |
and return:
"pagination": { "nextCursor": "eyJrIjoi…", "hasMore": true }
- Send the same filters with every page. A cursor is a position, not an offset: rows added while you page do not shift or repeat earlier rows.
- A page can hold fewer rows than
limitwhilehasMoreis true (filters applied after the scan, or a balance that covered only part of the page). Keep going untilnextCursorisnull. - Cursors are opaque. Do not build or edit one; an edited cursor gets
400 invalid_cursor. GET /v1/eventsruns oldest first. Save the last cursor and resume from it to read only new events next time.
Read a whole list
JavaScript:
async function* pages(path, params) {
let cursor;
do {
const qs = new URLSearchParams({ ...params, ...(cursor ? { cursor } : {}) });
const res = await fetch(`https://api.installgraph.com${path}?${qs}`, {
headers: { Authorization: `Bearer ${process.env.INSTALLGRAPH_API_KEY}` },
});
const body = await res.json();
if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message}`);
yield body.data;
cursor = body.pagination.nextCursor;
} while (cursor);
}
for await (const rows of pages("/v1/apps", { category: "reviews", limit: "500" })) {
for (const app of rows) console.log(app.slug);
}
Python:
import os, requests
def pages(path, params):
headers = {"Authorization": f"Bearer {os.environ['INSTALLGRAPH_API_KEY']}"}
cursor = None
while True:
query = {**params, **({"cursor": cursor} if cursor else {})}
body = requests.get(f"https://api.installgraph.com{path}", params=query, headers=headers).json()
if "error" in body:
raise RuntimeError(f"{body['error']['code']}: {body['error']['message']}")
yield body["data"]
cursor = body["pagination"]["nextCursor"]
if not cursor:
break
for rows in pages("/v1/apps", {"category": "reviews", "limit": 500}):
for app in rows:
print(app["slug"])
On a billed list, count first (GET /v1/apps/{slug}/companies/count, free) and stop once you have the rows you need.