Beta. This endpoint is not final. Field names and response shapes can change before general availability.
Every list endpoint in the Finn API returns a page, not a full collection. This page covers the paging model, the filter parameters that are shared across resources, and sorting. Resource-specific filters are documented on the resource pages: calls, finns, deployments, audiences.
Cursor pagination
List endpoints use cursor pagination. You pass a limit, read the results, and pass the returned cursor back to get the next page.
curl "https://api.hirefinn.ai/v1/calls?limit=50" \
-H "Authorization: Bearer $FINN_API_KEY"
{
"object": "list",
"data": [
{ "id": "call_01HZX8QP3M4K", "status": "completed" },
{ "id": "call_01HZX8QP3M4J", "status": "no_answer" }
],
"has_more": true,
"next_cursor": "eyJpZCI6ImNhbGxfMDFIWlg4UVAzTTRKIn0"
}
| Field | Type | Description |
|---|---|---|
object | string | Always list for a paged response. |
data | array | The page of results, ordered by the active sort. |
has_more | boolean | Whether more results exist after this page. |
next_cursor | string or null | Opaque cursor for the next page. Null when has_more is false. |
Request the next page by passing cursor:
curl "https://api.hirefinn.ai/v1/calls?limit=50&cursor=eyJpZCI6ImNhbGxfMDFIWlg4UVAzTTRKIn0" \
-H "Authorization: Bearer $FINN_API_KEY"
Cursors are opaque. Do not parse one, build one, or store one long term. A cursor encodes the sort and filter parameters of the request that produced it, so reusing a cursor with different filter or sort values returns 400 invalid_request. See error codes.
Whether the API also returns a total count is not yet settled. Do not write code that depends on a total field. Drive your loop off has_more.
| Parameter | Default | Notes |
|---|---|---|
limit | 25 | Maximum page size is not final. Requests above the cap are rejected rather than silently clamped, so read the error body instead of assuming you got what you asked for. |
cursor | none | Omit for the first page. |
Iterating a full collection
async function listAllCalls(apiKey: string): Promise<unknown[]> {
const results: unknown[] = [];
let cursor: string | null = null;
do {
const url = new URL("https://api.hirefinn.ai/v1/calls");
url.searchParams.set("limit", "50");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (!res.ok) {
throw new Error(`${res.status} ${await res.text()}`);
}
const page = await res.json();
results.push(...page.data);
cursor = page.has_more ? page.next_cursor : null;
} while (cursor);
return results;
}
import requests
BASE = "https://api.hirefinn.ai/v1"
def list_all_calls(api_key):
results = []
cursor = None
while True:
params = {"limit": 50}
if cursor:
params["cursor"] = cursor
res = requests.get(
f"{BASE}/calls",
headers={"Authorization": f"Bearer {api_key}"},
params=params,
timeout=30,
)
res.raise_for_status()
page = res.json()
results.append_all = None
results.extend(page["data"])
if not page["has_more"]:
return results
cursor = page["next_cursor"]
Two things go wrong with loops like this. First, paging a large collection burns rate limit quota fast, and a 429 mid-loop will drop you out with a partial result unless you retry. See rate limits. Second, the collection can change while you page it. New calls arriving during a descending-order walk are not picked up, and records that move can be seen twice or missed. For anything that has to be exact, filter to a closed time range instead of walking the live tail.
Filtering
Filters are query parameters. Unknown parameters are rejected with 400 invalid_request rather than ignored, so a typo fails loudly instead of returning an unfiltered page you mistake for a filtered one.
Timestamps are ISO 8601 in UTC. Range filters use _after and _before suffixes and are inclusive on the lower bound, exclusive on the upper.
curl -G "https://api.hirefinn.ai/v1/calls" \
-H "Authorization: Bearer $FINN_API_KEY" \
--data-urlencode "created_after=2026-07-01T00:00:00Z" \
--data-urlencode "created_before=2026-07-08T00:00:00Z" \
--data-urlencode "limit=100"
| Parameter | Applies to | Description |
|---|---|---|
created_after | all list endpoints | Include records created at or after this timestamp. |
created_before | all list endpoints | Include records created strictly before this timestamp. |
status | calls, deployments | Exact match on the resource status value. |
Repeating a parameter to mean OR (status=completed&status=failed) is not settled. Until it is, send one value per parameter and merge client side.
Sorting
sort takes a field name with a direction prefix. - is descending, no prefix is ascending. The default is -created_at, newest first.
curl "https://api.hirefinn.ai/v1/calls?sort=created_at&limit=50" \
-H "Authorization: Bearer $FINN_API_KEY"
Only created_at is guaranteed sortable on every list endpoint. Other fields vary by resource and are listed on the resource pages. Sorting on an unsupported field returns 400 invalid_request.
Because cursors encode the sort, changing sort means starting over from the first page. There is no way to re-sort mid-walk.
See API conventions for the shared request and response rules, and errors for the full status code list.