Skip to main content
CornectAPI Docsv1
Sign inGet an API tokenGet started free

Pagination

This API uses two pagination models, and which one applies depends on the endpoint. Check this table before writing a paging loop — code written for one will not work against the other.

EndpointModelControls
POST /companies/searchCursorcursor, limit — default 20, max 100
GET /companies/{id}/job-postingsOffsetpage (default 1), page_size — default 10, max 50
Why job postings are the exception

A company's open roles are a small, stable set, so a row cannot shift between pages the way it can in a 13-million-row search. Offset paging is safe there and simpler to consume. Everywhere else, cursors are what keep a page from skipping or repeating rows while the underlying data moves.

POST /companies/search returns results in pages using cursor-based pagination. Instead of asking for a page number, each response hands you an opaque, signed cursor that points at the next page. Treat it as a token: pass it back exactly as received. Cursors are tamper-checked, so an edited or hand-built one is rejected with 422 INVALID_CURSOR rather than quietly returning a different page.

Page size#

limit controls how many companies a search returns per page: 20 by default, 100 at most. Asking for more than 100 is a 422 naming the field rather than a silently truncated page, so a client cannot believe it received 500 rows and receive 100.

Job postings use page_size instead — 10 by default, 50 at most — with page starting at 1. The two endpoints do not share a parameter name or a default, so code written for one needs changing for the other.

How cursors work#

The search response includes a next_cursor field. To fetch the next page, send it back in the request body as cursor. When next_cursor is null, you've reached the end.

json
// Response shape
{
  "items": [ /* companies */ ],
  "total": 4213,
  "aggregations": { /* facet counts */ },
  "next_cursor": "{\"a\":[10,\"0004be64-…\"],\"s\":\"relevance:desc:f\"}"
  // null on the last page
}

The exact contents differ by search type and may change without notice — never parse or construct one. A lookalike search returns a cursor in a different, signed format again.

Paging through results#

Loop until next_cursor is null, passing the previous cursor each time:

async function* allCompanies(filters, token) {
  let cursor = null;
  do {
    const res = await fetch("https://api.cornect.io/api/v1/companies/search", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${token}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ ...filters, cursor }),
    });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const page = await res.json();
    for (const company of page.items) yield company;
    cursor = page.next_cursor;
  } while (cursor);
}

Best practices#

  • Treat the cursor as opaque — don't parse or construct it yourself; its format may change. Cursors are signed, so a modified one is rejected with 422 INVALID_CURSOR.
  • Keep sort_by and sort_order identical across pages of the same scan. A cursor is only valid for the ordering that produced it; changing the sort mid-scan returns 422 CURSOR_SORT_MISMATCH.
  • Keep your filters identical too — but note this is not enforced. Changing a filter mid-scan is accepted and silently resumes from the old cursor position against the new filter set, which can skip or repeat rows. Restart from the first page instead.
  • Respect the 60/min limit on search while paging — for large scans, add backoff (see Rate Limits & Backoff).
  • Use total to show progress, but page until next_cursor is null rather than computing page counts from it.
Exports vs. paging
For a file rather than pages, an export is usually better than paging the search endpoint — it's built for bulk and dedups against companies you've already exported.
A single export is capped at 10,000 companies
This matters if your filter matches more than that. The export succeeds, returns the first 10,000, and sets capped_at_10k: true in the preview and create responses — it does not fail, and it does not tell you in the file itself. Check that flag.

To cover a larger set, split it into narrower filters (by country, employee band, or industry) and run one export per slice. Use counts first to size a filter before exporting it — it is exact, uncapped, and costs no credits.