---
title: "Pagination, Filtering, and Cursors: How the unLocked CRM API Handles Large Result Sets"
description: "Cursor-based pagination, sparse fieldsets, range filters, and 'include' expansions — the four primitives that make our list endpoints fast at 10 contacts and at 10 million."
url: https://unlockedcrm.ai/blog/openapi-pagination-filtering-cursor-best-practices
canonical: https://unlockedcrm.ai/blog/openapi-pagination-filtering-cursor-best-practices
category: "Reference"
published: 2026-07-30
updated: 2026-07-30
author: "unLocked Team"
source: unLocked CRM — AI CRM for insurance agents
---

# Pagination, Filtering, and Cursors: How the unLocked CRM API Handles Large Result Sets

<p>Every CRM eventually has to answer "give me everything that matches X." How that question is asked and answered is the difference between an API that scales and one that times out at 10,000 records. Here's how unLocked CRM handles it.</p>

<h2>Cursor-Based Pagination (Not Offset)</h2>
<p>Every list endpoint accepts <code>limit</code> (max 200) and <code>cursor</code>. The response includes <code>data</code>, <code>next_cursor</code> (null when exhausted), and <code>has_more</code>. Cursor pagination is stable under concurrent writes — you'll never see the duplicate-or-skip artifacts that plague offset pagination.</p>

<pre><code>GET /contacts?limit=100&cursor=eyJpZCI6...</code></pre>

<h2>Filtering With Operators</h2>
<p>Filters use the bracket syntax familiar from JSON:API:</p>
<pre><code>GET /contacts?filter[created_at][gte]=2025-01-01
            &filter[lead_source][in]=facebook,google
            &filter[has_policy]=false</code></pre>
<p>Supported operators: <code>eq</code>, <code>neq</code>, <code>gt</code>, <code>gte</code>, <code>lt</code>, <code>lte</code>, <code>in</code>, <code>nin</code>, <code>like</code>, <code>null</code>.</p>

<h2>Sparse Fieldsets</h2>
<p>Pull only what you need to keep payloads small:</p>
<pre><code>GET /contacts?fields=id,first_name,last_name,email</code></pre>

<h2>Include Expansions</h2>
<p>Avoid the N+1 trap by expanding related resources inline:</p>
<pre><code>GET /contacts/123?include=policies,opportunities,tags</code></pre>

<h2>Sorting</h2>
<pre><code>GET /contacts?sort=-created_at,last_name</code></pre>
<p>Prefix with <code>-</code> for descending. Multiple sort keys are comma-separated.</p>

<h2>The Net Effect</h2>
<p>The same <code>/contacts</code> endpoint serves a 10-contact dashboard widget and a 10-million-row nightly export. No special "bulk" routes, no separate "search" endpoint, no surprises.</p>

---

Source: [Pagination, Filtering, and Cursors: How the unLocked CRM API Handles Large Result Sets](https://unlockedcrm.ai/blog/openapi-pagination-filtering-cursor-best-practices) — unLocked CRM, the AI CRM built for insurance agents. Citation permitted with attribution and a link to https://unlockedcrm.ai/blog/openapi-pagination-filtering-cursor-best-practices.
