Skip to main content
Every list endpoint in the Topo API returns the same envelope structure so you can handle pagination consistently across all resources. Instead of cursor-based navigation, Topo uses classic page-and-size parameters — straightforward to implement, easy to restart, and safe to cache by page number.

Response envelope

All list responses wrap their records in a standard pagination envelope:
items
array
required
The records on the current page.
total_count
integer
required
Total number of records matching the current query (across all pages).
total_pages
integer
required
Total number of pages given the current size value.
has_more
boolean
required
true if there are additional pages after the current one; false on the last page.
Example
{
  "items": [
    { "id": "018f1a2b-3c4d-7e8f-9a0b-1c2d3e4f5a6b", "email": "alex@example.com" },
    { "id": "019a2b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c", "email": "jordan@example.com" }
  ],
  "total_count": 142,
  "total_pages": 15,
  "has_more": true
}

Pagination parameters

Control which page of results you receive using these query parameters on any list endpoint:
page
integer
default:"1"
The page number to retrieve. Must be 1 or greater.
size
integer
default:"10"
Number of records to return per page. Must be between 1 and 100 inclusive.
size is capped at 100 records per request. To retrieve more than 100 records, increment page on successive requests until has_more is false.

Sorting

Most list endpoints accept sort_by and sort_order query parameters to control the order of results:
sort_by
string
The field name to sort by (e.g. created_at, email). If omitted, the endpoint uses its default ordering. Refer to individual endpoint documentation for the supported sort fields.
sort_order
string
default:"asc"
Sort direction. Accepted values: asc (ascending) or desc (descending).

Filtering

Most list endpoints accept additional query parameters to narrow results. Common filter parameters include:
ParameterTypeDescription
emailstringFilter by exact email address
statusstringFilter by record status
created_afterISO 8601 datetimeInclude only records created after this timestamp
created_beforeISO 8601 datetimeInclude only records created before this timestamp
Filter parameters vary by resource — see each endpoint’s reference for the full list of supported filters.
All query parameters use snake_case naming. No aliasing or camelCase variants are accepted.

Example: paginated request with sorting and filtering

Retrieve the second page of contacts, 25 per page, sorted by most recently created, filtered to a specific email domain:
curl 'https://api.topo.io/v1/contacts?page=2&size=25&sort_by=created_at&sort_order=desc' \
  -H "Authorization: Bearer topo_xxxxxxxxxxxx"
Iterate through all records To fetch every record matching a query, loop until has_more is false:
import requests

API_KEY = "topo_xxxxxxxxxxxx"
BASE_URL = "https://api.topo.io/v1"

def fetch_all_contacts():
    page = 1
    all_contacts = []

    while True:
        response = requests.get(
            f"{BASE_URL}/contacts",
            headers={"Authorization": f"Bearer {API_KEY}"},
            params={"page": page, "size": 100, "sort_by": "created_at", "sort_order": "asc"},
        )
        response.raise_for_status()
        data = response.json()

        all_contacts.extend(data["items"])

        if not data["has_more"]:
            break
        page += 1

    return all_contacts

Envelope stability

The { items, total_count, total_pages, has_more } envelope shape is frozen — it will not change within /v1. You can safely build your deserialization logic against this structure.