# Reverse Email Lookup — Email to Name, LinkedIn & Company (`apricot_blackberry/reverse-contact-osint`) Actor

Find the person and company behind any email using free public OSINT sources. Name, LinkedIn URL, job title, company domain, location, confidence score and per-field provenance. BYOK waterfall + MCP. Pay only for matches; misses free.

- **URL**: https://apify.com/apricot\_blackberry/reverse-contact-osint.md
- **Developed by:** [Creator Fusion](https://apify.com/apricot_blackberry) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 enriched contacts

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
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

## Reverse Email Lookup — Email to Name, LinkedIn, Company & OSINT Intel

### What does Reverse Email Lookup do?

Give it a list of email addresses and it returns, for each one, the person behind it (name, headline, LinkedIn URL, location, photo, social profiles) and the company behind the domain (name, description, logo, socials, tech stack, domain age, GitHub org, news, SEC status), plus email-intelligence signals (mail provider, SPF/DMARC, house email format, optional breach count). It uses **free, public OSINT sources only** — no login, no cookies, no paid data provider, no API key required. You pay only for rows where a person was found; misses are free.

It is a Reverse Contact / Clay / Apollo alternative for people who want transparent, source-attributed enrichment at a fraction of the price.

### What you get per email

| Field | Example |
|---|---|
| `fullName`, `firstName`, `lastName` | David Heinemeier Hansson |
| `headline` / `jobTitle` | CTO at 37signals |
| `company`, `companyDomain`, `companyWebsite` | Linux Foundation, linux-foundation.org |
| `linkedinUrl` | https://www.linkedin.com/in/… |
| `location`, `photoUrl`, `twitterUrl`, `githubUrl`, `personalWebsite` | Portland, OR |
| `emailProvider`, `isFreeProvider`, `isDisposable`, `isRoleAddress` | Google Workspace / false / false / false |
| `confidenceScore` (0–100), `confidenceLevel`, `matchType`, `confidenceReasons[]` | 75 · high · exact · `gravatar-profile, github-corroborated` |
| `notFoundReason` on misses | `role-mailbox`, `disposable-email`, `domain-does-not-resolve`, `no-public-footprint` |
| `checkedAt`, `sourcesHit[]` | freshness timestamp + which sources answered |

Every row is flat and CSV-ready. Nested objects carry the detail:

- **`person`** — Gravatar profile, GitHub profile, Keybase proofs, verified social accounts, LinkedIn candidates with scores, pages on the web that cite the email.
- **`companyProfile`** — name, legal name, description, logo, socials (LinkedIn/X/Facebook/Instagram/YouTube/GitHub/Crunchbase), phones, public emails, address (JSON-LD), tech stack, domain registration date & age, registrar, nameservers, certificate-transparency subdomain count and interesting hosts (vpn., sso., dev., jira., …).
- **`emailIntel`** — MX/SPF/DMARC, mail provider, optional aggregate breach signal (count, names, latest date — never raw records) and EmailRep reputation.
- **`sourceLog`** — per-source hit / latency / HTTP status, so you can see exactly where each fact came from.

### Sources (all free)

| Source | What it contributes | Trust |
|---|---|---|
| Gravatar | display name, photo, location, bio, verified social links | High — profile is bound to the exact email hash and Gravatar verifies ownership |
| Keybase | cryptographically-proven Twitter/GitHub/Reddit/domain identities | High |
| GitHub | profile by public email (+ commit-author search with token) | Medium — the public email on GitHub is self-declared; weighted up when another source agrees on the name |
| Web search (Bing → DuckDuckGo fallback) | pages citing the email, name harvesting, LinkedIn discovery by name + company | Medium; scored per candidate |
| Company website | name, description, logo, socials, phones, JSON-LD org data, tech fingerprint | High for company facts |
| DNS | MX / SPF / DMARC, mail provider fingerprint, SaaS footprint from TXT verification records | High |
| RDAP | domain registration date, registrar, nameservers | High |
| crt.sh | certificate-transparency subdomain footprint | High (service is slow / flaky; retried) |
| Company-site email crawl (/about, /team, /contact, /leadership…) | is this exact email published by the company itself? house email format (first.last, f.last…) and whether the input fits it | Very high when cited; format check is a soft signal |
| Wayback Machine CDX | first archived date, site changes in last 90 days | High |
| Hacker News (Algolia) | posts citing the email or name; HN user whose profile cites the email/domain | Medium–high |
| GitHub organisation | org matched to the domain: repos, languages, stars, last push | High |
| Google News RSS | company mentions, last-30-day count, latest headlines | Medium |
| SEC EDGAR | public-company detection: CIK, SIC, state, recent filings | High |
| Have I Been Pwned *(optional key)* | **aggregate** breach count, breach names, latest date | High |
| EmailRep.io *(optional key)* | reputation, first-seen, profiles seen | Medium |

Domain-scoped lookups (DNS, RDAP, crt.sh, website) are cached per company inside a run, so 500 people at the same company cost one set of company lookups.

### How to use it

1. Paste your emails into the **Emails** field (one per line) or pass `emails[]` via API.
2. Optionally untick sources you don't need and set **Max emails** as a cost cap.
3. Click **Start**. Results land in the dataset as flat rows — export as CSV, JSON or Excel, or pipe to Zapier/Make/Sheets.

### Input

```json
{
  "emails": ["jane.doe@acme.com", "dhh@hey.com"],
  "sources": ["dns", "rdap", "gravatar", "github", "keybase", "crtsh", "website", "search"],
  "maxEmails": 1000,
  "maxConcurrency": 3,
  "githubToken": "ghp_…   (optional, raises GitHub limits + enables commit-author search)",
  "hibpApiKey": "…        (optional, enables aggregate breach signal)",
  "proxyConfiguration": { "useApifyProxy": true }
}
```

Use **Apify residential proxy** for best results: search engines honour `site:` and exact-phrase operators far more reliably from residential IPs than from datacenter ranges.

### Sample output

```json
{
  "email": "dhh@hey.com",
  "success": true,
  "matchType": "probable",
  "confidenceScore": 55,
  "confidenceLevel": "medium",
  "notFoundReason": null,
  "fullName": "David Heinemeier Hansson",
  "firstName": "David",
  "lastName": "Hansson",
  "headline": "Creator of Ruby on Rails, CTO 37signals",
  "company": null,
  "companyDomain": null,
  "location": null,
  "linkedinUrl": "https://www.linkedin.com/in/david-heinemeier-hansson-374b18221",
  "emailProvider": "Free webmail",
  "isFreeProvider": true,
  "isDisposable": false,
  "isRoleAddress": false,
  "checkedAt": "2026-09-24T09:52:16.071Z",
  "sourcesHit": ["search", "hackernews"],
  "confidenceReasons": ["email-indexed-on-web", "name-from-web-listing", "linkedin-name-match", "hackernews-footprint"],
  "person": { "webMentions": [{ "url": "https://world.hey.com/dhh", "title": "David Heinemeier Hansson" }], "linkedinCandidates": [ "…" ], "hackernews": { "emailMentions": [ "…" ] } },
  "companyProfile": null,
  "emailIntel": { "domainResolves": null, "acceptsMail": null, "breachCount": null },
  "sourceLog": { "gravatar": { "hit": false, "ms": 378, "status": 404 }, "search": { "hit": true, "ms": 613 } }
}
```

A corporate address additionally fills `companyProfile` (name, description, logo, socials, phones, tech stack, domain age, GitHub org, news, SEC status) and `emailIntel` (MX/SPF/DMARC, provider).

### How much does it cost?

Pay-per-event. You are charged **only for rows where a person was found**.

| Event | Price | Notes |
|---|---|---|
| Enriched contact (`record-found`) | **$0.004** — $4 per 1,000 matches | Charged when `success: true` |
| Actor start | $0.00005 | Apify's standard start event |
| Misses, role mailboxes, disposable and invalid emails | **Free** | Still returned as rows with `notFoundReason` so you can filter |

Example: 1,000 cold emails with a 35 % match rate ≈ 350 × $0.004 = **$1.40**. Set *Maximum cost per run* in the run options to cap spend; the Actor stops cleanly when the limit is hit.

Compared with the store leaders at $10–150 per 1,000 and Reverse Contact at ~$60 per 1,000 credits, this is 60–97 % cheaper — and you can read exactly which public source every fact came from.

### Why use this instead of a paid enrichment API?

- **No vendor lock-in or hidden data.** Every field carries provenance in `sourceLog`; nothing comes from resold LinkedIn dumps.
- **Cheapest first pass.** Run your whole list here, then send only the misses to a $10–150/1k provider.
- **Signals the resellers don't give you:** confidence score with reasons, house email-format check, breach signal, tech stack, domain age, public-company flag.
- **Agent-ready.** Limited permissions, flat JSON rows, works from the Apify MCP server.

### Provenance on every field

Each row carries `fieldSources` — a map from field to the source that supplied it (`"linkedinUrl": "search:email-mention"`, `"location": "gravatar"`, `"company": "byok:prospeo"`) — plus `publicSourcesOnly: true|false`. That is your audit trail for GDPR Art. 6(1)(f) assessments and DPIAs: you can show, per contact, that the data came from a public source, when it was checked, and that no purchased broker data was involved. No other enrichment vendor exposes this.

### Bring your own keys — waterfall the misses

Free public sources run first and are the default. If you add a vendor key, addresses the public sources can't resolve are sent to **your** vendor account (Prospeo, Reverse Contact or People Data Labs), the result is merged, and every vendor-supplied field is stamped `byok:<vendor>` in `fieldSources`. You still pay us only our $0.004 per match; the vendor bills you directly at their rate (Prospeo 1 credit, Reverse Contact 2 credits, PDL 1 credit — all three are free on a miss).

Why this beats running the vendor first: on a typical cold B2B list 30–60 % of addresses have a public footprint, so the waterfall cuts your vendor credit spend by that much, and the rows that never touch a vendor stay fully public-sourced.

Options: `byokMode` = `on-miss` (default) / `always` / `off`; `byokOrder` to change vendor precedence; `byokMobile` to ask Prospeo for the mobile (10 credits, off by default).

### Use it from an AI agent (MCP) or as a live API

The Actor runs in **Standby mode**, so it also answers synchronously:

- **MCP:** point any MCP client (Claude Desktop, ChatGPT, Cursor, Apify MCP server) at the Standby URL shown on the Actor's *Endpoints* tab, path `/mcp`, with your Apify token as Bearer auth. Tools: `enrich_email(email, sources?)` and `enrich_emails(emails[])`. Each call returns a one-line summary plus the full row as structured content.
- **JSON:** `GET <standby-url>/enrich?email=jane@acme.com` or `POST <standby-url>/enrich` with `{"emails":[…]}` (max 25 per call). Same row shape as the dataset.

Standby calls are charged the same way: only matched records.

### Integrations

Use the dataset with any Apify integration — Zapier, Make, n8n, Google Sheets, webhooks — or call it from code:

```js
const { ApifyClient } = require('apify-client');
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('apricot_blackberry/reverse-contact-osint').call({ emails: ['jane.doe@acme.com'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

### FAQ

**Is this legal / GDPR-compliant?** It queries only public, unauthenticated endpoints and returns aggregate breach metadata, never raw records. You are the data controller for how you use the output; the usual legitimate-interest basis (GDPR Art. 6(1)(f)) and CCPA rules apply to B2B outreach.

**Why did an email return a miss?** Check `notFoundReason`: `role-mailbox` (info@, sales@…), `disposable-email`, `domain-does-not-resolve`, or `no-public-footprint` (the person has no public trace linked to that exact address). Misses are free.

**Why is a LinkedIn URL sometimes missing when the name is found?** LinkedIn discovery relies on web search. Enable Apify residential proxy in the input — search engines honour exact-phrase and `site:` operators far more reliably from residential IPs.

**Can I bring my own keys?** Yes: `githubToken` raises GitHub limits and enables commit-author search; `hibpApiKey` turns on the breach signal; `emailrepApiKey` improves EmailRep limits. All are optional and stored as secrets.

**Does it verify that the email is deliverable?** It checks the domain accepts mail (MX) and whether the address fits the company's published email format. It does not do SMTP verification.

### Confidence model

Identity-anchored hits (Gravatar hash, Keybase proof, LinkedIn page that cites the email) score highest. Name inference from `first.last@` alone never produces a match on its own — it needs corroboration. Disposable domains, role mailboxes and dead domains are penalised and returned as explicit misses with a `notFoundReason`, so you can filter before outreach.

### Extending

`src/enrich.js` holds a `SOURCES` registry. Add your own module as `{ fn, phase, scope }` — `phase 1` runs in parallel, `phase 2` receives the merged name/company guesses. Each source returns `{ hit, data }` and gets a row in `sourceLog` automatically.

### Data handling

Only public, non-authenticated endpoints are queried. Breach data is aggregate-only by design (count / names / dates); raw records, passwords and hashes are never fetched or stored. Respect applicable law (GDPR Art. 6(1)(f), CCPA) and each site's terms when using the output for outreach.

***

Built by Creator Fusion LLC. Related actors: LinkedIn → email, company enrichment from domain, Apify Store lead-gen suite.

# Changelog

This Actor's version history is a separate document: https://apify.com/apricot\_blackberry/reverse-contact-osint/changelog.md

# Actor input Schema

## `emails` (type: `array`):

Email addresses to enrich. One result per email.

## `sources` (type: `array`):

Which free sources to query. Disable any you don't need to speed up runs.

## `maxEmails` (type: `integer`):

Stop after this many emails. Leave empty to process the whole list.

## `maxConcurrency` (type: `integer`):

How many emails to enrich in parallel. Keep low (2–4) to avoid rate limits on free endpoints.

## `githubToken` (type: `string`):

Raises GitHub search rate limit from 10/min to 30/min and enables commit-author search.

## `hibpApiKey` (type: `string`):

Enables the Have I Been Pwned source. Output is aggregate-only (breach count, names, latest date) — never raw records.

## `emailrepApiKey` (type: `string`):

Optional; unauthenticated calls work but are heavily rate-limited.

## `byokMode` (type: `string`):

Bring-your-own-key waterfall. "On miss" (default) calls your vendor only when the free public sources find nobody — the cheapest way to lift match rate. "Always" also calls the vendor on hits to fill gaps. Vendor credits are billed by the vendor to your account; we still charge only our normal per-match event.

## `prospeoApiKey` (type: `string`):

Prospeo Enrich Person — 1 credit per email match, no charge on NO\_MATCH, free re-enrich within 90 days. Best price/precision of the three.

## `reverseContactApiKey` (type: `string`):

Reverse Contact /v2/enrich/persons — 2 credits on match, 404 free. LinkedIn-derived profile.

## `pdlApiKey` (type: `string`):

PDL Person Enrichment — 1 credit on match (likelihood ≥ 6), 404 free. Widest coverage, includes phone when present.

## `byokOrder` (type: `array`):

Order to try vendors (first hit wins). Default: reversecontact → prospeo → pdl, skipping any without a key.

## `byokMobile` (type: `boolean`):

Prospeo only: request the mobile (10 credits when found). Off by default because it is expensive.

## `proxyConfiguration` (type: `object`):

Apify Proxy is recommended for the website-scrape and web-search sources.

## `requestTimeoutSecs` (type: `integer`):

Seconds to wait for each source before giving up on it. Slow sources (crt.sh) get extra time automatically.

## Actor input object example

```json
{
  "emails": [
    "jane.doe@example.com"
  ],
  "sources": [
    "dns",
    "rdap",
    "gravatar",
    "github",
    "keybase",
    "crtsh",
    "website",
    "search",
    "sitePattern",
    "wayback",
    "hackernews",
    "githubOrg",
    "news",
    "secEdgar"
  ],
  "maxConcurrency": 3,
  "byokMode": "on-miss",
  "byokOrder": [
    "prospeo",
    "reversecontact",
    "pdl"
  ],
  "byokMobile": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "requestTimeoutSecs": 15
}
```

# Actor output Schema

## `contacts` (type: `string`):

One row per input email: person, company and email-intel fields plus confidence score and source log.

## `stats` (type: `string`):

Processed / matched / failed counts.

# 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 = {
    "emails": [
        "jane.doe@example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("apricot_blackberry/reverse-contact-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 = { "emails": ["jane.doe@example.com"] }

# Run the Actor and wait for it to finish
run = client.actor("apricot_blackberry/reverse-contact-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 '{
  "emails": [
    "jane.doe@example.com"
  ]
}' |
apify call apricot_blackberry/reverse-contact-osint --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,apricot_blackberry/reverse-contact-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/mInCIkw9n00oIZ7vv/builds/zBLs2fPabneoGMD3p/openapi.json
