Flora CodexFlora Codex

Pagination

Every endpoint that returns a list of records is paginated. A page holds up to 20 records, and you move through pages with the page query parameter. Each response tells you how big the full result is and gives you ready-made URLs for the pages around you, so you rarely have to build a paging URL by hand.

Requesting a page

Pages are 1-based. Omit page and you get the first one.

curl "https://api.floracodex.com/v2/species?page=2" \
  -H "Authorization: ApiKey YOUR_KEY"
const res = await fetch("https://api.floracodex.com/v2/species?page=2", {
  headers: { Authorization: "ApiKey YOUR_KEY" },
});
const data = await res.json();
import requests

res = requests.get(
    "https://api.floracodex.com/v2/species",
    params={"page": 2},
    headers={"Authorization": "ApiKey YOUR_KEY"},
)
data = res.json()

The page size is fixed at 20 and is the same across every collection. There is no parameter to change it.

The response

A paginated response carries three top-level keys: data is the page of records, links is the set of URLs for navigating, and meta describes the result as a whole.

{
  "data": [],
  "links": {
    "self": "/v2/species?page=2",
    "first": "/v2/species?page=1",
    "prev": "/v2/species?page=1",
    "next": "/v2/species?page=3",
    "last": "/v2/species?page=25"
  },
  "meta": {
    "total": 481,
    "per_page": 20,
    "current_page": 2,
    "last_page": 25
  }
}

The meta block

Four fields, enough to render a page indicator or build your own paginator:

  • total is the number of records that matched, across all pages.
  • per_page is the page size. Always 20.
  • current_page is where you are now, 1-based.
  • last_page is the index of the final page, equal to ceil(total / per_page).

If you only need the result count, read total from page one and stop. You do not have to walk the pages to know how many there are.

The links block hands you the URLs for the pages around the current one, so walking a result set is a matter of following next until it is gone:

  • self is the canonical URL for the page you are on.
  • first and last are the ends of the result.
  • prev and next are the neighbors.

Two omissions tell you where the edges are. prev is absent on the first page, and next is absent on the last. Treat their presence, not the page number, as the signal to keep going:

let url = "https://api.floracodex.com/v2/species";
do {
  const res = await fetch(url, {
    headers: { Authorization: "ApiKey YOUR_KEY" },
  });
  const page = await res.json();
  for (const record of page.data) handle(record);
  url = page.links.next ? `https://api.floracodex.com/v2${page.links.next.replace("/v2", "")}` : null;
} while (url);
import requests

url = "https://api.floracodex.com/v2/species"
while url:
    res = requests.get(
        url, headers={"Authorization": "ApiKey YOUR_KEY"}
    )
    page = res.json()
    for record in page["data"]:
        handle(record)
    nxt = page["links"].get("next")
    url = f"https://api.floracodex.com/v2{nxt.replace('/v2', '')}" if nxt else None

Every link preserves the query parameters from your original request. If you asked for ?q=oak&order[year]=desc, the next and prev URLs carry that search and sort forward, so each page lands on the same window. You do not have to re-attach your filters when paging.

Empty results

When nothing matches, data is an empty array and meta.total is 0. The links block keeps self but drops first, last, prev, and next, so there is no "page 1 of 0" to special-case. Check total or the length of data, not the links.

Last updated 16 June 2026