Using the API
Pagination
Organizations' contacts page per organization inside a batch, with a limit of 1 to 50 and an opaque cursor per item; follow each result's nextCursor until it is null.
The one endpoint that can return many records for an item — organizations' contacts, POST /organizations/contacts/match — pages each organization's contacts with a cursor. The cursor lives on the item, not on the request: one batch can read the first page of some organizations and the next page of others. Everything else answers a batch of at most 25 items in one response.
Request
| Field | Meaning |
|---|---|
limit | Top level. Contacts per organization, 1 to 50. Default 20. An item with a cursor ignores it: the first page fixed the page size. |
items[].cursor | Leave it out for an organization's first page. For its next page, send the nextCursor of that organization's previous result. The item then needs no identifier. |
curl https://api.funnelfeedr.com/external/v1/organizations/contacts/match \
-H "Authorization: Bearer $FUNNELFEEDR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "ref": "crm-1001", "domain": "nordviklogistik.se" },
{ "ref": "crm-1002", "orgNumber": "5590123456", "countryCode": "SE" }
],
"limit": 50
}'
Response
Each matched result holds one page in contacts, next to totalCount, filteredByTargetRoles and nextCursor:
{
"results": [
{
"ref": "crm-1001",
"status": "matched",
"matchedBy": "domain",
"organization": {
"id": "6f9d2f1e-6f37-4a2f-9a3e-1b5c0f4d7e21",
"name": "Nordvik Logistik AB",
"detailsRevealed": false,
"countryCode": "SE",
"domain": "nordviklogistik.se",
"city": "Göteborg",
"legalForm": "Aktiebolag",
"foundedYear": 2004,
"url": "https://app.funnelfeedr.com/organizations/6f9d2f1e-6f37-4a2f-9a3e-1b5c0f4d7e21"
},
"contacts": [
{
"id": "c0a8012e-5b1f-4e3d-9a7c-2f6b8d4e1a90",
"name": "Anna Svensson-Berg",
"jobTitle": "Head of Procurement",
"isExecutive": false,
"isDecisionMaker": true,
"organizationId": "6f9d2f1e-6f37-4a2f-9a3e-1b5c0f4d7e21",
"organizationName": "Nordvik Logistik AB",
"email": "XXXXX@nordviklogistik.se",
"emailIsMasked": true,
"phone": "+46701XXXXXX",
"phoneIsMasked": true,
"linkedInUrl": "https://linkedin.com/in/anna-svensson-berg"
}
],
"totalCount": 63,
"filteredByTargetRoles": true,
"nextCursor": "eyJzIjoib3JnYW5pemF0aW9uLWNvbnRhY3RzOjZmOWQyZjFlNmYzNzRhMmY5YTNlMWI1YzBmNGQ3ZTIxIiwicCI6MiwibiI6NTB9"
}
]
}
totalCount counts every contact the endpoint returns for the organization, across pages. nextCursor is on every matched result, and null on the organization's last page. Send the cursors that are not null back as items, and repeat until none are left:
import os, requests
url = "https://api.funnelfeedr.com/external/v1/organizations/contacts/match"
headers = {"Authorization": f"Bearer {os.environ['FUNNELFEEDR_API_KEY']}"}
items = [
{"ref": "crm-1001", "domain": "nordviklogistik.se"},
{"ref": "crm-1002", "orgNumber": "5590123456", "countryCode": "SE"},
]
while items:
body = requests.post(url, headers=headers, json={"items": items, "limit": 50}).json()
items = []
for result in body["results"]:
if result["status"] != "matched":
continue
for contact in result["contacts"]:
print(result["ref"], contact["name"], contact.get("jobTitle"))
if result["nextCursor"]:
items.append({"ref": result["ref"], "cursor": result["nextCursor"]})
Each round is one request against the search rate limit, however many organizations it pages. Keep a round to 25 items: add first pages of new organizations to the same request while there is room.
Cursors are opaque
A cursor is a token, not a page number. Don't decode, build or change it; pass it back exactly as you got it. It names its organization, so you can send it alone; if you send identifiers beside it, they must name the same organization. A cursor that has been altered, or whose item names another organization, makes that item invalid with the error invalid_cursor — the rest of the batch still runs. Cursors do not expire, but the contacts behind them can change between pages: a contact added, or one that no longer matches your target roles, can shift the rest of the list by a row.