# Email Finder & Reverse Phone Lookup — Contact Enrichment (`ntriqpro/contact-enrichment-osint`) Actor

Email finder and reverse phone lookup in one run: give emails, phones, domains or usernames and get public accounts, phone carrier and line type, company domain profile, company emails, breach signals and a person summary with a confidence score. Batch input, pay per result.

- **URL**: https://apify.com/ntriqpro/contact-enrichment-osint.md
- **Developed by:** [daehwan kim](https://apify.com/ntriqpro) (community)
- **Categories:** Lead generation, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $10.40 / 1,000 account founds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Email Finder & Reverse Phone Lookup — Contact Enrichment

**Paste emails, phone numbers, company domains or usernames. Get back everything public about each
one, source by source.** One identifier turns into many rows: the public accounts tied to it, the
phone's carrier and line type, the company domain's registration, DNS and homepage, the emails the
company publishes on its own site, breach history and a one-row person summary with a 0–100
confidence score that shows which signals produced it.

> This Actor is an **independent, unofficial tool**. It is **not affiliated with, endorsed by, or
> sponsored by** Gravatar, GitHub, GitLab, Hacker News, dev.to, Keybase, Docker Hub, npm, Chess.com,
> Lichess, Reddit, XposedOrNot, Have I Been Pwned, rdap.org or any other source it reads. It uses only
> **public, logged-out** endpoints: no login, no captcha solving, no password-reset probing, no
> account pools. **You are responsible** for having a lawful basis to look up the identifiers you
> enter, for complying with each source's terms of service and with privacy law (GDPR, CCPA, PIPA and
> others) in your jurisdiction, and **you are the data controller** for any personal data a run you
> start returns. Use the output for lawful purposes only (sales research, fraud and KYC checks, security
> reviews, verifying your own data). Every result row carries this notice in its `responsibility` field.

### Who this is for

Sales and lead-generation teams enriching a CRM list, recruiters checking a candidate's public
footprint, fraud and trust-and-safety analysts screening sign-ups, and security teams auditing their
own company's exposure.

### What one run returns

| `rowType` | One row per | Key fields |
|---|---|---|
| `identifier-check` | identifier | `identifierType` (email, phone, domain, username, invalid), `verdict`, `mxFound`, `mxHosts`, `disposable`, `roleBased`, `freeProvider`, `e164` |
| `account` | public account found | `site`, `username`, `profileUrl`, `matchedBy` (email-hash, public-email, username), `profile` (only with `includeProfileFields`) |
| `phone-profile` | phone number | `e164`, `valid`, `countryCode`, `carrier`, `lineType` (mobile, fixed-line, voip, toll-free…), `region`, `timeZones`, `international`, `national` |
| `domain-profile` | company domain × source | `source` = `rdap` (`registrar`, `registeredAt`, `expiresAt`, `nameservers`, `registrantOrganization`), `dns` (`dnsRecords`, `mailProvider`, `hasSpf`, `hasDmarc`) or `website` (`pageTitle`, `metaDescription`, `siteName`, `techHints`, `socialLinks`) |
| `company-email` | email published on the company's own pages | `email`, `foundOn`, `roleBased` |
| `breach` | public breach record | `breachScope` (email or domain), `breachName`, `breachDate`, `dataClasses`, `pwnCount` |
| `person-summary` | identifier | `displayName`, `companyName`, `companyDomain`, `location`, `phoneCountry`, `phoneCarrier`, `accountsFound`, `accountSites`, `socialProfiles`, `breachCount`, `confidence`, `confidenceSignals`, `sourcesChecked`, `sourcesUnanswered` |
| `notice` | message | `noticeCode`, `message` — never charged |

Every row also has `identifier`, `identifierType`, `normalized`, `source`, `checkedAt` and
`responsibility`. Company-domain layers run for company emails and domains only; free-mail domains
(gmail.com, outlook.com…) are skipped.

**Sources.** Accounts: Gravatar (email hash), GitHub public-email search, and username checks on
GitHub, GitLab, Hacker News, dev.to, Keybase, Docker Hub, npm, Chess.com, Lichess and Reddit (the
email's local part is used as the username unless it is a role address such as info@). Phone:
libphonenumber data, offline. Domain: rdap.org, DNS, the company's homepage and its contact, about and
team pages. Breaches: XposedOrNot for emails, the Have I Been Pwned public breach catalogue for domains.
Breach rows name the breach and the data classes; they never contain passwords, hashes or dump contents.

### Pricing (pay per event)

| Event | Charged per | USD (FREE plan) | Bronze | Silver | Gold+ |
|---|---|---|---|---|---|
| `run-started` | run | 0.005 | 0.004 | 0.00325 | 0.0026 |
| `identifier-checked` | unique identifier checked | 0.005 | 0.004 | 0.00325 | 0.0026 |
| `account-found` | account row | 0.02 | 0.016 | 0.013 | 0.0104 |
| `phone-profiled` | phone profile row | 0.03 | 0.024 | 0.0195 | 0.0156 |
| `domain-profiled` | domain source row (max 3 per domain) | 0.02 | 0.016 | 0.013 | 0.0104 |
| `company-emails-found` | company email row | 0.02 | 0.016 | 0.013 | 0.0104 |
| `breach-signal` | breach row | 0.03 | 0.024 | 0.0195 | 0.0156 |
| `person-summary` | summary row | 0.05 | 0.04 | 0.0325 | 0.026 |

**What is charged and what is not.**

- A row written to the dataset is charged once under its event. The same account, domain source,
  company email or breach is never charged twice in one run, even when two identifiers lead to it
  (two emails on one company domain pay for the domain once).
- `identifier-checked` is charged for every unique identifier we check, including one the check
  finds malformed (for example `jane@@acme`): the check is the work. Empty lines and repeats are
  dropped before the check and cost nothing.
- Sites where no account exists, and sites that did not answer (rate limit, block), produce no
  `account` row and no charge; they are listed in the summary's `sourcesUnanswered`.
- Notice rows (`noticeCode`: invalid input, time limit, spending limit) are messages, never charged.
- When your maximum charge per run is reached the run stops paid work and ends with a notice row.

**Example:** one company email with every layer on, 3 public accounts, 3 domain rows, 4 company
emails and 1 breach: start $0.005 + check $0.005 + accounts $0.06 + domain $0.06 + company emails
$0.08 + breach $0.03 + summary $0.05 = **about $0.29**. A phone number alone: $0.005 + $0.005 + $0.03 +
$0.05 = **$0.09**. Switch layers off to pay only for what you need.

### Input

```json
{
  "identifiers": ["jane.doe@acme.com", "+14155552671", "acme.com", "janedoe"],
  "layers": ["accounts", "phone", "domain", "companyEmails", "breaches", "summary"],
  "includeProfileFields": false,
  "defaultCountry": "US",
  "timeoutPerIdentifier": 60
}
```

- `identifiers` — array, up to 500 per run. The type of each value is detected automatically.
- `layers` — leave empty for all. The identifier check always runs.
- `includeProfileFields` — off by default: account rows confirm the account exists. Turn it on to
  also copy public display name, company, location and bio, and to use them in the summary. Turn it
  on only when you have a lawful basis to process that personal data.
- `defaultCountry` — country for numbers written without `+`.
- `timeoutPerIdentifier` — seconds before lookups for one identifier stop (10–300).

### Output example

```json
{ "rowType": "identifier-check", "identifier": "jane.doe@acme.com", "identifierType": "email", "verdict": "deliverable", "mxFound": true, "freeProvider": false }
{ "rowType": "account", "identifier": "jane.doe@acme.com", "site": "github", "username": "jane.doe", "profileUrl": "https://github.com/jane.doe", "matchedBy": "username" }
{ "rowType": "domain-profile", "identifier": "jane.doe@acme.com", "domain": "acme.com", "source": "dns", "mailProvider": "google-workspace", "hasDmarc": true }
{ "rowType": "company-email", "identifier": "jane.doe@acme.com", "email": "sales@acme.com", "foundOn": "https://acme.com/contact", "roleBased": true }
{ "rowType": "person-summary", "identifier": "jane.doe@acme.com", "companyName": "Acme", "accountsFound": 3, "confidence": 70, "confidenceSignals": ["identifier-valid", "mail-server-found", "account-found", "company-domain", "company-website-live"] }
```

### Limits worth knowing

- Unauthenticated public APIs have rate limits (GitHub allows about 60 lookups an hour from one IP).
  A source that stops answering is reported in `sourcesUnanswered`, never guessed.
- The phone carrier is the carrier the number range was originally allocated to; a number moved to
  another carrier keeps its original name.
- A username match on a site means an account with that name exists there, not that it belongs to
  the same person. The summary's `confidence` counts the signals; judge the match yourself.

### Runs on the Apify platform

Schedule it, call it from the API, or plug it into a workflow. It needs no browser and no proxy;
1 GB of memory is enough.

# Actor input Schema

## `identifiers` (type: `array`):

One per line, up to 500. Each value is detected automatically: email (jane@acme.com), phone in international format (+14155550123), company domain (acme.com) or username (janedoe). Duplicates are checked once. Only look up people and companies you have a lawful basis to research: you are responsible for how you use the output (GDPR, CCPA, PIPA).

## `layers` (type: `array`):

Which lookups to run for each identifier. Leave empty for all. The identifier check always runs.

## `includeProfileFields` (type: `boolean`):

Off: account rows confirm that the account exists (site, username, profile URL). On: also copy public display name, company, location and bio where the site publishes them, and use them in the person summary. Turn on only if you have a lawful basis to process these personal data.

## `defaultCountry` (type: `string`):

Two-letter country code used for numbers written without a country code (for example US, GB, KR).

## `timeoutPerIdentifier` (type: `integer`):

Lookups for one identifier stop after this many seconds; the identifier check is still delivered.

## Actor input object example

```json
{
  "identifiers": [
    "support@github.com",
    "+14155552671",
    "apify.com",
    "torvalds"
  ],
  "layers": [
    "accounts",
    "phone",
    "domain",
    "companyEmails",
    "breaches",
    "summary"
  ],
  "defaultCountry": "US",
  "timeoutPerIdentifier": 60
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# 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 = {
    "identifiers": [
        "support@github.com",
        "+14155552671",
        "apify.com",
        "torvalds"
    ],
    "layers": [
        "accounts",
        "phone",
        "domain",
        "companyEmails",
        "breaches",
        "summary"
    ],
    "includeProfileFields": false,
    "defaultCountry": "US",
    "timeoutPerIdentifier": 60
};

// Run the Actor and wait for it to finish
const run = await client.actor("ntriqpro/contact-enrichment-osint").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 = {
    "identifiers": [
        "support@github.com",
        "+14155552671",
        "apify.com",
        "torvalds",
    ],
    "layers": [
        "accounts",
        "phone",
        "domain",
        "companyEmails",
        "breaches",
        "summary",
    ],
    "includeProfileFields": False,
    "defaultCountry": "US",
    "timeoutPerIdentifier": 60,
}

# Run the Actor and wait for it to finish
run = client.actor("ntriqpro/contact-enrichment-osint").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 '{
  "identifiers": [
    "support@github.com",
    "+14155552671",
    "apify.com",
    "torvalds"
  ],
  "layers": [
    "accounts",
    "phone",
    "domain",
    "companyEmails",
    "breaches",
    "summary"
  ],
  "includeProfileFields": false,
  "defaultCountry": "US",
  "timeoutPerIdentifier": 60
}' |
apify call ntriqpro/contact-enrichment-osint --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ntriqpro/contact-enrichment-osint"
        }
    }
}
```

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/QXbipebPLLI6FimH3/builds/6BBeyi9TaaFmsFASS/openapi.json
