Instagram Profile & Email Scraper — Instagram Keyword Search
Pricing
from $10.00 / 1,000 profiles
Instagram Profile & Email Scraper — Instagram Keyword Search
Instagram profile scraper, hashtag scraper, email extractor — followers and following lists, post likers and commenters, similar-account and influencer discovery, Instagram keyword and location search. Each profile: email, phone numbers, contact info, bio links, engagement rate, business category.
Pricing
from $10.00 / 1,000 profiles
Rating
2.5
(10)
Developer
Andrey Afanasenko
Maintained by CommunityActor stats
80
Bookmarked
4.6K
Total users
395
Monthly active users
0.2 hours
Issues response
2 days ago
Last modified
Categories
Security
Limited permissionsShare
Analyze Instagram followers, following, or the accounts that engaged with a post to find valuable profiles. Filter large accounts by engagement, contact info, and business category to discover influencers, leads, or ideal customers. Perfect for lead generation, market research, and influencer marketing.
Discover and enrich Instagram profiles that match your target audience — followers and following of any public account, accounts you specify, similar accounts via Instagram's "Suggested for You" graph, keyword and hashtag search, creators tagged at any city or place, and the people who liked or commented on a specific post. Six discovery modes in one actor, with 25+ post-fetch filters — including keywords to require and keywords to exclude in the bio — to narrow the result to your exact persona. Searching the same niche again? The discovery modes can skip the accounts this actor already delivered to you, so a new run returns and charges only accounts you do not have yet.
Instagram's official Graph API is locked to your own business accounts. This actor reads any public profile and returns up to 43 columns per row — bio, contacts (email + phone + website), account type (business, creator or personal), engagement rate, business category, language, Reels analytics, recent post captions, a Quality flag, and — optionally — the country the account is based in and the month it was created.
🎯 What it does
⚠️ Pick your mode first in the
operationModedropdown. Each mode reads only its own input fields. Example: you select Mode 3 (Network Expansion) but type your terms into Mode 4's search-queries field. The actor runs Mode 3 — which doesn't look at the search-queries field — and finds nostartUsernamesseeds to expand from, so the run returns zero rows. All 6 mode sections stay visible regardless of your choice — fill only the section labelled with your selected mode.
Six independent discovery modes — pick what matches the input you already have:
| Mode | Input | What it returns |
|---|---|---|
| 1: Analyze Followers / Following | 1+ competitor handles | Their followers and/or following, enriched. Mutual Follow flag for warm leads (≥ 2 targets). Public accounts with visible lists only — private targets return nothing. Cost cap: maxCount. |
| 2: Analyze a Specific List | List of usernames | Full enrichment of a known list — no discovery. Cost cap: maxCountList. |
| 3: Network Expansion | 3–5 seed handles | Hundreds of similar profiles via Instagram's «Suggested for You» graph. Cost cap: maxCountExpansion. |
| 4: Keyword / Hashtag Discovery | Search queries / hashtags | Search-result authors + top-post hashtag authors, merged round-robin — an influencer search and creator search over the words people put in their own bios. Start with broad single-word terms — rare terms plus tight filters often return zero. Cost cap: maxCountDiscovery. |
| 5: Location Discovery | City, Country seeds | A location search by place: creators tagging posts at specific spots, with the Tagged Location column filled in. Cost cap: maxCountLocation. |
| 6: Post Engagement Discovery | Post / reel links | The accounts that liked or commented on those posts, ranked by how much they engaged. Instagram serves likers as one capped sample, so a viral post returns part of its liker list; comments are read in full. Cost cap: maxCountEngagers. |
Mode-specific columns (added per mode): Source (all modes — which mode + seed produced the row), Mutual Follow (Mode 1 ≥ 2 targets — warm-lead signal), Tagged Location (Mode 5 — Instagram place name), Liked Posts / Comments Left / Posts Engaged (Mode 6 — how much this account engaged), Matched By (any mode with keyword + category filters — which filter let the row through under OR).
For every profile that comes back you get:
- Identity — Account URL, Full Name, Profile Picture, Verified flag.
- Audience — Followers Count, Following Count, Posts Count, Mutual Follow flag.
- Engagement — Median ER (%), Avg Likes, Avg Comments, Posts-per-Month, Quality (Excellent / Good / Average / Poor).
- Reels analytics — Reels Count, Last Reel (Days Ago), Median Views, Views/Followers Ratio (%).
- Bio + classification — Biography, Detected Language, Category (1090+ Instagram business categories).
- Optional contacts + posts — Email, Phone, External URL, Address (parsed from bio + optionally from recent posts), plus captions of the 8 most-recent posts. When a profile shows no email or phone, the page its bio link opens (a Linktree-style page or the account's own website) is checked too; Email Source and Phone Source say where each contact came from.
- Optional account origin (paid add-on) — from Instagram's «About this account» panel: Account Country (where the account is based), Account Created (the month it joined,
YYYY-MM) and Username Changes (how many times the username changed — a count, Instagram does not show the old names). Turn onincludeAboutSection; see the pricing table for when it starts.
Then the actor applies your filters (follower band, ER, language, category, keyword in bio/name/posts, excluded keywords, location keyword, contact-info presence, recency, verified-only, has-website, plus an AND/OR combinator with a Matched By column) so only profiles matching your target persona end up in the dataset.
📦 Output sample
Every analyzed profile is pushed to the dataset as one item — one row per profile, regardless of mode. Here's a real row from a recent run (Profile Picture URL and Post captions truncated for readability):
{"Account": "https://instagram.com/chef.maria.recipes","Source": "Network Expansion of foodnetwork","Matched By": "category","Full Name": "Maria Rodriguez — Food Creator","Profile Picture": "https://scontent.cdninstagram.com/v/t51.../profile.jpg","Followers Count": 45300,"Following Count": 1200,"Total Posts": 487,"Posts per Month": 18,"Last Post Within (Days)": 1,"Biography": "🍳 Chef & Recipe Developer | 📧 collabs@chefmaria.com | 📍 Los Angeles","Detected Language": "English","Category": "Chef","Email": "collabs@chefmaria.com","Email Source": "bio","Email from Posts": "N/A","Phone": "N/A","Phone Source": "N/A","Phone from Posts": "N/A","External URL": "https://example.com/links","Address": "Los Angeles, CA","Avg Likes": 1108,"Avg Comments": 32,"Median ER": "2.45%","Quality": "Good","Reels Count": 9,"Last Reel (Days Ago)": 3,"Median Views": 18500,"Views/Followers Ratio": "40.85%","Post 1": "30-minute weeknight pasta — recipe in stories! Tested this twice...","Post 2": "Sourdough breakthrough: room temp + 20% rye for that perfect crumb...","Post 3": "Knife skills 101 — the 3 cuts every home cook should master...","Post 4": "Pantry challenge week 4: $40 grocery bill, 9 dinners. Spreadsheet in bio...","Post 5": "Fermentation Friday — kimchi day 7 update. Spice level: dialed up...","Post 6": "Behind the scenes: shooting the cookbook cover at 5am with natural light...","Post 7": "Reader question answered: gluten-free swaps that actually work in baking...","Post 8": "Weekend brunch board — 12 components, all made in under an hour...","Analysis Status": "analyzed","Why Not Analyzed": "N/A"}
Missing optional values come back as the string "N/A" rather than null, so every key is always present and downstream code (Sheets, Airtable, CSV) can rely on a stable shape. The full per-column schema is in .actor/dataset_schema.json.
The results also tell you what was left behind. Any row whose Analysis Status is not_analyzed is not a profile — it is the run telling you about something it did not deliver, with the reason in plain words in Why Not Analyzed. None of these rows receives a profile charge. An early measured-yield notice can appear among the first 20 items; final diagnoses and next-action links follow the results. Filter on Analysis Status === "analyzed" when counting profiles.
- A post link that could not be read (Mode 6, every plan) — one row per link you pasted that the run could not open, naming the link in
Sourceand saying which of three things happened: it was never a post link, so nothing was looked up; it is a real post that is deleted or inside a private account; or it hit a temporary error. Links past the 50-per-run ceiling are reported the same way. No link you paste disappears without a word. - Free plan, Modes 1 and 2 — one row for each account you submitted that the free plan did not reach, so you can see exactly which of your names were skipped rather than guessing.
- Free plan, Modes 3–6 — one row per starting profile, query, location or post that still had candidates when the free plan stopped the search, with how many more there were.
The free-limit and unread-link notices are capped at 25 rows per run, with the true total in the sentence. The free-plan rows never appear on a paid run; the unread-link rows appear on any plan, because a link you pasted and did not get back is a fact about your run, not about your plan.
Run diagnoses and continuing your work
A run that saves some profiles but cannot read others now reports the exact counts in its final status,
USER_MESSAGE, RUN_SUMMARY.partialProfileResults, and a non-profile dataset notice. Open PARTIAL_RESULTS
for every unread username and its reason. Not-found and private accounts are separate from temporary errors
and errors whose duration is unknown. RETRY_INPUT, when available, contains only the unread error targets;
review its budget and start it explicitly. New successful retrievals can be charged. It never repeats the
profiles already delivered by this run.
When filters keep fewer than 20% of at least 100 profiles checked so far, an early notice is written while
it can still appear in the first 20 dataset items. A final note gives the completed count and supersedes an
interim low yield that later recovered. If low yield develops only after 20 results are already delivered,
the append-only dataset cannot put that later finding into the first page: read the final diagnosis or
USER_MESSAGE too. These notices do not stop processing or change pricing.
After a useful export, open Next Actions in Output or read the run's RUN_ACTIONS record. Download one
of its NEXT_INPUT_* records, review the settings and visible maxBudgetUsd, then explicitly start a new run:
- Refresh retrieves fresh public data for exactly the delivered list. It does not reuse old cached data as if it were current. Fresh successful extractions, including filter rejects, use current actor pricing.
- Find new keeps a discovery run's source and filters and excludes its already delivered accounts before selecting candidates. The source may have no new profiles. Existing exclusions are preserved.
- Re-filter uses only the selected run's private snapshot, including its filter rejects, when available. Data retains its original age; missing fields cannot be fetched offline. There are no new profile extraction charges; Apify compute/storage can still apply.
The machine-readable input keys are repeatSourceRunId and repeatAction (refresh, find_new, refilter).
Online actions require a positive maxBudgetUsd; the downloaded template keeps a previously explicit
budget or suggests $2 for review. Run ID, owner, actor and storage identities are verified against the actor’s finalized export registry. No previous-run metadata access or broader API token is needed. Re-filtering reads only the snapshot prefix sealed for that export. An unavailable
or expired source produces an explanation, never a substitution with another run's cache. Parallel runs
keep separate snapshots. Source inputs cannot silently change during discovery/refilter; filters may change.
No action creates a schedule or starts another run automatically.
When Start Profiles contains a display name, it stays intact: NAME_SEARCH_INPUT prepares an explicit
keyword search, while RESUME_INPUT_TEMPLATE preserves the original mode and filters. Search results are
candidates, not identity matches. Choose the right username yourself, put it into the template, and run it.
If none fits, copy the profile link from Instagram. Bare names such as Alex Hormozi are never split into
unrelated usernames; multiple handles should use separate rows, commas, or explicit @handles.
⚡ Quick start
- Choose Mode in the operation-mode dropdown — Mode 3 (Network Expansion) is the easiest first run. All 6 mode sections stay visible in the form, and the run reads only the section for the mode you picked — so fill that one and ignore the rest.
- Add seeds / targets — 1–5 Instagram usernames in your niche, or a hashtag / search query /
City, Country/ a post link. - Set the mode's cap + filters —
maxCount*for budget, plus followers band / language / contact-info. - Run — results stream into the dataset; download as CSV / JSON / Excel.
Minimal Mode 2 input (enrich a known list, no discovery):
{"operationMode": "analyzeSpecificAccounts","specificUsernamesList": ["openai", "anthropic", "deepmind"],"maxCountList": 5}
🧭 When to use it
- Lead generation — find businesses or creators in a niche by category, follower band, and contact-info presence. Point it at a niche and it works as an Instagram email scraper: you get back the addresses those accounts publish on their own profiles or on the page their bio link opens, never anything hidden.
- Influencer marketing — influencer discovery at scale: surface ER + Quality + Reels-views/followers ratio across thousands of micro-influencers in one sweep.
- Competitor research — audit a competitor's followers + following + commenters for partnership-ready accounts.
- Niche audit — discover sub-niches (e.g. narrowing "wellness" → "recipe creators") via bio-keyword whitelists.
- Audience expansion — start from 1–5 seed handles and expand via Instagram's similar-graph; pair with location for geo + thematic targeting.
- Campaign audience reports — turn a post or reel into the list of accounts that actually liked or commented on it, enriched with contact details for follow-up.
🎯 Example tasks
Ready-to-run examples — each opens a pre-configured task you can run in one click (no setup):
- Find fitness coaches on Instagram — Search Instagram for fitness coaches and get a filtered list with follower count, engagement rate, and the contact details on each profile.
- Find real estate agents on Instagram — Search Instagram for real estate agents in your market and get follower count, engagement rate, and contact details for every matching account.
- Find wedding photographers on Instagram — Search Instagram for wedding photographers and get a clean list with follower count, engagement rate, and the contact details on each profile.
- Find Instagram accounts by hashtag — Give any hashtag and get the accounts posting under it, each with follower count, engagement rate, and the contact details on the profile.
- Analyze any Instagram profile's stats & engagement — Pull followers, engagement rate, business category, contact info, and recent-post metrics for any list of public Instagram profiles in one run.
- Find similar Instagram accounts & competitors — Discover accounts similar to any seed profile using Instagram's own suggestions, enriched with followers, category, and contact info for each match.
- Export & analyze a competitor's Instagram followers — Extract a public account's followers and enrich each with engagement rate, category, language, and contact info — a ready-to-use audience list.
- Scrape business emails from Instagram profiles — Collect public emails, phone numbers, and websites from Instagram business profiles to build outreach and lead lists — no manual copy-paste.
- Find Instagram influencers by niche or keyword — Search Instagram by keyword or hashtag and get a filtered list of creators in your niche, each enriched with followers, engagement, and contact info.
- Find the accounts that liked an Instagram post — Paste any Instagram post or reel link and get the accounts that liked it, each enriched with follower count, engagement rate, category, and contact details.
- Find the accounts that commented on an Instagram post — Paste any Instagram post or reel link and get the accounts that commented on it — comments are read page by page, not sampled — enriched with contact details.
- Analyze who engaged with an Instagram campaign post — Turn a campaign post into an audience report: the accounts that liked or commented, merged and deduplicated, with emails, followers, and engagement rate.
🛠 Input
Inputs are grouped into 4 sections in the Apify Console UI; the in-band help text on each field explains the consequence of each setting.
| Section | What it controls |
|---|---|
| 🎯 Mode 1–6 inputs | Targets / lists / seeds / queries / location seeds / post links + per-mode cost cap (maxCount / maxCountList / …) |
| ⚙️ Data Extraction | Email / phone / website / category / address / quality / posts / contacts-from-posts / account country & creation date (paid add-on) |
| 🔬 Advanced Filtering | Follower band, ER, language, category, keyword + excluded keywords + location, contact-info, recency, Reels ratio, account type |
| ▶️ Run & Storage | clearSavedData (resume vs fresh) |
Highlight fields:
operationMode— required; selects which mode runs. Only that mode's input section is read — values typed into another mode's fields are ignored, which is the single most common cause of a zero-row run.maxCount<Mode>— hard cost ceiling for the mode (each profile = $0.01).analyzeQuality— set true for Median ER + Quality + Avg Likes/Comments + Posts-per-Month columns; makes each profile take a little longer.categoryFilter— restrict to one of 1090+ Instagram business categories. MCP clients see only the first ~10 enum entries due to truncation; programmatic callers can pass any exact value.filterCombination—AND(default) requires both keyword AND category;ORrescues niches where most accounts don't set a category but mention the keyword in bio.excludeKeywords— skip profiles whose bio/name/posts (same place askeywordLocation) contain any of these words, e.g.keywords: ["constructor"]+excludeKeywords: ["SEO", "marketing agency"]. Whole words, not case-sensitive (seodoes not dropSeoul).excludeAccounts— usernames to skip before they are fetched or charged (your own list).skipDelivered— Skip accounts already delivered to you (Modes 3–6):"off"(default),"7","30","90","180","365"or"forever"; from the API any number of days as a string. See Skip accounts you already have.clearSavedData— leave true for fresh runs; uncheck to resume from KVS checkpoint.
💰 How much will scraping Instagram cost?
You're billed per profile, not per API call. Charges only on successful extractions; failed requests don't count.
| Event | Price | Trigger |
|---|---|---|
| Profile Analyzed | $0.01 | Each profile retrieved (live or from cache) and analyzed, regardless of whether it passed filters |
| User List Fetched | $0.10 | Full followers/following list pulled for one target account (Mode 1) |
| Account country & age lookup | $0.005 | Only with includeAboutSection on, from 12 November 2026: each delivered profile whose «About this account» panel returned its country or creation month |
The account-country add-on is off by default and starts on 12 November 2026. Until then the option is ignored — no extra columns and nothing extra charged — and the run log says so. From that date a run with the option on is charged $0.005 for each delivered profile whose panel answered, on top of the profile price. Profiles your filters reject are not looked up, and a panel that is empty or does not answer is not charged (its three columns read N/A). With 1 000 delivered profiles the add-on costs $5.00 at most.
Math at a glance:
| Volume | Mode | Cost |
|---|---|---|
| 50 profiles | any | $0.50 |
| 500 profiles | Mode 3 network expansion | $5.00 |
| 1 000 followers of 1 target | Mode 1 (1 list + 1 000 profiles) | $10.10 |
| 5 000 profiles | any | $50.00 |
What filters do to your bill
Filters run after each profile is retrieved, because deciding whether a profile matches requires reading it first. So filters narrow your results, not your bill — a run with strict filters can retrieve 40 profiles, keep 3, and charge for all 40.
Four things follow from that, and all four work in your favour:
- Your first run is free of filter mistakes. On your very first run with this Actor, profiles rejected by your filters are not charged until the run finds its first match (up to 100 of them). Set the filters you want and find out what they really do, without paying to learn it. Once a profile matches, your filters are working and the rest of the run bills normally.
- Every profile you pay for is saved — including the rejected ones. Nothing you paid to retrieve is thrown away.
- Re-filtering is free. Turn on Enable Offline Mode and run again: the Actor re-applies your new filter values to the profiles it already retrieved, makes no requests, and charges nothing. If a run comes back empty, this is the cheap way to fix it — and on the free plan it does not use up any of the monthly allowance.
- You can tell a run to stop when your filters reject almost everything. Set Stop below match rate (%) (
minMatchRatePercent): after the first 100 profiles analyzed, the run stops if fewer than that percent passed your filters. The rejects it already analyzed are charged; the candidates it did not get to are not. Off by default.
Unreadable accounts are never billed at all: not-found, private, and errored retrievals release their charge.
Skip accounts you already have
Running several searches in one niche — neighbouring seeds in Network Expansion, related keywords, nearby places —
brings back many of the same accounts. Set Skip accounts already delivered to you (skipDelivered) and the
discovery modes (3–6) leave out every account this actor already delivered to your Apify account in an earlier run,
whatever the seeds or search were:
- Skipped accounts are not fetched and not charged, and they do not count toward the run's Max profiles — the run takes the next candidates instead. In Network Expansion a skipped account still leads to its own similar accounts.
- Choose how far back: the last 7, 30, 90 or 180 days, the last year, or Ever delivered (never repeat). From the
API, pass any number of days as a string, e.g.
"45". - The history is a key-value store named
ig-delivered-…in your own Storage. Every run records what it delivered, with the option on or off, from version 0.1.218. Delete the store to start over. - It is off by default. Mode 1 (followers of an account you name) and Mode 2 (your own list) always analyze every account you asked for; so does an Offline Mode re-filter.
- Runs that overlap in time may still share an account: each reads the history when it starts.
- If every account a search reaches was already delivered, the run ends with nothing new and nothing charged, and
USER_MESSAGEsays so. If the history cannot be read while the option is on, the run stops before any work at $0 — start it again, or set the option to Off.
excludeAccounts still works beside it, for names you collected elsewhere.
🆓 Free vs paid plan
| Capability | Free plan | Paid plan |
|---|---|---|
| Profiles per run (Mode 1/2) | up to 50 | unlimited (cap via maxCount*) |
| Profiles per run (Mode 3/4/5/6) | up to 5 matching profiles — with a filter on, the run checks up to 20 rejected profiles beside them | unlimited |
| Profiles analyzed per month | up to 100 (resets on the 1st) | unlimited |
| Runs per month | up to 15 (resets on the 1st) | unlimited |
| Median ER + Quality + Avg Likes/Comments | ✅ | ✅ |
| Reels analytics (Median Views, Views/Followers Ratio, Last Reel) | ✅ | ✅ |
| All filters applied (engagement, Reels, posts-in-period, …) | ✅ | ✅ |
| Resume interrupted runs | ✅ | ✅ |
The free plan returns the same data quality as paid — every column and every filter works; the difference is volume (profiles per run, plus 100 analyzed profiles and 15 runs per month — whichever comes first). A profile counts when it is analyzed, including one your filters then leave out, because that is what a run is charged for. When a free run hits a ceiling, the run's USER_MESSAGE record says exactly what was held back (e.g., how large your targets' lists really are, or that discovery stopped at the cap). Free plan includes a $5 monthly platform credit; demo/test runs and Offline Mode re-filters don't count against the monthly allowance.
Free runs that start at the same time are counted exactly. A free run you stop (abort) or that times out counts as one of the 15, like a finished run; a run that fails, or stops on an input it cannot use, does not. If the run count cannot be checked, the free run stops before any work, charges nothing and says so — run it again in a minute.
While an unusually large number of new free accounts run this Actor at once, free runs from new accounts may be reduced to one profile for a short while — one target account, list, search term, location or post, without the account-country columns. The run says so in its message and still counts as one of the 15. Paid plans are never affected.
🧪 Filter recipes
Drop these straight into the input editor. Each one is sized for the maxCount* shown — every filter you stack cuts the share of candidates that survive, so if you lower the cap, drop a filter or two with it. A five-filter recipe over 2 000 candidates is comfortable; the same five over 10 candidates usually returns nothing.
Food / Restaurant micro-influencers (Mode 3):
{"operationMode": "networkExpansion","startUsernames": ["foodnetwork", "tasty", "gordonramsay"],"maxCountExpansion": 500,"minFollowers": 10000, "maxFollowers": 250000,"minEngagementRate": 2, "lastPostDays": 30,"extractEmail": true, "analyzeQuality": true}
Active English-speaking fitness creators (Mode 4):
{"operationMode": "keywordDiscovery","searchQueries": ["fitness coach", "personal trainer"],"searchHashtags": ["fitnessmotivation", "personaltrainer"],"maxCountDiscovery": 200,"profileLanguage": "English", "minFollowers": 10000,"filterCombination": "OR", "extractEmail": true}
Post-campaign audience report (Mode 6):
{"operationMode": "postEngagementDiscovery","postUrls": ["https://www.instagram.com/p/CA2aJYrg6cZ/","https://www.instagram.com/reel/CB3bKZsh7dY/"],"engagementType": "both","maxCountEngagers": 300,"minFollowers": 1000,"extractEmail": true}
Sort the result by Posts Engaged to put the people who engaged with more than one of your posts at the top — they are the warmest names in the list. Use "engagementType": "commenters" when you need a set you can verify is complete.
B2B partnership shortlist (Mode 1, mutual-follow surfaced automatically):
{"operationMode": "analyzeFollowersFollowing","targetUsernames": ["competitor1", "competitor2", "competitor3"],"analyzeFollowers": true, "maxCount": 2000,"minFollowers": 1000, "maxFollowers": 100000,"accountType": "business", "hasWebsite": true,"contactInfoType": "emailOrPhone"}
Local businesses in a metro area (Mode 4 + the Business-Address filter, sized right):
{"operationMode": "keywordDiscovery","searchQueries": ["med spa", "aesthetic clinic", "dental clinic"],"maxCountDiscovery": 300,"locationKeywords": "Miami, Fort Lauderdale, Boca Raton, Coral Gables, Hallandale Beach, Florida","accountType": "business","extractPhysicalAddress": true, "extractPhoneNumber": true}
This recipe is sized for the paid plan (the free plan stops at 5 matching profiles per run). extractPhysicalAddress adds the Address column so you can see exactly what each row matched — the filter itself works without it.
The Business-Address filter reads the address a business published about itself — not a place you search. Write it the way Instagram writes it: "Miami, Florida" in the US, Canada, Mexico and India (city, then state), "Berlin, Germany" everywhere else (city, then country in English). Six rules from our own run history:
- Separate terms with a comma. A space-separated list is read as one long term — of 21 runs that tried, one delivered.
- Give it a list, not a word. Name the city, the towns around it, and the region. Customers who wrote lists of six or more terms got rows 38% of the time; customers who never went past five, 11%. Terms are OR-ed — an extra term can only help, never hurt.
- Add the region written out. "Florida", "Ontario", "Germany", "Nigeria" — Instagram spells these in full; the field carries "United Arab Emirates", not "UAE", and a list of nothing but country abbreviations has not returned a row yet.
- Copy Instagram's spelling, accents included. "São Paulo", not "Sao Paulo".
- Search for business types, not people. "dental clinic" surfaces profiles with addresses; "fitness coach" surfaces creators, who have none.
- Neighborhoods work too. The street line is searched as well, so "Marina", "Lagos Island" or "Borivali West" can match where a city name is too broad.
- Don't pair it with Mode 5. Location pages are tagged by creators, and creators have no business address — the two cancel out. Not one customer run that combined them has returned a row.
🔁 Resume / checkpoint
Long Mode 1 runs (10K+ followers) sometimes get interrupted by network blips or platform restarts. Click Resurrect in the Apify Console to continue.
The actor detects the resume from the run's resurrectCount AND on-disk checkpoint-* records, and skips the wipe regardless of network conditions. The run continues from the exact profile it stopped at — no re-fetching, no double-charging. To start fresh anyway, leave clearSavedData checked (default).
📡 Live status & storage records
The actor exposes a live progress page (Apify Console → Live View) and writes named records to the run's Key-Value Store:
| Record | Purpose |
|---|---|
RUN_SUMMARY | End-of-run stats (status, profiles found/analyzed, cost, free-tier limits, and apiUsage). Fetch via Actor.getValue('RUN_SUMMARY'). |
API_USAGE | Request count for this run, split into data fetches vs searches ({ totalRequests, byCategory: { fetch, search } }). Lets tools integrating this actor see how much work each run performed. Fetch via Actor.getValue('API_USAGE'). |
USER_MESSAGE | Onboarding tip on a paid user's 1st and 3rd paid run; absent on free runs and test runs. |
FREE_LIMITS_APPLIED | Stable id codes for free-plan caps applied this run. Empty/omitted on paid runs. |
status.html | Auto-refreshing HTML view of current run progress; also exposed as a live URL while the run is active. |
SKIPPED_ACCOUNTS | Per-account list of usernames skipped during the run, with reason and category (filter / not_found / private / list_hidden / error). list_hidden is a public account whose owner has hidden its followers or following list. |
checkpoint-* | Resume state for interrupted runs (internal — safe to ignore). |
🤖 Programmatic / API use
Your API token is in Apify Console → Settings → API & Integrations. Pick the mode with operationMode, then give that mode's own fields.
Python (pip install apify-client):
from apify_client import ApifyClientclient = ApifyClient("<APIFY_TOKEN>")run = client.actor("afanasenko/instagram-profile-scraper").call(run_input={"operationMode": "keywordDiscovery","searchQueries": ["vegan chef"],"maxCountDiscovery": 100,"minFollowers": 5000,"contactInfoType": "emailOnly",})for row in client.dataset(run["defaultDatasetId"]).iterate_items():if row.get("Analysis Status") == "analyzed":print(row["Account"], row["Followers Count"], row["Email"])
JavaScript / Node.js (npm i apify-client):
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<APIFY_TOKEN>' });const run = await client.actor('afanasenko/instagram-profile-scraper').call({operationMode: 'analyzeSpecificAccounts',specificUsernamesList: ['nike', 'adidas'],maxCountList: 2,});const { items } = await client.dataset(run.defaultDatasetId).listItems();const profiles = items.filter((row) => row['Analysis Status'] === 'analyzed');
Each mode's own fields:
operationMode | Source | Cap |
|---|---|---|
analyzeFollowersFollowing | targetUsernames, analyzeFollowers / analyzeFollowing | maxCount |
analyzeSpecificAccounts | specificUsernamesList | maxCountList |
networkExpansion | startUsernames, searchDepth | maxCountExpansion |
keywordDiscovery | searchQueries, searchHashtags | maxCountDiscovery |
locationDiscovery | locationSeeds | maxCountLocation |
postEngagementDiscovery | postUrls, engagementType | maxCountEngagers |
Filters (minFollowers, contactInfoType, profileLanguage, …) work the same in every mode. Rows with "Analysis Status": "not_analyzed" are notices, not profiles. A run you start in Console or through an AI agent ends its log with this same call, filled in with that run's own input — copy it to repeat the search from code.
REST — start a run:
curl -X POST "https://api.apify.com/v2/acts/afanasenko~instagram-profile-scraper/runs?token=<APIFY_TOKEN>" \-H 'Content-Type: application/json' \-d '{"operationMode":"analyzeSpecificAccounts","specificUsernamesList":["openai"],"maxCountList":1}'
Then poll GET /v2/actor-runs/{id} until status is SUCCEEDED, and read rows from GET /v2/datasets/{defaultDatasetId}/items?clean=1.
Run-level stats live in the RUN_SUMMARY record of the same run:
$curl "https://api.apify.com/v2/key-value-stores/<defaultKeyValueStoreId>/records/RUN_SUMMARY?token=<APIFY_TOKEN>"
MCP — connect Claude / your AI agent to https://mcp.apify.com and ask: "Find me 200 Spanish-speaking veterinarians on Instagram with ≥ 50K followers and email contact info." The agent picks this actor, fills the input, and streams results back.
❓ FAQ
How much does it cost to scrape Instagram?
$0.01 per profile analyzed (regardless of filter pass/fail) + $0.10 per followers/following list pulled in Mode 1. The optional account-country add-on (includeAboutSection) adds $0.005 per delivered profile from 12 November 2026. Free plan includes a $5 monthly credit, caps Modes 1/2 at 50 profiles and Modes 3/4/5/6 at 5 matching profiles per run (with up to 20 filter rejects beside them, charged like any analyzed profile), and allows 100 analyzed profiles and 15 runs per month, whichever comes first.
I search the same niche again and again. Do I pay again for accounts I already got?
Not if you set Skip accounts already delivered to you (skipDelivered) in Modes 3–6: accounts this actor already delivered to your account are skipped before they are fetched and are not charged, and the run takes new candidates instead. Choose a period (7 days … a year) or Ever delivered. Runs that overlap in time may still share an account. See Skip accounts you already have.
My run returned 0 rows and my filters rejected everything. Do I have to pay again to fix it?
No. The profiles are already saved, including the rejected ones. Open the run's Input tab, turn on Enable Offline Mode, loosen the filter the run's USER_MESSAGE record names, and start it again — re-filtering makes no requests and charges nothing. If that run was your first with this Actor, you were not charged for the rejected profiles in the first place.
Is it legal to scrape Instagram profiles, and do I need an Instagram account? Yes, it's legal — this actor reads only publicly available data (public profiles, public bios, public post captions, public business contact fields). No Instagram account or login required. Compliance with Instagram's ToS, GDPR, CCPA, and CAN-SPAM is the operator's responsibility.
How is engagement rate calculated?
Average of likes + comments on the most recent ~12 posts, divided by follower count, formatted as "X.XX%". Set analyzeQuality: true to populate Median ER + Quality.
Why "N/A" instead of null for missing values?
Consistency — every always-emitted column key is present on every row, so Google Sheets / Airtable / CSV consumers don't need null-checks. Conditional columns (e.g. Email when extractEmail: false) are omitted entirely.
How fast is it, and what if a target account is private or doesn't exist?
Speed depends on the mode and on how much enrichment you switch on, because every profile takes a few seconds to read. A typical run lands around 75 profiles/min. Mode 2 (a plain list of usernames) is fastest at roughly 110/min; the discovery modes are slower because every candidate has to be found before it can be read — Mode 1 around 50/min, Modes 4 and 5 around 40/min, and Mode 3 around 25–30/min. Each run prints its own estimate in the log within the first few seconds. Private or non-existent accounts are skipped and logged in SKIPPED_ACCOUNTS KVS, with no charge. So is a public account whose owner has hidden its follower list: the run names it and says that running again won't change that.
Can I get the country an Instagram account is based in, or when it was created?
Yes — turn on includeAboutSection (Add account country & creation date). The run reads Instagram's «About this account» panel for every profile it delivers and adds three columns: Account Country, Account Created (YYYY-MM) and Username Changes (a count; Instagram does not show the former names). It is a separate paid event, $0.005 per profile whose panel answered, starting 12 November 2026 — before that date the option is ignored and nothing extra is charged. «Not shared» in Account Country means the owner hid it. It works in all six modes, but not with Enable Offline Mode, because the panel has to be read live.
Can I resume an interrupted run? Yes — click Resurrect in the Apify Console. The actor reuses the on-disk checkpoint records and continues from the exact profile it stopped at, with no re-fetching and no double-charging.
Do I need to log in or provide session cookies? No. The actor never asks for your Instagram login, password, or session cookies — it reads public data only. There is no account of yours involved, so there is nothing to get flagged or banned.
Can I analyze thousands of profiles in bulk?
Yes — Mode 2 takes a pasted list of any size and enriches every profile in one run (paid plans; the free plan analyzes up to 50 per run). Set maxCountList as a hard cost ceiling, and use Resurrect to continue an interrupted batch from where it stopped.
I selected one mode but the run returned zero rows — why?
Almost always because the input went into a different mode's section. All 6 mode sections stay visible in the Apify Console form, and the run reads only the section belonging to the mode in the operationMode dropdown. Pick Mode 6 but paste your handles into Mode 2's list, and Mode 6 runs with no post links and returns nothing. Check that the section labelled with your selected mode is the one you filled; the run's USER_MESSAGE record names the field it expected.
Why did a post with 90 000 likes return only ~1 500 likers?
Instagram serves a post's likers as a single ranked sample and provides no way to page past it, so on a high-engagement post you get part of the list, not all of it. Every row you do get is real — a capped sample leaves accounts out, it never invents them. The run writes an ENGAGEMENT_COVERAGE record stating, per post, how many likers came back against the post's like count. Comments behave differently: they are read page by page, so setting Engagement Type to Commenters only gives you a set you can verify is complete. Private accounts are also skipped before analysis — Instagram publishes no profile data for them, so they are never fetched and never billed.
Can I run it on a schedule? Yes — create an Apify Schedule on this actor (Console → Schedules) to refresh a niche list daily or weekly. Schedules work on the free plan too; each scheduled run bills and counts like a normal run.
How do I get more results per run?
Three levers, in order: (1) make sure you filled the input section of the mode you selected — each mode reads only its own fields; (2) loosen filters — they run after profiles are fetched, so tight combinations reject most of the harvest (the run's USER_MESSAGE record names the exact filters that rejected the most); (3) in Mode 4, raise maxSearchPagesPerQuery and add broader terms. On the free plan, runs stop at 50 profiles (Modes 1–2) or 5 matching profiles (Modes 3–6) — a paid plan lifts those ceilings. To keep a tight filter from spending a paid budget on rejects, set minMatchRatePercent; the run's status line names the filter that rejected the most.
🔗 Other Instagram scrapers (same author)
| You start with… | You want to find… | Use this actor |
|---|---|---|
| Handles / post links / locations / hashtags / keywords | Profiles via 6 discovery modes in one tool | This actor — 6-mode all-in-one |
| One known account | The full list of its followers and/or following | Instagram Followers & Following Extractor |
| One known account | Track who follows / unfollows it over time (scheduled change tracking) | Instagram Follower Tracker |
| 1–5 seed handles | Similar profiles via «Suggested for You» | Instagram Related Profiles Scraper |
| A reel link or a creator handle | What their reels say and show — spoken transcript, on-screen text, hook | Instagram Reel Script Extractor |
| A post or reel link | The accounts that liked or commented on it, enriched and filtered | Instagram Likes Scraper |
| A post or reel link | What the comments say — every comment and reply as a row, @mentions split out | Instagram Comments Scraper |
| A keyword or a hashtag | The accounts in that niche, enriched and filtered | Instagram Influencer Search |
| An AI agent (Claude, Cursor, ChatGPT) | The same Instagram data from inside your agent — MCP tools it picks by intent | Instagram Profile MCP Server |
| TikTok creators instead of Instagram | The same discover-and-enrich workflow on TikTok — followers, engagement, emails | TikTok Profile Scraper |
🛟 Support & feedback
Use the Issues tab on this actor's Apify Console page to report a bug or request a feature: https://console.apify.com/actors/r4hZOdD5FiHYo1bYa/issues. Please include Run ID, input configuration (redact sensitive values), expected vs. actual behaviour, and any error messages from the run log.
Store page: https://apify.com/afanasenko/instagram-profile-scraper.
⚖️ Is it legal to scrape Instagram?
Yes — the actor scrapes only publicly available Instagram data (public profiles, public bios, public post captions, public business contact fields). It does not log in to Instagram, access private accounts, read DMs, or bypass any access controls.
Compliance with Instagram's Terms of Service, GDPR, CCPA, CAN-SPAM and any other applicable laws is the operator's responsibility. Data is volatile — Instagram schemas, categories, and pricing change without notice; verify critical fields against the live profile before acting on them. No legal advice provided.