Seegnals

Getting started

Pagination and filters

One scheme for every list: limit, an opaque cursor and next_cursor. How ordering works, what a keyset cursor guarantees, and the two documented exceptions.

Updated 4 September 2026

Every GET that returns a list answers with the same envelope:

{ "data": [  ], "next_cursor": "eyJrIjoiMjAyNi0wOC0xOVQxMjowMDowMC4wMDArMDA6MDAiLCJpIjoiN2M5ZTY2NzktNzQyNS00MGRlLTk0NGItZTA3ZmMxZjkwYWU3In0" }
  • limit: 1 to 100, default 50. Anything else is 400.
  • cursor: the opaque string from next_cursor of the previous page. Do not build it yourself; a tampered cursor is 400 `cursor` is not valid.
  • next_cursor is null on the last page.
curl "https://app.seegnals.com/api/v1/prospects?status=replied&limit=100" -H "Authorization: Bearer $SEEGNALS_TOKEN"
curl "https://app.seegnals.com/api/v1/prospects?status=replied&limit=100&cursor=eyJr…" -H "Authorization: Bearer $SEEGNALS_TOKEN"
const res = await fetch("https://app.seegnals.com/api/v1/prospects?status=replied&limit=100&cursor=eyJr…", {
  method: "GET",
  headers: {
    "Authorization": `Bearer ${process.env.SEEGNALS_TOKEN}`
  }
});
const json = await res.json().catch(() => null);
console.log(res.status, json);
import os, requests

res = requests.get(
    "https://app.seegnals.com/api/v1/prospects?status=replied&limit=100&cursor=eyJr…",
    headers={"Authorization": f"Bearer {os.environ['SEEGNALS_TOKEN']}"},
    timeout=20,
)
print(res.status_code, res.json() if res.content else None)

Ordering and stability

Lists are newest first: by created_at, received_at or sent_at descending, then by id. The cursor is a keyset over that pair, so a row inserted while you are paging does not shift the pages and never produces a duplicate. You can safely walk a whole list while the campaign keeps sending.

Two exceptions, documented on purpose

  • /companies uses an offset cursor. The temperature filter works on a value computed from the last seven days of events rather than stored in a column, so the list is assembled in memory. A company created during your walk can shift a boundary row. The cursor is still opaque; only the guarantee differs.
  • /offers?status= is applied to the fetched page, because read status is derived from visits. A page can hold fewer than limit rows while next_cursor is not null. Keep paging until it is.

/mailboxes does not paginate at all. Workspaces have tens of mailboxes, so a single response is enough. next_cursor is always null there.

Dates

Timestamps are ISO 8601 with an offset, as Postgres returns them: 2026-09-03T10:00:00.123456+00:00. Date filters (created_since, since) accept any ISO 8601 date or date-time: 2026-09-01 or 2026-09-01T00:00:00Z.

What a list does not offer

  • No updated_since on prospects or campaigns: those tables carry no modification timestamp, and the API refuses the parameter with 400 rather than pretend to filter.
  • No sorting parameters. Sort on your side after fetching.
  • No total counts. Page until next_cursor is null.