Instagram Email Scraper — Emails, Phone Numbers & Contacts
Pricing
$10.00 / 1,000 profiles
Instagram Email Scraper — Emails, Phone Numbers & Contacts
Scrape Instagram profiles for the contact details they publish — email, phone, website and business category — from a list of usernames you paste. One read per profile, no posts fetched, no login needed.
Pricing
$10.00 / 1,000 profiles
Rating
0.0
(0)
Developer
Andrey Afanasenko
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
2
Monthly active users
2 days ago
Last modified
Categories
Share
Paste a list of Instagram usernames and get back the contact details each account publishes — email, phone, website and business category — as one row per account. No login, no cookies, no Instagram account of yours involved.
You already know whose contacts you want. This actor reads each account once and returns what it publishes: the email on its Contact button or in its bio, the phone number, the bio link, the category, the follower count. Nothing is discovered, no posts are fetched, and nothing is guessed — every value in the output is something the account owner chose to put on their own profile.
🎯 What it does
Give it handles, get back contacts. One account in, one row out.
| You paste | You get back |
|---|---|
natgeo | The email, phone, website, category, bio and follower count that account publishes |
| 500 handles from a CSV | 500 rows, in the order you gave them, ready to import |
| A list with private or deleted accounts in it | Those rows named in SKIPPED_ACCOUNTS, and no charge for them |
⚠️ Read this before you run it
Two accounts in three publish no contact details at all — and which two depends entirely on the kind of account.
Instagram has three kinds. Business and creator accounts are both professional: both can show a Contact button, and both do, often. A personal account cannot show one at all — whatever it publishes is written into the bio by hand.
So the yield of a list is decided before you run anything, by who is on it. Read the table below before you size one.
Measured on 44 accounts drawn at random from real customer lists:
| Account kind | Share of the list | Published an email or a phone |
|---|---|---|
| Business | 14 of 44 | 43% |
| Creator | 18 of 44 | 39% |
| Personal | 12 of 44 | 17% — and every one of those was an address typed into the bio, never a contact field |
| All three together | 44 | 34% |
And by field, across all 44:
| Share that publishes it | |
|---|---|
| An email (Contact button or bio) | 30% |
| A phone | 14% |
| A website (bio link) | 55% |
| A business category | 36% |
Three things follow, and they are what to know before you spend anything:
- A professional list is worth about 2.4× a personal one — 41% against 17%. Local businesses, agencies, shops and creators all do far better than a list of ordinary followers.
- Creators count as professional. They are the largest group here — 18 of 44 — and they publish contacts almost as often as businesses do. Filtering for "business only" throws most of them away; use Professional if what you want is "anyone who can be contacted".
- Every account that published a phone also published an email. A phone-only lead does not exist in practice, so turning phone extraction on adds a column rather than adding leads. It is off by default for that reason.
If a run comes back with no contacts at all, the actor says so and tells you whether that was ordinary for the length of your list or whether it says something about the list itself.
⚡ Quick start
A minimal input that works on the free plan (30 accounts, no setup):
{"specificUsernamesList": ["natgeo", "nasa", "nike"],"extractEmail": true,"extractWebsiteUrl": true}
Leave Accounts to scrape at its default and the run does a small capped demo instead of failing, so you can see the output shape before deciding what to ask for.
📦 Output sample
A real row, copied unchanged from a run of this actor on 4 September 2026 (Profile Picture truncated — the live URL is a 700-character signed link):
{"Account": "https://instagram.com/semrush","Source": "Direct Input","Full Name": "Semrush","Followers Count": 170891,"Following Count": 1189,"Biography": "An Adobe Company. Helping 28M+ marketers be found everywhere search happens","Email": "mail@semrush.com","Email Source": "public_field","Phone": "+18558144510","External URL": "https://linktr.ee/socialmediasemrush","Category": "N/A","Account Type": "business","Verified": "yes","Profile Picture": "https://scontent-lax3-1.cdninstagram.com/v/t51.82787-19/650988809_...jpg","Total Posts": 1958,"Analysis Status": "analyzed","Why Not Analyzed": "N/A"}
Matched By is the eighteenth column and is absent above because no filter was set on that run — it appears only when a category or keyword filter decided the row.
That run read ten well-known business accounts and one of them published an email. That is the shape of this data, not a bad draw — those ten were all businesses, the kind that does best.
Every account is pushed to the dataset as one item. Columns marked optional appear only when the matching extraction switch is on; everything else is on every row.
| Column | Always? | What it is |
|---|---|---|
Account | yes | https://instagram.com/<username> |
Source | yes | Direct Input for a row you asked for |
Matched By | when a category or keyword filter is on | Which filter kept this row: category, keyword, both |
Full Name | yes | The display name |
Followers Count | yes | Followers at the moment the account was read |
Following Count | yes | Accounts it follows |
Biography | yes | Bio text, including line breaks and emoji |
Email | optional, on by default | The published address, or N/A |
Email Source | optional, on by default | public_field (the Contact button) or bio (parsed from the text) |
Phone | optional, off by default | The published phone number, or N/A |
External URL | optional, on by default | The bio link, or N/A |
Category | optional, on by default | The Instagram category — «Athlete», «Restaurant», … |
Account Type | yes | business, creator or personal — the first two are professional and can show a Contact button |
Verified | yes | yes or no |
Profile Picture | when Instagram exposed one | Avatar URL |
Total Posts | yes | Lifetime post count on the profile |
Analysis Status | yes | analyzed, or not_analyzed on a notice row |
Why Not Analyzed | yes | The reason on a notice row, N/A otherwise |
Email Source is worth reading in an outreach list. A public_field address is the one the account chose to publish as its point of contact; a bio address is whatever the bio text happened to contain, which is sometimes a personal address and sometimes someone else's.
🧭 When to use it
- Turning a list into an outreach list. You have handles from a CSV, a scrape, an event page or a hashtag export, and you need the way to reach them.
- Local business prospecting. Restaurants, gyms, clinics and shops are professional accounts with a filled-in Contact button far more often than ordinary personal ones — 43% against 17% in the measured sample.
- Qualifying a list before you buy tooling for it. Run 30 accounts on the free plan and read the contact rate off your own list rather than off anybody's marketing.
- Enriching a CRM. Give it the handles you already store and get the email, site and category back as columns.
Want the same contact details for everyone who follows an account? That is Instagram Followers & Following Extractor — it walks a followers or following list and returns email, phone and bio link per profile, the same fields as here. For any other way of arriving at a list — a hashtag, a niche, a post's likers — the table at the bottom points at the actor for it.
🛠 Input
The form is four sections:
🎯 Accounts to scrape — your list, and a spending cap.
| Field | Key | Default |
|---|---|---|
| Accounts to scrape | specificUsernamesList | the demo pair |
| Max accounts to process | maxCountList | 0 (no limit) |
One account per line — a bare handle, an @handle and a full profile link all work. Max accounts to process is a spending cap: each account costs $0.01, so setting it to 100 means the run cannot go past $1.00. On the free plan a run stops at 30 accounts whatever you put there.
⚙️ What to extract — extractEmail (on), extractWebsiteUrl (on), extractBusinessCategory (on), extractPhoneNumber (off). You pay per account, not per field — switching one off removes a column from the output, it does not make the run cheaper.
🔬 Filters (optional) — contact-info presence, follower band, account type (professional / business / creator / personal), verified, has-website, category, keywords in bio or name, and an AND/OR combinator for the last two.
▶️ Run & storage options — clearSavedData (start fresh, or resume an interrupted run) and excludeAccounts (handles to skip before they are read or charged for).
💰 Pricing
The actor uses Apify's Pay Per Event model with one single event:
| Event | Price | Triggered |
|---|---|---|
| Profile | $0.01 | Once per account successfully read, whether or not it passes your filters. |
Not charged: accounts that could not be read (not found, private, failed), and anything on your excludeAccounts list. On your first run you are also not charged for accounts your own filters rejected, up to 100 — so the run where you are most likely to over-filter is the one where over-filtering costs nothing.
You are billed per account, not per API call, so maxCountList translates directly into your maximum spend: 500 accounts is $5.00, whatever their bios turn out to contain.
What filters do to your bill
Filters run after the read. An account has to be read before the actor can tell whether it has an email or matches your follower band, so a narrow filter stack costs exactly the same as no filters and returns fewer rows.
That matters most for Filter by Contact Info Presence. At a 34% contact rate, setting it to "Has Email or Phone" means roughly two thirds of what you pay for is discarded before it reaches the dataset. It is still the right switch when you want a clean list and are happy to pay for the reading — but it is a formatting convenience, not a saving.
What to do instead, on the first run: leave the filters alone, read 30 accounts on the free plan, and look at the contact rate your own list actually returns. Then decide.
🆓 Free vs paid plan
| Free plan | Paid plan | |
|---|---|---|
| Accounts per run | 30 | unlimited |
| Runs per calendar month | 15 | unlimited |
| Every extraction option | ✓ | ✓ |
| Every filter | ✓ | ✓ |
| Resume from checkpoint | ✓ | ✓ |
Accounts held back by the free ceiling are handed back to you by name, as extra rows in the dataset with Analysis Status: not_analyzed — so you always know which of your handles were not reached, not merely how many.
Demo runs do not consume the monthly allowance. A run that ends by itself before reading anything gives its slot back — a configuration error, say. A run you abort keeps its slot: the allowance is reserved before the first read precisely so that stopping a run mid-flight is not a way around it.
🧪 Recipes
A plain contact list from handles you already have
{"specificUsernamesList": ["handle1", "handle2", "handle3"],"extractEmail": true,"extractWebsiteUrl": true}
Only the accounts that published an email
{"specificUsernamesList": ["handle1", "handle2"],"extractEmail": true,"contactInfoType": "emailOnly"}
You still pay for every account read; the filter decides what reaches the dataset, not what is read.
Businesses only, above a size floor
{"specificUsernamesList": ["handle1", "handle2"],"accountType": "business","minFollowers": 1000,"extractEmail": true,"extractBusinessCategory": true}
Cap the spend on a long list
{"specificUsernamesList": ["...500 handles..."],"maxCountList": 100}
Reads the first 100 and stops — a hard $1.00 ceiling.
Do not pay twice for handles you already have
{"specificUsernamesList": ["...this week's list..."],"excludeAccounts": ["...everything from last week..."]}
Excluded handles are dropped before they are read, so they cost nothing.
🔁 Resume / checkpoint
A run that is interrupted — a platform migration, a timeout, an abort — saves its progress. Start it again with clearSavedData unchecked and it continues from where it stopped without paying again for the accounts it had already read.
📡 Live status & storage records
While a run is going, its Output tab shows a live status page: how many accounts are done, how many contacts have been found, and what it has spent so far.
When it finishes, the key-value store holds:
| Record | What it is |
|---|---|
RUN_SUMMARY | Everything the run counted — read, charged, filtered, contact yield, cost |
USER_MESSAGE | The one thing the run most needs to tell you, in plain words |
SKIPPED_ACCOUNTS | Every handle that was skipped, with the reason and its category |
FREE_LIMITS_APPLIED | Which free-plan ceilings applied, as stable ids |
FILTER_BURN_PREDICTION | The pre-read yield estimate for your filter combination |
API_USAGE | Upstream request count for this run |
🤖 Programmatic / API use
Start a run and wait for it with the Apify API:
curl -X POST "https://api.apify.com/v2/acts/afanasenko~instagram-email-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"specificUsernamesList":["natgeo","nasa"],"extractEmail":true}'
Or with the JavaScript client:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_TOKEN' });const run = await client.actor('afanasenko/instagram-email-scraper').call({specificUsernamesList: ['natgeo', 'nasa'],extractEmail: true,});const { items } = await client.dataset(run.defaultDatasetId).listItems();
Every value a run needs to explain itself is in the key-value store, not only in the log — a caller that never reads the console still gets USER_MESSAGE, RUN_SUMMARY and SKIPPED_ACCOUNTS.
💡 Tips & best practices
- Run 30 first. The contact rate on your own list is the only number that matters, and the free plan measures it for you.
- Prefer business-heavy lists. Local businesses and agencies publish a Contact button far more often than creators and personal accounts do.
- Leave phone extraction off unless you need the column. Every account in the sample that published a phone also published an email.
- Read
External URLwhenEmailisN/A. 55% of accounts have a bio link and most business sites have a contact form behind it — that is the fastest second route. - Do not set filters on the first run. They cost the same as no filters and hide the thing you are trying to measure.
❓ FAQ
Why is Email "N/A" for so many accounts?
Because those accounts publish no email. Instagram shows an email only on a professional account whose owner filled in the Contact button; a personal account has nowhere to put one. Measured on 44 real accounts, 30% publish an email. This actor returns what is published and never guesses an address.
Can it find an email that is not on the profile? No. It reads the Contact button and the bio text, and that is all there is to read. Nothing is inferred from the name, the domain or anywhere off Instagram.
Do I need an Instagram account or a login? No. It reads public profiles only.
What happens to private accounts in my list?
They are skipped before anything is charged and named in SKIPPED_ACCOUNTS. A private account publishes no contact details to anyone.
Does it read posts? No. One read per account, the profile only. That is why it costs what it costs.
What if my list has duplicates or profile URLs mixed with handles?
Both are handled: duplicates are read once, and @handle, bare handle and full profile link are all accepted.
How do I know which of my handles were not reached?
They come back as rows with Analysis Status: not_analyzed and the reason in Why Not Analyzed, plus the full list in SKIPPED_ACCOUNTS.
Is scraping this data legal? It reads publicly available profile information. What you may then do with it is governed by GDPR, CCPA and Instagram's terms — see the disclaimer.
🔗 Other Instagram scrapers (same author)
Pick the one that matches what you start with:
| You start with… | You want to find… | Use this actor |
|---|---|---|
| A list of usernames | The contact details each one publishes | This actor |
| A keyword or a hashtag | The accounts in that niche, enriched and filtered | Instagram Influencer Search |
| 1–5 seed handles you like | Similar / suggested accounts in the same niche | Instagram Related Profiles Scraper |
| One known account | Its full followers or following list — with the same email, phone and bio link as here, on every profile | Instagram Followers & Following Extractor |
| One known account | Track who follows / unfollows it over time (scheduled) | Instagram Follower Tracker |
| A post or reel link | The accounts that liked or commented on it | Instagram Likes Scraper |
| Anything else (handles, post links, locations, keywords) | All of the above plus five other discovery modes in one tool | Instagram Profile Scraper |
| A reel link or a creator handle | What their reels say and show — spoken transcript, on-screen text, hook and structure | Instagram Reel Script Extractor |
| An AI agent (Claude, Cursor, ChatGPT) | The same Instagram data from inside your agent — MCP tools it picks by intent | Instagram Profile MCP Server |
🛟 Support & feedback
- Open an issue on the actor's Issues tab in Apify Console — the fastest way to get a fix shipped.
- Feature requests in the same tab; we read every one.
- Ratings & reviews on the Apify Store page help other users find the actor.
⚖️ Disclaimer
This actor reads publicly available Instagram profile data. It does not bypass authentication, read private profiles, or interact with content on your behalf. Use it in compliance with Instagram's Terms of Service and applicable privacy laws (GDPR, CCPA) — for outreach lists, make sure you have a legitimate interest and a clear unsubscribe path. The contact details returned are what the profile owner chose to publish on Instagram.