Skip to content

Lists & pagination

Some endpoints return a list of things — your API keys, your projects. Kenly uses two shapes for those lists, and the rule for which is which is easy: almost every list is a plain array, and exactly one (your project library) comes wrapped in an envelope so you can page through it. Each operation page tells you which shape it returns, so you never have to guess — but here's the whole story.

Bare arrays (the default)

Most list endpoints return a plain JSON array. There's no wrapper object around it — you just loop over it directly.

json
[
  { "id": "…", "name": "reporting", "prefix": "kenly_sk_live_ab12", "scopes": ["projects:read"] },
  { "id": "…", "name": "ci-pipeline", "prefix": "kenly_sk_live_cd34", "scopes": ["projects:write"] }
]
javascript
const keys = await (await fetch("https://api.getkenly.com/v1/api-keys", {
  headers: { Authorization: `Bearer ${process.env.KENLY_API_KEY}` },
})).json();
for (const key of keys) console.log(key.name, key.prefix); // it's already an array

For example, GET /v1/api-keys hands back a bare array.

The PagedResult envelope

The one exception is the project library — GET /v1/projects. Because you might have a lot of projects, and because you can search and sort them, this one comes back as a PagedResult: an object that holds one page of results plus the counts you need to fetch the rest.

json
{ "items": [ /* … */ ], "total": 128, "page": 1, "pageSize": 25 }
  • items — the array of results for this page (this is the part you iterate).
  • total — how many there are across all pages, so you can show "128 projects" or draw page controls.
  • page / pageSize — which page you're on (counting from 1) and how many are on it.

To walk the whole library, keep asking for the next page until you've seen everything — that is, until page * pageSize >= total:

python
import os, requests

base = "https://api.getkenly.com/v1/projects"
headers = {"Authorization": f"Bearer {os.environ['KENLY_API_KEY']}"}

page, all_items = 1, []
while True:
    res = requests.get(base, headers=headers, params={"page": page, "pageSize": 25}).json()
    all_items.extend(res["items"])
    if page * res["pageSize"] >= res["total"]:
        break
    page += 1

Telling them apart

Quick rule of thumb: if the response is an object with an items array inside it, it's a PagedResult; if the response simply is an array, it's a bare list. And you never have to rely on your memory — each operation page shows the exact response shape.

Source of truth: the list convention is single-sourced in the repo at docs/api-contract.md §3; this page renders it for developers.