> Reverse Contact API documentation. This is the Markdown copy of https://docs.reversecontact.com/use-cases/build-a-list-of-people
> Every page is listed at https://docs.reversecontact.com/llms.txt, every endpoint with its credit cost and status at https://docs.reversecontact.com/catalog.json

# Build a list of people

> Build a prospect list with the people search API. Count matches for free, filter by title and location, use prompts or lookalikes, and cap credits.

- Last verified: 2026-11-04

Find people by job title, company and location, describe your audience in a prompt, or find people similar to a known profile. You can count a filtered search for free before retrieving profiles. Search returns full profiles synchronously from the index, within a budget you set.

## Which endpoint

| Input you have | Endpoint | Cost | Mode |
| --- | --- | --- | --- |
| Filters to count matches before buying profiles | [People search count](https://docs.reversecontact.com/endpoints/search-people-count) | Free | Synchronous |
| Explicit filters | [People search](https://docs.reversecontact.com/endpoints/search-people) | 1 credit per profile delivered. An empty page is free | Synchronous |
| A description of the people to find | [Agentic Search](https://docs.reversecontact.com/endpoints/search-people-prompt) | 1 credit per page plus 1 per profile delivered | Synchronous |
| One seed profile URL or person id | [People lookalike](https://docs.reversecontact.com/endpoints/search-people-lookalike) | 1 credit per profile delivered. An empty page is free | Synchronous |

## How it works

Set `RC_API_KEY` to your [API key](https://docs.reversecontact.com/authentication). Install the [TypeScript SDK](https://docs.reversecontact.com/sdk/javascript), [Python SDK](https://docs.reversecontact.com/sdk/python) or [CLI](https://docs.reversecontact.com/guides/cli) for your chosen example. Shell examples use `jq`. Prompt and lookalike searches are alternatives to filtered search, each with its own charges.

### Count your target audience for free

Count people whose current title matches `CTO` and whose location is France. The response contains `data.count` without any profiles. Leave `cursor` and `perPage` out of the count request, then use the same filters for the paid search.

**Code Examples**

```bash [cURL]
curl --fail-with-body -sS -X POST https://api.reversecontact.com/v2/search/people/count \
  -H "Authorization: Bearer $RC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"currentPositionTitle": "CTO", "location": {"country": "France"}}'
```
```typescript [TypeScript SDK]
import ReverseContact from "@reverse-contact/sdk";

const client = new ReverseContact({ apiKey: process.env.RC_API_KEY });

const count = await client.search.countPeople({
  currentPositionTitle: "CTO",
  location: { country: "France" }
});
console.log("Matching people:", count.data.count);
```
```python [Python SDK]
import os

from reversecontact import ReverseContact

with ReverseContact(api_key=os.environ["RC_API_KEY"]) as client:
    count = client.search.count_people(
        current_position_title="CTO", location={"country": "France"}
    )
    print("Matching people:", count.data.count if count.data else None)
```

### Read profiles within a credit budget

Retrieve at most 100 profiles for at most 100 credits. Request 25 at a time and reduce the final request to the remaining budget. The raw API returns profiles in `data.data` and the next cursor in `data.metadata.nextCursor`. SDK iterators handle pagination, and the CLI counts for free before searching.

**Code Examples**

```bash [cURL]
remaining=100
cursor=""
filters='{"currentPositionTitle": "CTO", "location": {"country": "France"}}'
while [ "$remaining" -gt 0 ]; do
  size=$((remaining < 25 ? remaining : 25))
  body=$(printf '%s' "$filters" | jq --arg cursor "$cursor" --argjson size "$size" \
    '. + {perPage: $size} + (if $cursor == "" then {} else {cursor: $cursor} end)')
  result=$(curl --fail-with-body -sS -X POST https://api.reversecontact.com/v2/search/people \
    -H "Authorization: Bearer $RC_API_KEY" \
    -H "Content-Type: application/json" -d "$body") || break
  printf '%s' "$result" | jq -c '.data.data[]'
  delivered=$(printf '%s' "$result" | jq -er '.data.data | length')
  remaining=$((remaining - delivered))
  next_cursor=$(printf '%s' "$result" | jq -r '.data.metadata.nextCursor // empty')
  [ -z "$next_cursor" ] && break
  [ "$next_cursor" = "$cursor" ] && { echo "Repeated cursor" >&2; break; }
  cursor=$next_cursor
done
```
```typescript [TypeScript SDK]
import ReverseContact from "@reverse-contact/sdk";

const client = new ReverseContact({ apiKey: process.env.RC_API_KEY });

for await (const person of client.search.iterPeople(
  { currentPositionTitle: "CTO", location: { country: "France" }, perPage: 25 },
  { maxResults: 100 }
)) {
  console.log(person.firstName, person.lastName);
}
```
```python [Python SDK]
import os

from reversecontact import ReverseContact

with ReverseContact(api_key=os.environ["RC_API_KEY"]) as client:
    for person in client.search.iter_people(
        current_position_title="CTO",
        location={"country": "France"},
        per_page=25,
        max_results=100,
    ):
        if person is not None:
            print(person.first_name, person.last_name)
```
```bash [CLI]
remaining=100
cursor=""
while [ "$remaining" -gt 0 ]; do
  size=$((remaining < 25 ? remaining : 25))
  if [ -n "$cursor" ]; then
    result=$(rc search people --title CTO --country France --limit "$size" --cursor "$cursor" --json) || break
  else
    result=$(rc search people --title CTO --country France --limit "$size" --json) || break
  fi
  printf '%s' "$result" | jq -c '.data.data[]'
  delivered=$(printf '%s' "$result" | jq -er '.data.data | length')
  remaining=$((remaining - delivered))
  next_cursor=$(printf '%s' "$result" | jq -r '.data.metadata.nextCursor // empty')
  [ -z "$next_cursor" ] && break
  [ "$next_cursor" = "$cursor" ] && { echo "Repeated cursor" >&2; break; }
  cursor=$next_cursor
done
```

### Describe the audience in a prompt

You can use Agentic Search when your input is a sentence. Inspect `data.interpretation.filters` and `warnings` before buying more pages. This example requests one page of up to 25 profiles, which costs at most 26 credits. Do not add explicit search filters alongside `prompt`.

**Code Examples**

```bash [cURL]
curl --fail-with-body -sS -X POST https://api.reversecontact.com/v2/search/people/prompt \
  -H "Authorization: Bearer $RC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "CTOs at software companies in France", "perPage": 25}'
```
```typescript [TypeScript SDK]
import ReverseContact from "@reverse-contact/sdk";

const client = new ReverseContact({ apiKey: process.env.RC_API_KEY });

const page = await client.search.peopleByPrompt({
  prompt: "CTOs at software companies in France", perPage: 25
});
console.log("Filters:", page.interpretation?.filters);
console.log("Warnings:", page.interpretation?.warnings);
console.log(page.data, "credits:", page.quotas?.creditsConsumed);
```
```python [Python SDK]
import os

from reversecontact import ReverseContact

with ReverseContact(api_key=os.environ["RC_API_KEY"]) as client:
    page = client.search.people_by_prompt(
        prompt="CTOs at software companies in France", per_page=25
    )
    if page.interpretation is not None:
        print("Filters:", page.interpretation.filters)
        print("Warnings:", page.interpretation.warnings)
    print(page.data, "next cursor:", page.next_cursor)
```

### Find people similar to a seed profile

You can use People lookalike with exactly one indexed seed `url` or person `id` and no extra search filters. Results exclude the seed. This example retrieves at most 50 lookalikes for at most 50 credits.

**Code Examples**

```bash [cURL]
remaining=50
cursor=""
filters='{"url": "https://www.social.com/in/janedoe"}'
while [ "$remaining" -gt 0 ]; do
  size=$((remaining < 25 ? remaining : 25))
  body=$(printf '%s' "$filters" | jq --arg cursor "$cursor" --argjson size "$size" \
    '. + {perPage: $size} + (if $cursor == "" then {} else {cursor: $cursor} end)')
  result=$(curl --fail-with-body -sS -X POST https://api.reversecontact.com/v2/search/people/lookalike \
    -H "Authorization: Bearer $RC_API_KEY" \
    -H "Content-Type: application/json" -d "$body") || break
  printf '%s' "$result" | jq -c '.data.data[]'
  delivered=$(printf '%s' "$result" | jq -er '.data.data | length')
  remaining=$((remaining - delivered))
  next_cursor=$(printf '%s' "$result" | jq -r '.data.metadata.nextCursor // empty')
  [ -z "$next_cursor" ] && break
  [ "$next_cursor" = "$cursor" ] && { echo "Repeated cursor" >&2; break; }
  cursor=$next_cursor
done
```
```typescript [TypeScript SDK]
import ReverseContact from "@reverse-contact/sdk";

const client = new ReverseContact({ apiKey: process.env.RC_API_KEY });

for await (const person of client.search.iterPeopleLookalike(
  { url: "https://www.social.com/in/janedoe", perPage: 25 },
  { maxResults: 50 }
)) {
  console.log(person.firstName, person.lastName);
}
```
```python [Python SDK]
import os

from reversecontact import ReverseContact

with ReverseContact(api_key=os.environ["RC_API_KEY"]) as client:
    for person in client.search.iter_people_lookalike(
        url="https://www.social.com/in/janedoe", per_page=25, max_results=50
    ):
        if person is not None:
            print(person.first_name, person.last_name)
```

## What it costs

People search and People lookalike cost **1 credit per profile delivered**. An empty page costs nothing, and counting a filtered search is free. Every result is a full profile, so no second fetch is needed to get the profile fields.

Agentic Search costs **1 credit per page for the search, plus 1 credit per profile delivered**. The page fee still applies when no profile matches. SDK iterators use `maxResults` / `max_results` to cap returned profiles, but this limit does not cover the extra page fees. Budget prompt pages separately or set an [API key credit limit](https://docs.reversecontact.com/guides/api-key-management).

> [!NOTE]
> Count a query for free, then retrieve 100 profiles over four full pages of 25: filtered search costs **100 credits**. The same four full pages through Agentic Search cost 100 + 4 = **104 credits**. Each additional empty prompt page costs **1 credit**.

## Good to know

Filters combine with AND. Supported arrays such as `currentPositionTitle` match any listed value. Title matching is token-based, so `CTO` can also match `Deputy CTO`.

Use `currentCompanyIndustry` values from [Industry values](https://docs.reversecontact.com/reference/industries), and full `location.area` ids such as `MC:DACH` from [Area values](https://docs.reversecontact.com/reference/areas). The `currentFunction` filter accepts values such as `Marketing`, and `currentSeniority` accepts `Director` and `VP`. Check the [People search filter reference](https://docs.reversecontact.com/endpoints/search-people#request-body) for all accepted values and exclusions.

For headcount, `currentCompanyEmployeeCountRange` matches whole size brackets: `{ "min": 51, "max": 200 }` matches the 51 to 200 bracket. Unknown fields and invalid closed-list values return `422` and cost 0 credits. Search count calls still count toward your [rate limits](https://docs.reversecontact.com/guides/rate-limits).

Request 1 to 100 profiles per page, 25 by default. Keep filters unchanged and pass the opaque cursor back as `cursor`. Stop when `data.metadata.nextCursor` is `null` or your budget is spent. A short or empty page can still have a next cursor.

For filtered People search, read successive pages within 10 minutes for a consistent cached result set. The [pagination reference](https://docs.reversecontact.com/endpoints/search-people#pagination-and-credits) describes what happens after that.

For prompt searches, reuse the same prompt on the next page. You can send `data.interpretation.filters` to People search count for a free total after the first paid prompt page.

A lookalike seed must already be indexed. An unindexed URL returns a free `404`. Search uses cached data, so use [profile freshness checks](https://docs.reversecontact.com/use-cases/keep-profiles-fresh) when you need a refresh.

The examples print profiles for your application to store. To save people in a persistent Reverse Contact List and buy contact data, use [Lists: orchestrate enrichment](https://docs.reversecontact.com/guides/lists). For individual profiles, see [Get a work email or a phone number](https://docs.reversecontact.com/use-cases/get-a-work-email-or-phone).

## FAQ

### How do I count people matching search filters without spending credits?

Send the People search filters to `POST /v2/search/people/count`, without `cursor` or `perPage`. People search count returns `data.count` and costs 0 credits, but the call counts toward request rate limits.

### How do I cap the credits spent on a people search?

For filtered People search, request no more profiles than your remaining credit budget, or use an SDK iterator with `maxResults` / `max_results`. Each delivered profile costs 1 credit. Agentic Search adds 1 credit per page, so budget those pages separately or set an API key credit limit.

### Does an empty people search page mean there are no more results?

No. A search page can be empty while `data.metadata.nextCursor` is still set. Continue with that cursor until the cursor is `null` or your budget is spent.

### Can I search for people using a sentence instead of filters?

Yes. Agentic Search accepts a `prompt` and returns the applied filters and warnings in `data.interpretation`. Each page costs 1 credit plus 1 credit per profile delivered. You cannot add explicit search filters alongside the prompt.

### How do I find people similar to a Social profile?

Send exactly one seed `url` or person `id` to People lookalike. The seed must already be indexed and is excluded from the results. Each delivered lookalike costs 1 credit. People lookalike does not accept additional search filters.
