Email OSINT Checker — Account Finder & Reverse Lookup
Pricing
from $10.40 / 1,000 account founds
Email OSINT Checker — Account Finder & Reverse Lookup
Looks an email up on 210+ sites (Twitter/X, Spotify, Pinterest, Adobe, Dropbox and more) and lists where it is registered. Silent: no password-reset mail is sent. Holehe-based email OSINT, batch up to 500 addresses. Free: 1 address, 3 accounts shown; paid: every account on all sites.
Pricing
from $10.40 / 1,000 account founds
Rating
5.0
(1)
Developer
daehwan kim
Maintained by CommunityActor stats
6
Bookmarked
2.1K
Total users
194
Monthly active users
2 days ago
Last modified
Share
Email OSINT Checker, Account Finder, Reverse Lookup, 210+ Sites
Give it 1 email address: it looks that address up on 210+ sites and returns 1 row per site it is registered on — without any password-reset email reaching the target. 210+ is the number of sites a lookup is sent to; not every site answers every lookup, and you are billed only for lookups that returned a confirmed answer. Find which accounts an email address has signed up for. This email lookup / email OSINT tool discovers which sites an email is registered on — Twitter, Spotify, Pinterest, Imgur, Tumblr, Adobe, and many more — without ever sending a password-reset notification to the target. A fast reverse email search powered by holehe (GPL-3.0, 5.6k+ stars), a widely used open-source email OSINT tool.
Free Apify plans get a sample, paid plans get the full result set. A run on a free plan looks the first address up on every site, shows the first 3 accounts in full (plus 1 extended-site account and one row of each extra layer) and counts the rest without naming them. It ends successfully and, once the address lookup has finished, adds a
free-plan-previewrecord (3 of 10 shown-style counts per layer, the list-price cost of the locked rows, the estimated paid cost of the addresses left unchecked) — never an error — and adds afree-plan-caprecord when something was held back. Any paid plan removes every limit (all modules, up to 500 addresses per run, every account found) and applies your plan's Store discount: Bronze 20%, Silver 35%, Gold and above 48% off. See Pricing.
What a lookup costs. One address is looked up on up to 211 sites (lookups sent; the number that come back with a confirmed answer is lower and changes from run to run). Each completed lookup is billed once:
account-foundwhen the address is registered on the site and, on runs whose price table lists thesite-checkedevent,site-checkedwhen the site confirms the address is not registered there. A lookup the site blocked, throttled or left unanswered is never billed. Before the first lookup the run log and the run's status message state the maximum for your run at your plan's prices;maxSitesPerEmailand the run's maximum cost let you choose how much to spend. See Pricing.
Free plan vs paid plan at a glance
| Apify free plan | Any paid Apify plan | |
|---|---|---|
| Addresses per run | First 1 | Up to 500 |
| Sites looked up per address | All 121 holehe modules (+ a short extended-sites pass) | All 121 + 90 extended sites |
| Account rows | First 3 in full + 1 extended-site account; the rest counted, not named (3 of 10 shown) | Every account found |
Same-username accounts (username-match) | 1 per address, the rest counted | Every match, up to 3 username candidates + deepUsernameSearch |
| Company domain records | One of each type (WHOIS, MX, SPF, DMARC) per run | Every record |
| Company domain assets | One of each type (subdomain, IP, address) per run | Every asset up to maxDomainAssets |
| Gravatar profile | One full profile per run (name, photo, link); later profiles show only that they exist | Every profile in full |
| What stayed locked | Counted in the last free-plan-preview row and the status message (accounts and each layer as N of M shown, list-price cost of the locked rows, addresses left unchecked and their estimated cost — estimates marked as such) | Nothing locked |
| Price per event | List price | Bronze 20% off · Silver 35% off · Gold and above 48% off |
What you get
| Capability | Detail |
|---|---|
| Sites looked up | 210+ per email — 121 holehe sites (Twitter/X, Spotify, Pinterest, Imgur, Tumblr, Adobe, Atlassian and more) plus 90 extended sites when the run has spare time for them. A lookup that a site blocks or leaves unanswered gives no row and no charge |
| Second engine for 11 big sites | Instagram, Pinterest, Spotify, Tumblr, Adobe, Atlassian, Quora, Patreon, CodePen, Firefox and Vivino often give holehe no answer. When that happens for an address, a second engine (user-scanner) asks the same site. Still one lookup and one charge per site |
| Extended sites | 90 more sites (Wix, Dropbox, Duolingo, Etsy, Facebook, Quizlet, Canva, Figma, Walmart, Coursera and more) checked after the 121 holehe sites, same account-found price, on by default |
| Silent lookups | No password-reset emails sent to the target — passive account-existence signals only |
| Flat, table-ready rows | One row per account found, with platform, domain and detection method; on runs priced with site-checked, also one row per site that confirmed the address is not registered there |
| Bulk mode | Up to 500 emails per run on any paid plan (free plans: the first address) |
| Extra identity hints | Masked recovery email and masked partial phone, when the platform exposes them |
| Username layer | Also searches the email's local part as a username on 390+ sites (Sherlock) and returns one username-match row per public account — a same-username lead, not proof of the same person |
| Company domain layers | For company addresses: WHOIS, MX, SPF and DMARC rows (on by default); optionally the domain's subdomains, host IPs and same-domain addresses from public certificate records (off by default, can be hundreds of rows) |
| Gravatar layer | One row when the address has a public Gravatar profile (name, photo and link on paid plans) |
| Ready to export | Download the dataset as JSON, HTML, CSV or Excel |
| No setup | No subscription, no scraping config — just an API call |
Sample result
One dataset item per registered account found — not one per email. An address registered on 17 sites produces 17 rows.
{"email": "test@gmail.com","platform": "codepen","exists": true,"domain": "codepen.io","method": "register","emailRecovery": null,"phoneNumber": null,"rateLimit": false,"charged": true,"sitesChecked": 121}
| platform | domain | exists | method | rateLimit | |
|---|---|---|---|---|---|
| test@gmail.com | codepen | codepen.io | true | register | false |
| test@gmail.com | anydo | any.do | true | login | true |
A live test of test@gmail.com returned 17 confirmed accounts across 121 sites checked.
Use Cases
- Security incident response — given a breached email, map exposure across SaaS
- Fraud investigation — confirm an email is a real online identity, not a throwaway
- Counterparty due diligence — confirm a business contact's email is registered on professional platforms
- KYC enhancement — supplement identity verification with online-account footprint
- OSINT pentesting — reconnaissance phase for authorized engagements
- Data-broker compliance — auditing your own organization's email exposure
Built for security researchers, fraud investigators, OSINT analysts, KYC teams, and trust & safety teams who need to verify online presence of a given email address with zero footprint.
Not a consumer reporting agency. Do not use results to make decisions about employment, credit, housing or insurance.
Input
| Field | Type | Required | Description |
|---|---|---|---|
emails | array of string | ✅ | Up to 500 email addresses to investigate |
email | string | — | Legacy single-email input (use emails for batch) |
timeout | integer | — | Per-site timeout in seconds (default 30) |
maxEmails | integer | — | How many emails this run processes; hard cap 500 |
usernameLayer | boolean | — | Search the email's local part as a username (default true); set false for account-registration rows only |
maxUsernameCandidates | integer | — | Username forms searched per address with Sherlock, 1–3 (default 3: dot-less, original, underscore). Candidates 2 and 3 only run on spare run time |
deepUsernameSearch | boolean | — | Paid plans only (default false): also run Maigret (top 500 sites) on the dot-less form; rows from sites Sherlock did not return carry matchSource: "maigret". Takes several minutes per address |
includeDomainInfo | boolean | — | Company addresses only (default true): WHOIS, MX, SPF and DMARC rows for the address's domain, each domain once per run. Free mailboxes such as Gmail, Outlook, Naver and Daum are skipped |
includeDomainAssets | boolean | — | Default false, because one domain can return hundreds of billed rows: subdomains, host IPs and same-domain addresses from public certificate and host records |
maxDomainAssets | integer | — | Most domain-asset rows delivered and billed per domain when includeDomainAssets is on (default 200, maximum 1000) |
extendedSites | boolean | — | Default true: also checks 90 extended sites after the 120+ (rows carry source: "user-scanner"; a registered address is billed as account-found, a confirmed "not registered" as site-checked when the run is priced with it). Set false to get exactly the 120+ site result. Runs only on spare run time |
maxSitesPerEmail | integer | — | Optional ceiling on how many sites each address is looked up on: the holehe sites first (in module-name order), then the extended sites. Empty means every site. It lowers the most a run can cost in account-found and site-checked events. On free plans it narrows the lookup too; the sample of shown rows is the same |
includeGravatar | boolean | — | Default true: one profile row when the address has a public Gravatar profile; no profile, no row |
{"emails": ["alice@example.com", "bob@example.org"],"timeout": 30,"maxEmails": 10}
Output
| Field | Type | Description |
|---|---|---|
email | string | The address checked |
platform | string | Platform short name, e.g. codepen |
domain | string | Platform domain, e.g. codepen.io |
exists | boolean | true when an account registered with this email was detected on that platform. Always false on username-match rows (same username only); those rows carry usernameExists: true |
method | string | How existence was detected: register, login or other |
rateLimit | boolean | true when holehe flags this site as one that throttles frequent lookups (a static property of the site, not a failure of this lookup) — the account is still confirmed, so the row is charged; the flag simply tells you the site is worth re-verifying |
source | string | "user-scanner" on rows from the 90 extended sites (absent on the 120+ site account rows). These rows also carry profileUrl (the site's address) and siteCategory; method, rateLimit and recovery hints are not available for them. not-registered rows always carry it: "holehe" or "user-scanner" |
billingEvent | string | The event this row is billed under: account-found on account rows, site-checked on not-registered rows. On a no-match summary row it names the event that pays for that answer on this run (email-checked, or site-checked when the per-site rows are billed instead) and is null when nothing is billed for it. On notice and error rows it names the event concerned |
siteCheckedCharged / siteCheckedBillingStatus | boolean / string | On not-registered rows: whether the row was billed as a site-checked event, and the charge status. Only charged rows are written |
conflictSites / sitesConflicting | array of string / integer | On the site-answer-conflict notice: sites where two lookups of the same address gave opposite confirmed answers. Neither answer is delivered as a result or billed |
withheldAfterRestart | object | On the restart-rows-withheld notice: per event, how many rows a restarted run left out because the platform's charge count for the run did not cover them. Nothing was charged for those rows |
charged | boolean | true when this row was billed as an account-found event. On the free plan the row is written first and charged right after, so a run restarted in between leaves that one row uncharged (it still says true) |
emailRecovery | string | Masked recovery-email hint, when the platform exposes one (else null) |
phoneNumber | string | Masked partial phone, when the platform exposes one (else null) |
sitesChecked | integer | Total platforms probed for this email |
sitesUndetermined | integer | On result rows: how many of the 121 holehe lookups got no answer for this email (blocked, throttled or no response) — never billed. Often a large part of the 121. Some of these sites are then answered by the second engine; those answers arrive as their own rows with source: "user-scanner" |
modulesMissing / modulesMissingCount | array of string / integer | On result rows: the sites that returned no row at all for this email (at most 3, and at most 5% of the sites probed; a larger gap makes the email an error row instead). They are counted in sitesUndetermined; the rest of the email's result is complete and billed as usual |
rowType | string | result, analysis, username-match, domain-record, domain-asset, profile, notice, or error |
companyDomain | string | On domain-record and domain-asset rows: the address's domain |
recordType / recordValue / recordDetails | string / string / object | On domain-record rows: whois, mx, spf or dmarc, its main value, and details (registrar, dates and name servers for WHOIS; priority for MX) |
assetType / assetValue / assetHost | string | On domain-asset rows: subdomain, host-ip or email, its value, and the host it belongs to |
displayName / avatarUrl | string | On profile rows: the Gravatar name and photo URL (profileUrl holds the public profile link) |
matchSource | string | On username-match rows added by the deep search: maigret |
matchBasis | string | On username-match rows: username-from-email |
sameUsername | string | On username-match rows: the username that was searched (from the email's local part) |
usernameCandidateRank | number | On username-match rows: 1 for the highest-priority candidate (usually the dot-less form), 2 and 3 for the lower-priority ones (original and underscore forms). Rank 2 and 3 are less certain matches |
profileUrl | string | On username-match rows: the profile URL on that site |
checkCompleted | boolean | true only when the email investigation finished |
matchStatus | string | found, not-registered (one site confirmed the address is not registered there; only on runs priced with site-checked), no-match (per-address summary: no account anywhere), incomplete, not-started, or withheld |
accountDetailsDelivered | boolean | On a completed-check summary, whether separately billed account detail rows were delivered |
emailCheckedBillingStatus | string | Whether the completed-check event was inactive, charged, platform-unbilled, stopped by a charge limit, or not-applicable (the address had accounts, so only account-found applies, or the run is priced with site-checked, which bills the same answer per site instead) |
disclaimer | string | Legal-use notice, repeated on every row |
Username layer (username-match rows)
For each address the Actor takes the local part (jane.doe+news@example.com → janedoe) and looks that username up on the 390+ sites Sherlock covers. Each public account found is one username-match row with matchBasis: "username-from-email", the sameUsername searched and the profileUrl.
A username match is not an identity match. The row says an account with the same username exists on that site. Different people share usernames, so read it as a lead and confirm it yourself; every row carries the note Same username; not verified as the same person.
The layer is skipped, with a free username-layer-skipped notice, when the local part is four characters or fewer, is a shared mailbox such as info@ or support@, or has characters usernames cannot contain. Turn the layer off with usernameLayer: false. Up to maxUsernameCandidates (default 3) forms are searched, the 2nd and 3rd only when the run has spare time; rows for candidates 2 and 3 are less certain matches (usernameCandidateRank). On a paid plan deepUsernameSearch: true also runs Maigret (top 500 sites) on the dot-less form. Only sites Sherlock did not already return are added, as username-match rows with matchSource: "maigret". Maigret takes minutes per address, so send few addresses and raise the run timeout.
Company domain layers (domain-record and domain-asset rows)
When the address is on a company domain, includeDomainInfo (on by default) adds one row per public record: the WHOIS registration, each MX host, the SPF policy and the DMARC policy. A domain is looked up once per run, however many of your addresses share it. includeDomainAssets (off by default) adds the subdomains, host IPs and same-domain addresses listed in public certificate and host records, up to maxDomainAssets per domain (default 200). It is off by default because a single domain can return hundreds of billed rows; turn it on when you want the footprint, and set the run's maximum cost before a large batch. These rows describe a domain, not an account: exists is false on them.
Extended sites layer (account-found rows with source: "user-scanner")
extendedSites (on by default) checks 90 more sites after the 120+ site check, one lookup per site and address. Each site where the address is registered is one row with exists: true, the same shape as the other account rows plus source: "user-scanner", profileUrl and siteCategory, and it is billed as the same account-found event and price — there is no separate price. A site holehe already reported is not repeated. Sites that error leave no row and no charge. On a run priced with site-checked, an extended site that confirms the address is not registered is one not-registered row billed as site-checked, exactly like a holehe site; a site both engines cover counts once. Set extendedSites: false to get exactly the 120+ site result and bill.
The same layer also asks 11 of the holehe sites a second time — Instagram, Pinterest, Spotify, Tumblr, Adobe, Atlassian, Quora, Patreon, CodePen, Firefox and Vivino — but only for an address where holehe got no answer from that site. These are not extra sites: the site counts once towards maxSitesPerEmail and the run's maximum, and its one answer is billed once. A site holehe answered, either way, is never asked again. If two lookups of one site still end with opposite confirmed answers, neither is delivered or billed and a free site-answer-conflict notice names the site.
When run time is short, the extended sites run before the username layer, so a tight run may return fewer username-match rows than the same run with extendedSites: false. An address that holehe finds nothing on but the extended sites do is billed by account-found alone (no email-checked, no no-match row). If the run has no time left to deliver those found rows, a layer-delivery-time-budget notice counts them and the address still gets no no-match row and no email-checked charge. Where a site's only link is a Google Play or App Store page, profileUrl carries that page and domain is empty.
FAQ — are the extended sites a separate product? No. They add rows to the same account list at the same per-account price; the list of 90 sites is fixed per release and comes from user-scanner (MIT, version 1.5.2.2).
Gravatar layer (profile row)
includeGravatar (on by default) looks up the address's public Gravatar profile. If one exists you get one profile row (display name, photo URL and public profile link on paid plans; on the free plan the first profile of the run is shown in full and later ones are locked); exists is true because the profile is registered with this email. If there is no profile there is no row and no charge.
All of these layers only use run time left over after the account checks, so they never reduce how many addresses a run checks. When a run has no spare time they are skipped with a free company-layers-time-budget notice.
A confirmed account on a site holehe flags as frequently rate-limiting — still billed, with rateLimit: true telling you the site is worth re-verifying:
{"email": "test@gmail.com","platform": "anydo","exists": true,"domain": "any.do","method": "login","emailRecovery": null,"phoneNumber": null,"rateLimit": true,"charged": true,"sitesChecked": 121}
When an address is registered nowhere, you get a completed-check row instead
(platform: null, exists: false, checkCompleted: true, matchStatus: no-match,
foundCount: 0). A real engine or system failure is different: the address gets an error row
with checkCompleted: false and no completed-check or account-found fee. If every address
fails this way, no run-start, account or completed-check fee is charged (email-analysis rows
already delivered keep their own charge once that event is active), the run ends SUCCEEDED, and
the status message names the cause. To detect unfinished work, check rowType: error and checkCompleted: false rather
than the run status. A billing failure that cannot be confirmed still ends the run FAILED.
You can download the dataset as JSON, HTML, CSV or Excel.
Email analysis row
emailAnalysis (on by default) adds one rowType: "analysis" row per input address, before the site check, including addresses whose format is invalid (formatValid: false). Invalid inputs share the run's address limit (maxEmails, the free-plan input limit and the run-time budget) with the valid ones, and valid addresses are counted first. It does not count against the free-plan result sample. Set emailAnalysis: false to skip it.
| Field | Type | Description |
|---|---|---|
formatValid | boolean | Whether the address has a valid local part and domain syntax |
localPart / domain | string | The two halves of the address |
isDisposable | boolean | The domain is on the public disposable-email-domains list (CC0); null when the format is invalid |
isRoleAccount | boolean | The local part is a role name such as admin, info, support, sales, contact or noreply; null when the format is invalid |
isFreeProvider | boolean | The domain is a free mail provider such as Gmail, Outlook or Yahoo; null when the format is invalid |
hasMx | boolean | The domain publishes MX records; null when the lookup failed or the format is invalid |
mxHosts | array | Up to 5 MX hosts, best preference first |
emailAnalysisEventActive / emailAnalysisCharged / emailAnalysisBillingStatus | boolean / boolean / string | Whether the email-analysis event is in the run's price table, whether this row was billed, and the status (inactive, charged, platform-unbilled) |
Pricing
Current active billing:
- $0.005 once per run (event:
run-started, listed as "Charged once when a run starts and performs the email investigation"). It is charged when the first address actually completes its investigation, so a run where every address fails before completion is not charged. - $0.02 per registered account delivered (event:
account-found), including accounts on the 90 extended sites.
Completed-check fee pending activation: the Actor declares $0.01 per email whose
completed site investigation found no account (event: email-checked). It applies only to that
no-match answer: an address with one or more accounts is billed by account-found alone, so
the same address is never billed twice. The code checks the run's effective price table and
charges this event only after the platform activates it; until then no email-checked
charge is attempted. Invalid input and an engine or system failure that prevents completion
never incur this fee.
Completed "not registered" lookups — site-checked, charged only when the run's price table lists it.
The Actor declares $0.02 per completed lookup of one address on one site whose answer is a
confirmed "not registered" (event: site-checked, with the same plan discounts as
account-found). One lookup is one event, never two:
| The site's answer for this address | Billed as | Row |
|---|---|---|
| Registered | account-found | matchStatus: "found" |
| Confirmed not registered | site-checked | matchStatus: "not-registered", billingEvent: "site-checked" |
| Blocked, throttled, errored or no usable answer | nothing | no row; counted in sitesUndetermined or the extended-sites-summary notice |
| Two lookups of the site gave opposite confirmed answers | nothing | no result row; named in a site-answer-conflict notice |
A site covered by both engines counts once, and a site that reported an account is never also
billed as site-checked. On a run priced with site-checked the email-checked fee is not
charged: the same no-account answer is not billed twice. The code reads the run's effective price
table and charges site-checked only when the event is listed there. On a run whose price table
does not list it, billing and output are exactly as described under "Current active billing": no
site-checked charge is attempted and no not-registered rows are written.
What it means for the cost of a run priced with site-checked: an address is looked up on up to
211 sites, so the site lookups of one address cost at most 211 × $0.02 = $4.22 at list price,
and less whenever sites give no confirmed answer. In a measured run (2026-10-13) an address
registered nowhere got 139 confirmed answers out of 211 sites, $2.785 at list price with the
run start. Before the first lookup, the run log and the run's status message state
addresses × sites and the maximum for your run at your plan's prices. To spend less:
- set
maxSitesPerEmail(for example40caps the site lookups of one address at $0.80), - set
extendedSites: falseto stay on the 121 holehe sites, - set the run's maximum cost in Run options: the run stops there, keeps what it delivered and
ends successfully with a
Charging limit reachedrecord.
Free Apify plans get no not-registered rows and no site-checked charge; the free-plan-preview
record counts the confirmed "not registered" lookups of the sample and includes them in the
estimated paid cost.
Email analysis row — not yet active. The email-analysis event is not yet active; the row is free until it is listed on the pricing tab.
Username layer pricing — pending registration. The username-match event ($0.005 proposed
per match row) is declared in the Actor but is not yet registered in the Store price list,
so no username-match charge applies until it is registered and its notice period has
passed. Until then the rows are delivered at no charge (
usernameMatchBillingStatus: "inactive"Company domain and Gravatar pricing — pending registration. The domain-record ($0.005 proposed
per record row), domain-asset ($0.001 proposed per asset row) and profile-found ($0.005 proposed
per profile) events are declared in the Actor but are pending registration: they start after the listed
effective date, once they are in the Store price list and their notice period has passed. Until then
the rows are delivered at no charge (domainRecordBillingStatus, domainAssetBillingStatus and
profileBillingStatus read "inactive"). The Actor charges each event only when the run's own price
table carries it, and this README will be updated with the effective date before that happens.
Free plans and paid plans
| Free Apify plan | Any paid plan | |
|---|---|---|
| Addresses checked per run | the first 1 | up to 500 |
| Sites looked up per address | all 121 holehe modules + the extended sites (shorter time budget) | all 121 holehe modules + 90 extended sites |
| Account rows returned | the first 3 holehe accounts in full + 1 extended-site account; every other account is counted, not named (accountsShownOfTotal, e.g. 3 of 10 shown) | every account found |
Sites that confirmed "not registered" (runs priced with site-checked) | no rows, no charge; counted in free-plan-preview (siteCheckRowsLocked) and included in its paid-cost estimate | one not-registered row per site, billed as site-checked |
| When the limit is reached | run finishes SUCCEEDED with a Free tier limit reached status message that says it is the free-plan sample, not an error, and a free-plan-cap record | no such limit |
| Extended sites (90) | 1 row shown; the rest are counted as locked (extendedSiteAccountsLocked, extendedSiteAccountsShownOfTotal, with extendedSiteLockedCostUsd, the list-price cost to unlock them) in free-plan-preview | every account found, same account-found price |
| Username layer | 1 username-match row per address, plus the number left locked (usernameMatchesShownOfTotal) | every match across up to 3 username candidates, and deepUsernameSearch (adds Maigret; deepUsernameSearchLocked on the free plan) |
| Company domain records | one domain-record row of each type (WHOIS, MX, SPF, DMARC) per run, plus the number left locked | every record |
| Company domain assets | one domain-asset row of each type (subdomain, IP, address) per run, plus the number left locked | every asset up to maxDomainAssets |
| Gravatar | the first profile row of the run in full; later ones say only that a profile exists (name, photo and link locked) | every profile in full |
| Last row of the run | a free free-plan-preview record: accounts and each layer as N of M shown (counted from this run), the list-price cost of the locked rows (lockedRowsCostUsd), the estimated paid cost of the addresses checked here, and the addresses left unchecked with their estimated cost (addressesNotChecked, estimatedPaidCostNotCheckedAddressesUsd; estimates marked as such); written only when at least one address lookup finished | none |
| Order of a row and its charge | each row is delivered first and charged right after; if the platform restarts the run in between, that row is yours and is not charged | each row is charged first and delivered once the charge is confirmed |
| Event price per account found | $0.02 | $0.016 Bronze · $0.013 Silver · $0.0104 Gold and above |
Event price per site that confirmed "not registered" (site-checked, when listed in the run's price table) | not charged | $0.02 list · $0.016 Bronze · $0.013 Silver · $0.0104 Gold and above |
| Event price per run started | $0.005 | $0.004 Bronze · $0.00325 Silver · $0.0026 Gold and above |
The sample limits are policy, not a failure: nothing is retried, nothing errors, and the rows already delivered are kept. Your run log names the plan and the discount tier it was priced at before the first lookup starts. Platform credits, maximum-cost settings, the 500-email hard cap and the run-time budget apply on every plan.
| What you run | Accounts found | Cost |
|---|---|---|
| 1 typical personal address, current billing | 10–30 | $0.205 – $0.605 |
| 1 typical personal address, after completed-check activation | 10–30 | $0.205 – $0.605 (no completed-check fee when accounts are found) |
| 1 completed no-match, current billing | 0 | $0.005 |
| 1 completed no-match, after activation | 0 | $0.015 |
1 address registered nowhere, run priced with site-checked, every site | 0 | up to $4.225 (211 × $0.02 + $0.005 run); only confirmed answers are billed (measured: 139 of 211, $2.785) |
The same with maxSitesPerEmail: 40 | 0 | up to $0.805 |
1 typical personal address, run priced with site-checked, every site | 10–30 | up to $4.225: each of the 211 lookups is billed once, as account-found or as site-checked, both $0.02 |
A live test of test@gmail.com returned 17 confirmed accounts. At current active
billing that is $0.345 ($0.005 run + 17 × $0.02). After the completed-check event is
activated, the same run costs the same $0.345, because a found address is billed by
account-found only. The $0.01 completed-check fee applies to no-match addresses.
The legacy charged field continues to describe only the account-found event. A
no-match is never relabeled as an account finding. The separate
emailCheckedBillingStatus field reports the once-per-email completed-check event when
it is active. Notices, including free-plan-preview, are not product findings and do not create extra product fees.
The examples above show Actor event fees only. Apify platform usage is charged separately
under the customer's current Apify plan and the platform's applicable rates. Set the
run's maximum cost in Run options before a large batch.
Large batches and the run charge limit
Every Apify run has a maximum cost, which you set per run (or per schedule) in the Actor's Run options. This Actor accepts up to 500 email addresses in a single run, and a big batch can easily find more accounts than the default limit covers.
When a run reaches its charge limit, this Actor stops checking and finishes successfully with everything it found up to that point, plus a final Charging limit reached record explaining what happened. You are never charged past your limit, and the run is not reported as a failure.
To get the full batch, do one of the following:
- Raise the run's maximum cost in Run options before starting, or
- Split the email addresses across several runs — for example 25 emails per run instead of 500.
Quick Start
curl
curl -X POST "https://api.apify.com/v2/acts/ntriqpro~email-osint-search/runs?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"emails": ["alice@example.com"]}'
Python (Apify Client)
from apify_client import ApifyClientclient = ApifyClient("YOUR_TOKEN")run = client.actor("ntriqpro/email-osint-search").call(run_input={"emails": ["alice@example.com", "bob@example.org"]})items = list(client.dataset(run["defaultDatasetId"]).iterate_items())for item in items:if not item.get("exists"):continueflag = " (site frequently rate-limits — re-verify)" if item.get("rateLimit") else ""print(f"{item['email']} -> {item['platform']} ({item['domain']}){flag}")
Technology
- holehe (GPL-3.0) — Email-based account existence checker, 5.6k+ stars
- user-scanner (MIT) — Email lookup modules for the 90 extended sites
- Sherlock (MIT) — Username lookup for the username layer
- Maigret (MIT) — Deeper username lookup (top 500 sites) for the deep username search
- dnspython (ISC) — MX, SPF and DMARC lookups for the company domain layer
- Public WHOIS servers, certificate-transparency records (crt.sh, Cert Spotter), HackerTarget host search and Gravatar's public profile API — queried only for the layers you turn on
- Apify SDK for Python (Apache 2.0) — Actor runtime
- httpx (BSD) — Async HTTP client
Limitations
| Limitation | Detail |
|---|---|
| Sites that frequently rate-limit | holehe flags ~10-15% of sites as ones that throttle frequent lookups. Confirmed accounts on those sites are flagged rateLimit: true so you can re-verify |
| Same username, not same person | username-match rows only show that the same username exists on a site. Common or short usernames match many strangers; treat them as leads |
| Sites that give no confirmed answer | A site that blocks or throttles a lookup, or answers in a form the engine cannot read, gives neither "registered" nor "not registered". Such lookups produce no row, are never billed (account-found or site-checked) and are counted in sitesUndetermined. The share changes with the site, the address and the time of the run, and can be a large part of the 121 holehe sites. For 11 of them a second engine is asked when holehe gets no answer |
| Extended sites | 90 sites, one lookup per site and address, 10-second limit each. A site that errors or times out leaves no row and no charge; the end-of-run extended-sites-summary notice counts errors per site. The layer only uses spare run time, so a batch near its time limit may skip it for some addresses (free notice, no charge). A site that holehe already reported is not repeated |
| False positives | Existence signals can be ambiguous; treat as leads, not proof |
| New site discovery | holehe upstream adds sites quarterly; we update with library releases |
| Rate limiting | Recommended: batch <10 emails per run to avoid IP-based throttling |
| Billing retries | An ambiguous charging failure is not retried within the same run because the remote charge may already have succeeded. |
| Platform restarts | If the platform moves or resurrects a run, the run continues from its own record: a finished lookup is not sent to the site again and a row already in the dataset is not written again. A charge that was in flight at the moment of the restart is checked against the platform's own charge count for the run: what the platform counted is not charged again and its result is delivered; what it did not count is charged once and then delivered. A company domain that several addresses of the run share is looked up once, and after a restart its rows stay under the address they were first charged to, so the domain is not charged again under another address. Records in the run's key-value store cannot make a result free: a restarted run writes a charged row only while the platform's charge count covers it, and rows it does not cover are left out and counted in a restart-rows-withheld notice. If the platform's charge count or the dataset cannot be read after a restart, results the earlier part of the run already recorded are neither charged again nor delivered, and an error row says so. On the free plan a row is delivered before it is charged: after a restart, a row already in the dataset (or recorded as delivered) is neither delivered nor charged again, the sample is chosen from the lookup's answer the same way in every part of the run, and a restart that falls between a row and its charge leaves that row delivered and not charged. Starting a new run with the same input is a new run and is billed as one |
Disclaimer
Legal Disclaimer: This Actor is an unofficial integration of holehe (megadose) and is not affiliated with or endorsed by the original project. Use only on email addresses you own or have explicit authorization to investigate. Comply with PIPA (KR), GDPR (EU), CCPA (US), and applicable privacy laws.
This Actor is an unofficial open-source wrapper around megadose/holehe. It is not affiliated with, sponsored by, or endorsed by the holehe project, its maintainers, or any of the platforms being probed. OSINT results are based on publicly observable account-existence signals and may produce false positives.
You are solely responsible for ensuring you have legal authorization to investigate any email address. Misuse may violate privacy laws (PIPA, GDPR, CCPA, etc.) and the terms of service of target platforms. This tool is intended for security research, fraud prevention, and authorized investigation use only.
Privacy & data responsibility
You are the data controller for every search you run with this Actor; ntriqpro is a data processor acting solely on your instructions. You are responsible for having a lawful basis and a legitimate, purpose-limited reason for each lookup, and for complying with GDPR, PIPA, CCPA, and other applicable privacy laws. We do not keep copies of your inputs or results outside your own Apify run; searched identifiers are masked in run logs. These roles are governed by the Standard Actor Contract (§5.2) that applies to this Actor on Apify. Public availability of a profile is not, by itself, a lawful basis — you need your own. Use this Actor only for lawful purposes and only on email addresses you own or are authorized to investigate.
🔗 Related Actors by ntriqpro
Running several of these by hand? OSINT Recon Suite takes one target — email, username, domain or phone — runs Maigret, Sherlock, holehe, theHarvester, WHOIS and dnstwist against it, then correlates everything into a single risk-scored report. One input, one report, instead of six separate runs to stitch together yourself.
Build your full OSINT stack:
- maigret-actor — Username OSINT across 3000+ sites (5.0★ rated)
- phoneinfoga-osint — Phone number OSINT — carrier, country & footprint
- dnstwist-osint — Typosquatting & phishing domain detector
- subfinder-osint — Subdomain finder for attack-surface mapping
- gitleaks-secret-scanner — Find leaked secrets in public repos
⭐ Rate this Actor
If this saves you investigation time, please leave a review — it helps other security researchers discover it.