Email OSINT Checker — Account Finder & Reverse Lookup avatar

Email OSINT Checker — Account Finder & Reverse Lookup

Pricing

from $10.40 / 1,000 account founds

Go to Apify Store
Email OSINT Checker — Account Finder & Reverse Lookup

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

daehwan kim

Maintained by Community

Actor 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-preview record (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 a free-plan-cap record 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-found when the address is registered on the site and, on runs whose price table lists the site-checked event, site-checked when 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; maxSitesPerEmail and the run's maximum cost let you choose how much to spend. See Pricing.

Free plan vs paid plan at a glance

Apify free planAny paid Apify plan
Addresses per runFirst 1Up to 500
Sites looked up per addressAll 121 holehe modules (+ a short extended-sites pass)All 121 + 90 extended sites
Account rowsFirst 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 countedEvery match, up to 3 username candidates + deepUsernameSearch
Company domain recordsOne of each type (WHOIS, MX, SPF, DMARC) per runEvery record
Company domain assetsOne of each type (subdomain, IP, address) per runEvery asset up to maxDomainAssets
Gravatar profileOne full profile per run (name, photo, link); later profiles show only that they existEvery profile in full
What stayed lockedCounted 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 eventList priceBronze 20% off · Silver 35% off · Gold and above 48% off

What you get

CapabilityDetail
Sites looked up210+ 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 sitesInstagram, 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 sites90 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 lookupsNo password-reset emails sent to the target — passive account-existence signals only
Flat, table-ready rowsOne 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 modeUp to 500 emails per run on any paid plan (free plans: the first address)
Extra identity hintsMasked recovery email and masked partial phone, when the platform exposes them
Username layerAlso 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 layersFor 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 layerOne row when the address has a public Gravatar profile (name, photo and link on paid plans)
Ready to exportDownload the dataset as JSON, HTML, CSV or Excel
No setupNo 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
}
emailplatformdomainexistsmethodrateLimit
test@gmail.comcodepencodepen.iotrueregisterfalse
test@gmail.comanydoany.dotruelogintrue

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

FieldTypeRequiredDescription
emailsarray of string✅Up to 500 email addresses to investigate
emailstring—Legacy single-email input (use emails for batch)
timeoutinteger—Per-site timeout in seconds (default 30)
maxEmailsinteger—How many emails this run processes; hard cap 500
usernameLayerboolean—Search the email's local part as a username (default true); set false for account-registration rows only
maxUsernameCandidatesinteger—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
deepUsernameSearchboolean—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
includeDomainInfoboolean—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
includeDomainAssetsboolean—Default false, because one domain can return hundreds of billed rows: subdomains, host IPs and same-domain addresses from public certificate and host records
maxDomainAssetsinteger—Most domain-asset rows delivered and billed per domain when includeDomainAssets is on (default 200, maximum 1000)
extendedSitesboolean—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
maxSitesPerEmailinteger—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
includeGravatarboolean—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

FieldTypeDescription
emailstringThe address checked
platformstringPlatform short name, e.g. codepen
domainstringPlatform domain, e.g. codepen.io
existsbooleantrue 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
methodstringHow existence was detected: register, login or other
rateLimitbooleantrue 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
sourcestring"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"
billingEventstringThe 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 / siteCheckedBillingStatusboolean / stringOn not-registered rows: whether the row was billed as a site-checked event, and the charge status. Only charged rows are written
conflictSites / sitesConflictingarray of string / integerOn 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
withheldAfterRestartobjectOn 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
chargedbooleantrue 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)
emailRecoverystringMasked recovery-email hint, when the platform exposes one (else null)
phoneNumberstringMasked partial phone, when the platform exposes one (else null)
sitesCheckedintegerTotal platforms probed for this email
sitesUndeterminedintegerOn 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 / modulesMissingCountarray of string / integerOn 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
rowTypestringresult, analysis, username-match, domain-record, domain-asset, profile, notice, or error
companyDomainstringOn domain-record and domain-asset rows: the address's domain
recordType / recordValue / recordDetailsstring / string / objectOn 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 / assetHoststringOn domain-asset rows: subdomain, host-ip or email, its value, and the host it belongs to
displayName / avatarUrlstringOn profile rows: the Gravatar name and photo URL (profileUrl holds the public profile link)
matchSourcestringOn username-match rows added by the deep search: maigret
matchBasisstringOn username-match rows: username-from-email
sameUsernamestringOn username-match rows: the username that was searched (from the email's local part)
usernameCandidateRanknumberOn 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
profileUrlstringOn username-match rows: the profile URL on that site
checkCompletedbooleantrue only when the email investigation finished
matchStatusstringfound, 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
accountDetailsDeliveredbooleanOn a completed-check summary, whether separately billed account detail rows were delivered
emailCheckedBillingStatusstringWhether 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)
disclaimerstringLegal-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.

FieldTypeDescription
formatValidbooleanWhether the address has a valid local part and domain syntax
localPart / domainstringThe two halves of the address
isDisposablebooleanThe domain is on the public disposable-email-domains list (CC0); null when the format is invalid
isRoleAccountbooleanThe local part is a role name such as admin, info, support, sales, contact or noreply; null when the format is invalid
isFreeProviderbooleanThe domain is a free mail provider such as Gmail, Outlook or Yahoo; null when the format is invalid
hasMxbooleanThe domain publishes MX records; null when the lookup failed or the format is invalid
mxHostsarrayUp to 5 MX hosts, best preference first
emailAnalysisEventActive / emailAnalysisCharged / emailAnalysisBillingStatusboolean / boolean / stringWhether 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 addressBilled asRow
Registeredaccount-foundmatchStatus: "found"
Confirmed not registeredsite-checkedmatchStatus: "not-registered", billingEvent: "site-checked"
Blocked, throttled, errored or no usable answernothingno row; counted in sitesUndetermined or the extended-sites-summary notice
Two lookups of the site gave opposite confirmed answersnothingno 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 example 40 caps the site lookups of one address at $0.80),
  • set extendedSites: false to 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 reached record.

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"
). The Actor charges the event only when the run's own price table carries it, and this README will be updated with the effective date before that happens.

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 planAny paid plan
Addresses checked per runthe first 1up to 500
Sites looked up per addressall 121 holehe modules + the extended sites (shorter time budget)all 121 holehe modules + 90 extended sites
Account rows returnedthe 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 estimateone not-registered row per site, billed as site-checked
When the limit is reachedrun 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 recordno 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-previewevery account found, same account-found price
Username layer1 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 recordsone domain-record row of each type (WHOIS, MX, SPF, DMARC) per run, plus the number left lockedevery record
Company domain assetsone domain-asset row of each type (subdomain, IP, address) per run, plus the number left lockedevery asset up to maxDomainAssets
Gravatarthe 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 runa 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 finishednone
Order of a row and its chargeeach row is delivered first and charged right after; if the platform restarts the run in between, that row is yours and is not chargedeach 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 runAccounts foundCost
1 typical personal address, current billing10–30$0.205 – $0.605
1 typical personal address, after completed-check activation10–30$0.205 – $0.605 (no completed-check fee when accounts are found)
1 completed no-match, current billing0$0.005
1 completed no-match, after activation0$0.015
1 address registered nowhere, run priced with site-checked, every site0up to $4.225 (211 × $0.02 + $0.005 run); only confirmed answers are billed (measured: 139 of 211, $2.785)
The same with maxSitesPerEmail: 400up to $0.805
1 typical personal address, run priced with site-checked, every site10–30up 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 ApifyClient
client = 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"):
continue
flag = " (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

LimitationDetail
Sites that frequently rate-limitholehe 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 personusername-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 answerA 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 sites90 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 positivesExistence signals can be ambiguous; treat as leads, not proof
New site discoveryholehe upstream adds sites quarterly; we update with library releases
Rate limitingRecommended: batch <10 emails per run to avoid IP-based throttling
Billing retriesAn ambiguous charging failure is not retried within the same run because the remote charge may already have succeeded.
Platform restartsIf 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.


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:

⭐ Rate this Actor

If this saves you investigation time, please leave a review — it helps other security researchers discover it.