# People Search API - Reverse Phone, Email and Address (`nabeelbaghoor/people-search-lookup-api`) Actor

Reverse lookup a phone number, email address, name, street address or username into the people behind it: names, ages, current and past addresses, phone numbers, emails, relatives, social profiles, work history, schools and property records. Pay per person found.

- **URL**: https://apify.com/nabeelbaghoor/people-search-lookup-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Business, Developer tools, Lead generation
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$20.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## People Search API - Reverse Phone, Email and Address

Start from a phone number, an email address, a name, a street address or a username, and get back the people behind it, one row per person.

### What this actor does

- **Runs six searches over one key.** Reverse phone, reverse email, name search, address search, username search, and a full record lookup by person identifier. All six write to the same dataset shape, so a mixed set of runs still reads as one table.
- **Writes one row per person, not one row per search.** These searches are one to many: a phone number may have had four owners, a name search can match sixty people. Burying that in nested JSON would make the output unusable as a table, so a row here is one entity found, and the search that produced it is carried on every row.
- **Separates the current holder from the previous ones.** A reverse search returns everyone the provider has ever associated with an identifier. The person who had the number in 2011 is not the person who has it now, so that is its own column with its own filter rather than being silently resolved one way or the other.
- **Returns the full person record.** Names and aliases, age, month and year of birth, deceased status, current and historical addresses, phone numbers, email addresses, relatives, social profiles, usernames, work history and schools.
- **Chains a search into a full record.** Every person comes back with an identifier, and the person identifier search takes that identifier and returns the complete record, including certifications, languages and historical records. Search wide first, then pull the full record for the handful you actually care about.
- **Reads addresses as properties.** An address search returns the property itself: type, bedrooms, bathrooms, year built, market valuation with a confidence score, county, coordinates, nearby schools, and the residents and owners with their own person identifiers so you can look each one up.
- **Follows result pages properly.** The two searches that page do so by opaque token, not by page number, and the token sits at a different depth depending on the route. Both locations are read, and a token the provider hands back twice stops the loop rather than spinning on it.
- **Refuses searches that cannot work, before they cost anything.** A number that is not ten digits, an address with no city, a name search with no name: all refused locally and recorded as refusals rather than paid for.
- **Says what a forbidden response actually means.** This provider checks the key at its gateway before it looks at the request, so a wrong key answers with a bare forbidden that says nothing about keys. The credential error here says so, because otherwise it reads as a data permissions problem rather than a typo.
- **Never charges for a miss.** Only rows carrying a found person, profile or property are billed.

### Input

| Field | What it does |
| --- | --- |
| `mode` | Which search to run. |
| `queries` | What to look up, one per line or as JSON objects. |
| `ageMin` | Narrow a name search, and drop younger people. |
| `ageMax` | Narrow a name search, and drop older people. |
| `distance` | Search radius in miles around a city or address. |
| `includePossibleMatches` | Also return partial name matches. |
| `followPages` | Keep asking for the next page of a paged search. |
| `currentOwnersOnly` | Keep only who has the number or address now. |
| `minConfidenceScore` | Keep only matches at this confidence or better. |
| `excludeDeceased` | Drop records marked deceased. |
| `excludeCompanies` | Drop business rather than person records. |
| `requirePhone` | Keep only people with a phone number. |
| `requireEmail` | Keep only people with an email address. |
| `requireAddress` | Keep only people with a street address. |
| `states` | Keep only these US states. |
| `networks` | Keep only these social networks. |
| `requestsPerMinute` | Pace the run under your plan's rate limit. |
| `skipNotFound` | Leave misses and refusals out of the dataset. |
| `maxResults` | Hard cap on rows, and therefore on spend and run time. |
| `apiKey` | Your own API key. Stored as a secret. |

### Example output

```json
{
  "found": true,
  "mode": "phone",
  "query": "2125550100",
  "personId": "p1234567890",
  "fullName": "Jane A Doe",
  "firstName": "Jane",
  "middleName": "A",
  "lastName": "Doe",
  "allNames": ["Jane A Doe", "Jane Smith"],
  "age": 41,
  "dobMonth": 6,
  "dobYear": 1985,
  "isDeceased": false,
  "isCompany": false,
  "isCurrentOwner": true,
  "confidenceScore": 0.94,
  "primaryPhone": "2125550100",
  "phones": ["2125550100", "9175550188"],
  "phoneCount": 2,
  "primaryEmail": "jane.doe@example.com",
  "emails": ["jane.doe@example.com"],
  "street": "123 Main St",
  "city": "New York",
  "state": "NY",
  "zip": "10001",
  "addressCount": 3,
  "addresses": ["123 Main St, New York, NY, 10001", "44 Oak Ave, Newark, NJ, 07102"],
  "relatives": ["John Doe", "Mary Smith"],
  "socialProfiles": ["https://www.linkedin.com/in/example"],
  "workHistory": ["Director at Example Corp"],
  "schools": ["State University"],
  "totalMatches": 4,
  "error": null,
  "raw": { }
}
```

### Frequently asked questions

#### What does a row represent?

One person, one social profile, or one property, depending on the search. Not one search. A reverse phone search on a number with four historical owners produces four rows, and a name search matching sixty people produces sixty. This matters when you set the result cap, because a list of twenty names can easily produce several hundred rows.

#### What is the difference between a current owner and the other results?

A reverse phone or address search returns everyone the provider has ever associated with that identifier, not just whoever holds it today. Numbers get recycled and people move, so the person who had the number in 2011 shows up alongside the person who has it now. The `isCurrentOwner` column tells them apart, and `currentOwnersOnly` filters to just the present holder. Which you want depends entirely on the job: skip tracing usually wants the history, outreach usually does not.

#### How do I go from a search result to a full record?

Every person comes back with a `personId`. Feed those identifiers into a second run in person identifier mode, and each one returns the complete record, which is deeper than what a search result carries: certifications, languages, historical records and the full address and employment history. The efficient pattern is to search wide, filter down to the people you actually care about, then pull full records for only those.

#### Which search should I use to find someone by name?

Name search, and give it more than the name. A bare name in a large country returns a lot of people who are not the one you want. Add a city and state, or an age range, or a relative's name; those three are what actually narrow it. If a precise search returns nothing, turn on possible matches and sift, rather than assuming the person is absent.

#### Why was my phone number refused before it was sent?

This provider's reverse phone search takes a ten digit US number. A number written with the North American country code in front is the same number, so the code is stripped and the search runs. Anything that is not ten digits after that is refused locally, because the provider charges per request whether or not it can answer, and a nine digit or fifteen digit string cannot be a US number. Refusals are written to the dataset with the reason, so you can see exactly which inputs were skipped.

#### What does an address search return?

The property, not just the coordinates. Type, bedrooms, bathrooms, year built, a market valuation with its own confidence score, county, latitude and longitude, and nearby schools, plus the residents and owners with their person identifiers so each one can be looked up. An address with several units returns one row per property.

#### Why do I keep getting a forbidden response?

Almost always the key. This provider checks the credential at its gateway before it routes the request, so a missing, mistyped, or not-yet-enabled key produces a bare forbidden with no explanation, and so does a request to a path that does not exist. Since the two are indistinguishable from outside, the credential error from this actor says so plainly rather than guessing. Check the key first, and check that the people search product is enabled on your account.

#### Does this search outside the United States?

The searches here are built around US data: the reverse phone search takes ten digit US numbers, and the address search takes a US state. Email and username searches are less geographically bound, since profiles are global. If your list is international, the email and username searches are the two worth running.

#### Do I need my own API key?

Yes. This actor does not include data access. You use your own API key from Spokeo, which is the provider whose people search API this actor calls, taken from your provider account and pasted into the `apiKey` field, where it is stored as an Apify secret. Your own plan, quota and terms apply.

#### Is there anything I should know before using people data?

Yes. Personal data of this kind is regulated in most places, and the provider's own terms restrict what it may be used for. In particular, consumer report uses such as employment, tenancy, insurance and credit decisions are typically prohibited unless you hold a specific permissible purpose. This actor is a transport for your own licensed access, and the responsibility for lawful use sits with you and your agreement with the provider.

#### What happens when a search finds nobody?

That is a result worth keeping: a number nobody is associated with is an answer. Searches that matched nobody are written with `found: false` and are never billed, as are entries refused before they were sent. Set `skipNotFound` to true to leave them out entirely. A search whose matches were all removed by your filters is not counted as a miss, because it did find people.

#### How much does a run cost?

Pricing is pay per result: you are charged for each person, profile or property written to the dataset, and never for searches that matched nobody, for locally refused entries, for rows dropped by the filters, or for duplicates. The same person returned by two different searches in one run is charged once. Apify platform usage is included in the per-result price. Your own provider quota is separate and billed by them.

### Keyword map

people search API, reverse phone lookup API, reverse email lookup, caller ID lookup, phone number to name, email to person, email to social profiles, username search API, social profile lookup, name search API, find a person by name, address lookup API, reverse address search, property records API, resident lookup, skip tracing API, contact enrichment API, identity data API, relatives lookup, background data API, people data enrichment, person identifier lookup, work history lookup, genealogy data API, debt collection skip trace, tenant screening data

# Actor input Schema

## `mode` (type: `string`):

Which search to run. Each takes a different kind of input, so a run searches one way at a time. Entries that cannot be what this search expects are refused before they are sent, since the provider charges per request either way.

## `queries` (type: `array`):

One value per line. A reverse phone search takes a ten digit US number; a leading country code is dropped rather than refused. Name searches accept a plain name such as Jane Doe, and address searches accept a one line address such as 123 Main St, Springfield, IL. For finer control, give JSON objects instead and use the provider's own field names: first\_name, last\_name, middle\_name, street, unit, city, state, relative\_first\_name, relative\_last\_name. Duplicates are searched once.

## `ageMin` (type: `integer`):

In a name search, narrow the search to people at least this old, and drop younger people from the results of any search. Age is one of the few things that reliably separates two people with the same name in the same city. Leave at zero to skip this.

## `ageMax` (type: `integer`):

In a name search, narrow the search to people no older than this, and drop older people from the results of any search. Leave at zero to skip this.

## `distance` (type: `integer`):

In a name search that includes a city or an address, how far around it to look. Widen this when somebody may have moved within a metropolitan area. Leave at zero for the provider's own default.

## `includePossibleMatches` (type: `boolean`):

In a name search, also return people the provider considers a partial rather than a confident match. Turn this on when a confident search returned nothing and you would rather sift than miss the person; leave it off when precision matters more than recall.

## `followPages` (type: `boolean`):

Keep asking for the next page of a name or address search until the result cap is reached. The other four searches return everything at once and are unaffected. Turn this off to take only the first page of each search, which is the cheaper way to sample a large list.

## `currentOwnersOnly` (type: `boolean`):

In a reverse search, keep only the person who has the number or address now and drop everyone who had it before. Worth understanding rather than just ticking: a reverse search returns everyone the provider has ever associated with an identifier, and the person who had the number in 2011 is not the person who has it today. Off by default, because the history is often the point.

## `minConfidenceScore` (type: `number`):

Keep only people the provider matched to your search with at least this confidence. The right instrument when a name search returns too many near-misses. Leave at zero to keep every match.

## `excludeDeceased` (type: `boolean`):

Leave out records the provider has marked as deceased. Usually right for any outreach or contact list, and usually wrong for genealogy or estate work.

## `excludeCompanies` (type: `boolean`):

Leave out records that are a business rather than a person, which is common on reverse phone searches of numbers that were once commercial lines.

## `requirePhone` (type: `boolean`):

Drop people whose record carries no phone number.

## `requireEmail` (type: `boolean`):

Drop people whose record carries no email address.

## `requireAddress` (type: `boolean`):

Drop people whose record carries no street address.

## `states` (type: `array`):

Only keep people whose current address is in one of these US states, written as two-letter codes. Leave empty to keep every state.

## `networks` (type: `array`):

In a username search, only keep profiles on these networks, written as the provider names them. Matching ignores capitalisation. Leave empty to keep every network.

## `requestsPerMinute` (type: `integer`):

Pace the run so it stays under whatever rate your plan allows. Lower this if the provider starts rate limiting; raise it to finish a long list sooner.

## `skipNotFound` (type: `boolean`):

Leave searches that matched nobody, and entries refused before they were sent, out of the dataset. Off by default, because knowing which of your inputs found nothing is usually the point. Misses are never charged for either way.

## `maxResults` (type: `integer`):

Stop after this many rows. Note that a row is one person found rather than one search performed, so a short list of names can produce a lot of rows. This is the cap on both spend and run time.

## `apiKey` (type: `string`):

Your own API key for the people search API, taken from your provider account. This provider checks the key at its gateway before it looks at the request, so a plain forbidden response almost always means the key is missing, mistyped, or not yet enabled for this product on your account. Stored as a secret.

## Actor input object example

```json
{
  "mode": "phone",
  "queries": [
    "2125550100"
  ],
  "ageMin": 0,
  "ageMax": 0,
  "distance": 0,
  "includePossibleMatches": false,
  "followPages": true,
  "currentOwnersOnly": false,
  "minConfidenceScore": 0,
  "excludeDeceased": false,
  "excludeCompanies": false,
  "requirePhone": false,
  "requireEmail": false,
  "requireAddress": false,
  "requestsPerMinute": 120,
  "skipNotFound": false,
  "maxResults": 100
}
```

# Actor output Schema

## `results` (type: `string`):

One row per person, social profile or property found, carrying names, age, current and past addresses, phone numbers, email addresses, relatives, social profiles, work history and schools, with the search that produced it.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "queries": [
        "2125550100"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/people-search-lookup-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "queries": ["2125550100"] }

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/people-search-lookup-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "queries": [
    "2125550100"
  ]
}' |
apify call nabeelbaghoor/people-search-lookup-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/people-search-lookup-api"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/c1EpfPn62eIWAFRVp/builds/yKvq5xWsoKtjHdesZ/openapi.json
