Instagram Profile Email Scraper · Apify
Instagram Profile Email Scraper avatar

Instagram Profile Email Scraper

Pricing

from $1.00 / 1,000 profile results

Go to Apify Store
Instagram Profile Email Scraper

Instagram Profile Email Scraper

Extract public email addresses and rich Instagram profile data, including followers, bio, links, verification status, and business metadata. Ideal for lead generation, CRM enrichment, influencer discovery, and automation.

Pricing

from $1.00 / 1,000 profile results

Rating

5.0

(1)

Developer

LanceAPI

LanceAPI

Maintained by Community

Actor stats

0

Bookmarked

24

Total users

5

Monthly active users

5 hours ago

Last modified

Features

Limited permissions

Share

Instagram Profile Email Scraper API

Instagram Profile Email Scraper turns Instagram profile URLs into structured public profile data plus any email address that profile publishes — and, when the profile publishes none, reads the website that profile links to and picks up the address there. One dataset item per profile, a clear status for every profile, and two-part pricing: a low base price per returned result, plus an email-found charge only when an address is actually delivered.

No Instagram login, cookies, browser session, proxy or external data-source credentials are required from you.

Key features

  • Batch input: one profile URL or up to 100 per run.
  • Public email detection: addresses published in the profile's public contact fields, biography and bio links.
  • Second pass over the linked website: when the profile itself publishes no address, the Actor reads the site it links to — contact page, about page, and the privacy or terms page small shops use instead — and keeps the address it finds there. Client-side sites are handled by reading their JavaScript bundle.
  • Structured enrichment: username, user ID, display name, category, followers, following, account flags, links and avatar URLs when available.
  • One item per profile, always: every valid unique input produces exactly one dataset item, including failures — so a batch never silently loses a row.
  • Explicit statuses: eight documented statuses and a stable errorCode, so a downstream job can branch without parsing prose.
  • Ready-made views: All profiles, Emails found, No email found, Failed profiles.
  • Run summary: counts written to the run's key-value store as RUN_SUMMARY.

Quick input example

{
"profileUrls": [
"https://www.instagram.com/example.creator/"
]
}

Short output example

{
"username": "example.creator",
"profileUrl": "https://www.instagram.com/example.creator/",
"displayName": "Example Creator",
"emails": ["hello@examplecreator.com"],
"followersCount": 24500,
"followingCount": 312,
"category": "Digital creator",
"status": "EMAIL_FOUND",
"errorCode": null
}

Try it in 3 steps

  1. Paste one or more Instagram profile URLs.
  2. Click Start.
  3. Open the Dataset to inspect or export the results.

Start with one profile to confirm the output format, or process batches of up to 100 profile URLs per run. When you are ready, scale with Apify Tasks, schedules, API calls or integrations.

Table of contents

What does this Actor do?

This Actor is an Instagram profile email scraper for profile-level contact discovery. It accepts Instagram profile URLs (including share links with query strings), resolves each one to a canonical username, retrieves that profile's public data, extracts the addresses the profile publishes, and stores one result item per profile.

It does not search for profiles by keyword, scrape follower lists, search hashtags, read posts or Reels for addresses, follow links that are not published by the profile itself, access private content, or solve CAPTCHA challenges.

Duplicates are collapsed: submitting the same profile twice in one run processes it once and returns one item, so you are never billed twice for the same profile in the same run.

Input

FieldTypeRequiredDescription
profileUrlsarray of stringsYesOne or more Instagram profile URLs. Query strings are accepted and normalized. Minimum 1, maximum 100.
scrapeWebsiteContactsbooleanNoDefault true. When a profile publishes no address of its own, visit the website or link-in-bio page listed on that profile and read the addresses it publishes. Adds no extra charge; adds a few seconds for those profiles only.
maxWebsitePagesintegerNoDefault 3, range 1–5. How many pages of a linked site may be read, starting from the homepage.
websiteTimeoutSecondsintegerNoDefault 15, range 5–30. Total time allowed for one website, including page discovery. A slow site is cut off rather than allowed to hold up the run.
websiteCrossDomainEmailsbooleanNoDefault false. Keep an address found on the site whose domain differs from the site's own (an agency, a parent brand, a press contact) in the website evidence block, but do not add it to emails. Set true to accept those addresses too.

Accepted forms — all of these resolve to the same profile:

https://www.instagram.com/example.creator/
instagram.com/example.creator
https://instagram.com/Example.Creator/?igsh=EXAMPLE
@example.creator
example.creator

Rejected forms — a post, Reel, story or any sub-path is not a profile:

https://www.instagram.com/p/CxYz123/
https://www.instagram.com/example.creator/reels/

Invalid values never abort the run. They are skipped, counted in the run summary, and listed there so you can correct them. If every submitted value is invalid, the run still succeeds and returns a single INVALID_INPUT item explaining why — the item is the answer, so the run does not have to fail to deliver it.

API usage

The Actor slug is lance_api/instagram-profile-email-scraper.

cURL

curl -X POST "https://api.apify.com/v2/acts/lance_api~instagram-profile-email-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"profileUrls":["https://www.instagram.com/example.creator/"]}'

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('lance_api/instagram-profile-email-scraper').call({
profileUrls: ['https://www.instagram.com/example.creator/'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);

Python

from apify_client import ApifyClient
client = ApifyClient("YOUR_TOKEN")
run = client.actor("lance_api/instagram-profile-email-scraper").call(
run_input={"profileUrls": ["https://www.instagram.com/example.creator/"]}
)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)

Output

One dataset item per valid unique input profile. The order of the fields below is the order they appear in the Dataset.

Full output example

{
"actorVersion": "1.0.0",
"inputUrl": "https://www.instagram.com/example.creator/?igsh=EXAMPLE",
"profileUrl": "https://www.instagram.com/example.creator/",
"username": "example.creator",
"userId": "52410962",
"eimuId": null,
"displayName": "Example Creator",
"category": "Digital creator",
"emails": ["hello@examplecreator.com", "partners@examplecreator.com"],
"externalUrls": ["https://examplecreator.com"],
"bioLinks": [
{
"title": "Work with us",
"url": "https://examplecreator.com/contact",
"linkType": "external"
}
],
"threadsUrl": null,
"profilePictureUrl": "https://example.com/profile.jpg",
"profilePictureUrlHd": "https://example.com/profile-hd.jpg",
"followersCount": 24500,
"followingCount": 312,
"isVerified": false,
"isPrivate": false,
"isBusinessAccount": true,
"isProfessionalAccount": true,
"businessPhoneNumber": null,
"businessContactMethod": "EMAIL",
"highlightReelCount": 4,
"website": {
"url": "https://examplecreator.com",
"finalUrl": "https://examplecreator.com/",
"status": "ok",
"reason": null,
"httpStatus": 200,
"jsSuspected": false,
"pagesCrawled": 3,
"elapsedMs": 1840,
"candidatesTried": 1,
"emails": [
{
"email": "hello@examplecreator.com",
"method": "mailto",
"confidence": 0.95,
"crossDomain": false,
"sourceUrl": "https://examplecreator.com/contact"
}
],
"phones": [],
"addresses": []
},
"status": "EMAIL_FOUND",
"scrapedAt": "2026-11-05T08:00:00.000Z",
"error": null,
"errorCode": null
}

Complete output field reference

Input and identity

FieldTypeDescription
actorVersionstringCode version that produced the item.
inputUrlstringThe value you submitted, unchanged.
profileUrlstring or nullCanonical profile URL.
usernamestring or nullNormalized lowercase username.
userIdstring or nullInstagram numeric user ID when available.
eimuIdstring or nullAdditional profile identifier when the data source returns one.

Profile information

FieldTypeDescription
displayNamestring or nullDisplay name or full name.
categorystring or nullPublic profile category.
isVerifiedboolean or nullVerified badge.
isPrivateboolean or nullPrivate account flag.
isBusinessAccountboolean or nullBusiness account flag.
isProfessionalAccountboolean or nullProfessional or creator account flag.

Contact information

FieldTypeDescription
emailsarray of stringsDeduplicated, lowercased, validated public addresses. Empty when none were found.
businessPhoneNumberstring or nullPublic business phone number when the profile publishes one.
businessContactMethodstring or nullPublic contact method, for example EMAIL.

Audience information

FieldTypeDescription
followersCountinteger or nullFollower count.
followingCountinteger or nullFollowing count.
highlightReelCountinteger or nullNumber of highlight reels.
FieldTypeDescription
externalUrlsarray of stringsPublic external URLs returned with the profile.
bioLinksarray of objects{ title, url, linkType } for each structured bio link.
threadsUrlstring or nullThreads URL when one is published.
profilePictureUrlstring or nullProfile picture URL.
profilePictureUrlHdstring or nullHigh-resolution profile picture URL.

Website second pass

FieldTypeDescription
websiteobject or nullWhat the linked site published, when it was visited. null when the profile carried no crawlable link, when the pass is switched off, or when the profile already published an address of its own.
website.urlstring or nullLink taken from the profile that was crawled.
website.finalUrlstring or nullWhere the request landed after redirects.
website.statusstringok (an address on the site's own domain was found), no_contact (the site was read and publishes nothing usable), error (the page could not be read), skipped (nothing to crawl).
website.reasonstring or nullWhy the pass ended the way it did — for example js_rendered_page or parked.
website.httpStatusinteger or nullHTTP status of the homepage.
website.jsSuspectedbooleantrue when the page body is rendered by JavaScript; its copy then lives in a bundle, which is read separately.
website.pagesCrawledintegerPages that were actually read successfully.
website.elapsedMsintegerTime spent on the site.
website.candidatesTriedintegerHow many links from the profile were tried before giving up (1 or 2).
website.emailsarray of objectsEvery address the site published, with email, method, confidence, crossDomain and sourceUrl.
website.phonesarray of objectsPhone numbers the site published, with phone, e164, method, confidence and sourceUrl.
website.addressesarray of objectsPostal addresses the site published, with address, method, confidence and sourceUrl.

Processing status

FieldTypeDescription
statusstringOne of the eight statuses below.
errorCodestring or nullStable machine-readable reason. null when status is EMAIL_FOUND or NO_EMAIL_FOUND.
errorstring or nullHuman-readable sentence for a failed item. Never contains internal detail.
scrapedAtstringISO 8601 timestamp of when the item was produced.

Status values

StatusMeaningBase result charge?email-found charge?
EMAIL_FOUNDAt least one valid public address was delivered.YesYes
NO_EMAIL_FOUNDThe profile was read, but publishes no address.YesNo
PROFILE_NOT_FOUNDThe account does not exist.YesNo
PRIVATE_OR_LOGIN_REQUIREDThe profile is private or behind a login wall.YesNo
BLOCKED_OR_RATE_LIMITEDThe data source refused or throttled the request.YesNo
FETCH_FAILEDThe profile could not be read right now; retry later.YesNo
INVALID_INPUTThe submitted value is not an Instagram profile URL.YesNo
FREE_USAGE_LIMIT_REACHEDThe free-plan allowance for this period is used up.YesNo

On the free plan, profiles are capped at 50 per day, 500 per calendar month and 1000 in total (UTC), and a single free run accepts at most 20 profiles while only one free run per account may be in progress at a time. Once any cap is reached, further profiles in that run return FREE_USAGE_LIMIT_REACHED with a message quoting the exact used/limit numbers of the cap that applies. Only profiles that were actually looked up count toward the allowance — duplicate inputs, retries and blocked profiles do not. Paid plans have no such cap. The daily and monthly allowances reset automatically; nothing needs to be done on your side.

A batch over the 20-profile free ceiling, or a second free run started while another is still running, processes nothing and returns one FREE_USAGE_LIMIT_REACHED item naming the limit that was hit. Both cases leave the run itself successful: the refusal is the answer, not a crash, and a caller reads it from the item's status/errorCode rather than from the run's exit code. Split the batch, or wait for the running one to finish, and start again.

errorCode narrows the failure without changing the status:

errorCodeTypical status
PROFILE_NOT_FOUNDPROFILE_NOT_FOUND
PROFILE_IS_PRIVATEPRIVATE_OR_LOGIN_REQUIRED
UPSTREAM_RATE_LIMITEDBLOCKED_OR_RATE_LIMITED
UPSTREAM_QUOTA_EXHAUSTEDBLOCKED_OR_RATE_LIMITED
UPSTREAM_AUTH_FAILEDFETCH_FAILED
UPSTREAM_TIMEOUTFETCH_FAILED
UPSTREAM_ERRORFETCH_FAILED
MALFORMED_RESPONSEFETCH_FAILED
PROFILE_METADATA_UNAVAILABLEFETCH_FAILED
FREE_USAGE_LIMIT_REACHEDFREE_USAGE_LIMIT_REACHED
FREE_BATCH_LIMIT_EXCEEDEDFREE_USAGE_LIMIT_REACHED
FREE_ACTIVE_RUN_LIMITFREE_USAGE_LIMIT_REACHED
FREE_USAGE_SERVICE_UNAVAILABLEFETCH_FAILED
INVALID_PROFILE_URLINVALID_INPUT
BATCH_LIMIT_EXCEEDEDINVALID_INPUT
DATASET_WRITE_FAILEDFETCH_FAILED
INTERNAL_ERRORFETCH_FAILED

New errorCode values may be added over time. Branch on status for stable behaviour; treat errorCode as extra detail.

Where are email addresses found?

Only in fields that belong to the requested profile and are publicly visible, plus the website that same profile publishes:

  1. Public contact fields on the profile (for example a published business or contact email).
  2. The biography text.
  3. The bio link URL, when the link itself is a mailto: or contains an address.
  4. The title of a bio link, when the title is an address.
  5. The profile's own website, and only when steps 1–4 produced nothing. The Actor reads the homepage and the contact, about or imprint pages it links to, decodes mailto: links, [at]/[dot] obfuscation, JSON-LD and microdata, and — for client-side sites whose HTML is an empty shell — the site's own JavaScript bundle. If those still yield nothing, it takes one more look at the refund, privacy or terms page, because small shops often publish their only address there.

Addresses from the website are added to the same emails list and are marked in website.emails with the page they came from (sourceUrl) and the method that proved them. An address on a different domain than the site — an agency, a payment processor, a regulator named in a privacy notice — stays in the evidence block but is not added to emails, unless you set websiteCrossDomainEmails: true.

Every address is normalized to lowercase, de-duplicated and format-checked. Obvious non-addresses are dropped: template placeholders such as your@email.com, documentation and example domains, and strings that only look like addresses.

Addresses are never mined from unrelated parts of the profile data. A profile that publishes nothing on Instagram and links to a site that publishes nothing either yields NO_EMAIL_FOUND — that outcome is a normal, correctly billed result rather than an error.

Dataset views

The dataset ships with four views, so you do not have to build filters:

ViewWhat it shows
All profilesEvery item, with username, profileUrl, emails, status, displayName, followersCount, category first.
Emails foundOnly EMAIL_FOUND items, with the addresses and the core profile fields.
No email foundProfiles that were read successfully but publish nothing.
Failed profilesEverything with an errorCode, including the public error sentence.

Run summary

Each run writes a RUN_SUMMARY record to its key-value store, also linked from the run's Output tab. It contains:

  • status: SUCCEEDED, PARTIAL or FAILED;
  • input: how many values were submitted, how many were unique, how many were duplicates, how many were invalid (with the invalid values listed), how many were processed, and how many were left unprocessed;
  • output: dataset rows, EMAIL_FOUND, NO_EMAIL_FOUND, PROFILE_NOT_FOUND, PRIVATE_OR_LOGIN_REQUIRED, BLOCKED_OR_RATE_LIMITED, FETCH_FAILED, FREE_USAGE_LIMIT_REACHED, total addresses, addresses that came from a linked website, how many profiles had their website visited, total failures;
  • errorTaxonomy: a count per errorCode;
  • notes: present only when something needs explaining — for example, profiles skipped because the run reached the maximum charge set for it. input.notProcessed counts those, and they are never added to output.failed;
  • settings: the per-run profile cap and the concurrency used.

It is safe to log or forward: it contains business counts only.

Pricing

Two events, unchanged from previous versions of this Actor.

EventWhen it is chargedPrice
Base resultOnce per dataset item, charged automatically by Apify$0.001 per item
email-foundOnce per profile whose item delivers at least one address$0.006 (FREE) / $0.004 (BRONZE) / $0.0035 (SILVER) / $0.003 (GOLD, PLATINUM, DIAMOND)

What this means in practice:

  • Several addresses in one profile = one email-found charge. Two addresses are not two charges.
  • No address = no email-found charge. You still pay the base result price for the item.
  • One profile per run = at most one email-found charge, even if the profile is submitted twice.
  • The website pass adds no extra charge. An address it finds is charged as the same single email-found, and if it finds nothing you pay nothing for the attempt.
  • Retries and any internal recovery cost you nothing extra. Only the two events above are ever billed.
  • A failed item can never carry an email-found charge, because the charge is raised only after the item has been written to the dataset.

Public data and responsible use

This Actor reads publicly visible profile data. It does not log in, does not bypass privacy settings and does not return anything a logged-out visitor cannot see. You remain responsible for how you use the data: comply with Instagram's terms, applicable privacy law (for example GDPR or CCPA) and anti-spam rules in your jurisdiction. Use the addresses for legitimate business communication and honour opt-out requests.

FAQ

What input do I need? One or more Instagram profile URLs. Post links, Reel links and other sub-paths are rejected as INVALID_INPUT.

Can I process many profiles at once? Yes, up to 100 unique profiles per run. Duplicates are collapsed and processed once.

Where do you find email addresses? In the profile's own public contact fields, biography and bio links — and, when those publish nothing, on the website that same profile links to. See Where are email addresses found?.

Are the addresses verified? They are validated for format, normalized and de-duplicated, and they are published by the profile itself. They are not SMTP-verified, so an address can still be stale.

Does it work with private profiles? No. A private profile returns PRIVATE_OR_LOGIN_REQUIRED with no data.

Do I need Instagram cookies or a login? No.

What happens when no email is found? You get one item with status: "NO_EMAIL_FOUND", the profile fields that were available, and only the base result charge.

How is a paid result counted? One base result charge per dataset item plus one email-found charge per item that contains at least one address.

Can I use it through an API? Yes — REST, the JavaScript client, the Python client, Tasks, schedules and webhooks all work.

Can I export the results? Yes: JSON, CSV, Excel or XML from the Dataset, or through the API.

Limitations

  • Profile-level only: no keyword search, follower lists, hashtags, posts or Reels.
  • The website pass reads only the links the profile itself publishes, and only when the profile published no address. It follows no third-party links found on that site.
  • A site that hides everything behind a login, a bot challenge (for example a Cloudflare interstitial) or client-side rendering it never exposes in its bundle cannot be read; the website.status field says which of those happened.
  • One Instagram profile per input value; a share link that points at a post is not a profile.
  • Address discovery is limited to what the profile and its own site publish; many creators publish nothing.
  • Availability depends on public profile data being reachable. A blocked or throttled request returns BLOCKED_OR_RATE_LIMITED, and the same profile may succeed on a later run.
  • Very large batches can take a while; the run summary reports progress and outcomes when it finishes.

Support

Open an issue from the Actor's Issues tab with the run ID and the affected profile URL. Please do not paste tokens or private data into an issue.