# Instagram Profile Scraper

> Turn a list of Instagram usernames into each profile's followers, bio, emails and phone numbers in the bio, website and social links, post and video counts, engagement rate and 6 latest posts.

Instagram Profile Scraper turns a list of usernames or profile links into one row per profile: followers, following, post and video counts, bio, bio links, email in bio, engagement rate and the 6 latest posts with their likes and comments. Also get similar profiles adds the accounts Instagram suggests as similar. It needs no Instagram account.

- Web page: https://keklik.io/scrapers/instagram-profile-scraper
- Apify Actor: `keklik/instagram-profile-scraper` (https://apify.com/keklik/instagram-profile-scraper)
- You enter: Usernames, @handles or profile links
- You get: Followers, bio, bio links, email in bio, engagement rate and latest posts
- Status: working, last tested 12 Nov 2026
- Needs: an Apify account (free is enough to try). No login to the site itself.

## Price per 1,000 profiles

US dollars, on each Apify plan. Apify's platform costs are included. Add-ons are charged only when switched on in the input.

| Charge | Free | Starter | Scale | Business |
|---|---:|---:|---:|---:|
| Profile: Each profile the scraper saves, including the similar profiles it finds. Profiles that fail are free. | $1.00 | $0.90 | $0.80 | $0.70 |
| Post (add-on) | $1.00 | $0.90 | $0.80 | $0.70 |
| Reel (add-on) | $1.00 | $0.90 | $0.80 | $0.70 |
| Retried blocked profile (add-on) | $1.50 | $1.50 | $1.50 | $1.50 |

Apify's plans and a calculator: https://keklik.io/pricing.md

## Input

The run input is a JSON object with these keys:

- `usernames` (array, required): 👤 Profiles. One per line: a profile link, @nasa or nasa. Post, reel and hashtag links are skipped.
- `discoveryMode` (boolean, optional, default false): 👥 Also get similar profiles. Also scrapes the profiles Instagram suggests as similar to each profile in your list. Each one names the profile it came from, and each one is charged like any other profile.
- `maxResults` (integer, optional): 🛑 Max profiles in total. Stops the run after this many profiles, yours and similar ones together. Leave it empty for no limit. Set it when Also get similar profiles is on, to cap what a run can cost.
- `maxPostsPerProfile` (integer, optional): 🔢 Max posts and reels per profile. Get up to this many posts, and this many reels, for each profile. Leave it empty to get all of them. Not counted in Max profiles in total.
- `postedAfter` (string, optional): 📅 Posted after. Pick a date, or a period back from now such as "7 days" or "3 months", to get only posts and reels published after it. Leave it empty for any date. Through the API, 24h, 7d and 2w work too.
- `postedBefore` (string, optional): 📅 Posted before. Pick a date, or a period back from now such as "1 year", to get only posts and reels published before it. Older dates take longer to reach on big profiles; the posts passed over on the way are not charged.
- `retryBlockedProfiles` (boolean, optional, default false): 🔁 Retry blocked profiles ($). Sometimes Instagram won't show a profile to us. Turn this on and we try those profiles again through a home internet connection, which Instagram almost always lets through. You pay an extra $1.50 per 1,000 for only the profiles that needed it; all other profiles cost the normal price. Leave it off and blocked profiles are skipped and free.

Example input:

```json
{
  "usernames": [
    "gymshark",
    "glossier",
    "allbirds",
    "fentybeauty",
    "huel"
  ],
  "maxResults": 50
}
```

## Output: one row per profile

52 fields in 8 groups.

### Profile

- `username`: Username. Instagram username (handle), without the @, in the casing Instagram shows.
- `full_name`: Full name. The name shown on the profile.
- `biography`: Bio. The bio text, with its emojis and line breaks.
- `profile_pic_url`: Profile picture. Link to the profile picture. Instagram image links expire after a few days, so download the picture soon if you need it.
- `is_verified`: Verified. True when the profile has the blue verified badge.
- `is_private`: Private. True for a private profile: its counters and bio are filled, its posts are not.

### Followers and activity

- `followers_count`: Followers. Number of followers.
- `follows_count`: Following. Number of accounts the profile follows.
- `posts_count`: Posts. Number of posts on the profile. Empty when the profile turned off embedding (about 1 in 100).
- `avg_engagement`: Engagement rate. Engagement rate: the average likes plus comments of the latest posts, divided by followers. Posts whose likes the author hides are left out; empty when every latest post hides them. 0.004538 means 0.45%.
- `highlight_reel_count`: Story highlights. Number of story highlights. Empty for about 1 profile in 5, when Instagram leaves it out.
- `videos_count`: Videos. Number of videos the profile has posted. Empty for private profiles and profiles that turned off embedding.
- `has_public_story`: Story up now. True when the profile has a story up right now, a sign it is active today.
- `has_clips`: Has reels. True when the profile has posted reels.

### Contact

- `email_address`: Email. The first email address written in the bio (all of them are in emails). Found on about 1 profile in 20.
- `emails`: All emails. Every email address written in the bio or in a bio link's title, in order. Empty when there is none.
- `phone_numbers`: Phone numbers. Phone numbers written in the bio, as written, and the number behind a WhatsApp button or wa.me bio link (as +<country code><number>). A number counts when it starts with +, follows a phone word or emoji (📞, call, WhatsApp, tel) or is written like (555) 123-4567; follower counts, years and dates are left out. Found on about 1 profile in 50.
- `website`: Website. The first bio link to the account's own website: not a social network, a link page like Linktree or a short link like bit.ly. Found on about half of all profiles.
- `link_pages`: Link pages. Bio links to a page of more links: Linktree, Beacons, Stan Store, lnk.bio and the like, and music links such as lnk.to. Found on about 1 profile in 5.
- `social_links`: Social links. Bio links to other platforms, each with network (youtube, tiktok, facebook, x, threads, linkedin, spotify, whatsapp, amazon, etsy and others) and url. Can be a profile, a video or a shop.

### Bio links and hashtags

- `external_url_1`: Bio link 1. The first link in the bio: title, url, lynx_url (the same link through Instagram's redirect) and link_type.
- `external_url_2`: Bio link 2. The second link in the bio, like external_url_1.
- `external_url_3`: Bio link 3. The third link in the bio, like external_url_1.
- `all_external_urls`: All bio links. Every link in the bio, each like external_url_1.
- `bio_hashtags`: Bio hashtags. Hashtags written in the bio, without the #.
- `post_hashtags`: Post hashtags. Hashtags in the captions of the latest posts, without the # and without repeats.

### Latest 6 posts

- `latest_post_1`: Latest post 1. The profile's most recent latest post: id, shortcode, url, type (GraphImage for a photo, GraphVideo for a video, GraphSidecar for a carousel), is_video, caption, likes (empty when the author hides them), likes_hidden, comments, posted_at (Unix time in seconds), display_url (the image), location (a place name), media_count (slides in a carousel), dimensions, is_paid_partnership (marked "Paid partnership"), paid_partners (the brands it names) and coauthors (the post's other authors on a collab, each with username and is_verified). views, tagged_users, audio_info and product_type are always empty: Instagram doesn't show them to logged-out visitors. Empty when the profile has fewer posts, is private or turned off embedding.
- `latest_post_2`: Latest post 2. The profile's 2nd latest post: id, shortcode, url, type (GraphImage for a photo, GraphVideo for a video, GraphSidecar for a carousel), is_video, caption, likes (empty when the author hides them), likes_hidden, comments, posted_at (Unix time in seconds), display_url (the image), location (a place name), media_count (slides in a carousel), dimensions, is_paid_partnership (marked "Paid partnership"), paid_partners (the brands it names) and coauthors (the post's other authors on a collab, each with username and is_verified). views, tagged_users, audio_info and product_type are always empty: Instagram doesn't show them to logged-out visitors. Empty when the profile has fewer posts, is private or turned off embedding.
- `latest_post_3`: Latest post 3. The profile's 3rd latest post: id, shortcode, url, type (GraphImage for a photo, GraphVideo for a video, GraphSidecar for a carousel), is_video, caption, likes (empty when the author hides them), likes_hidden, comments, posted_at (Unix time in seconds), display_url (the image), location (a place name), media_count (slides in a carousel), dimensions, is_paid_partnership (marked "Paid partnership"), paid_partners (the brands it names) and coauthors (the post's other authors on a collab, each with username and is_verified). views, tagged_users, audio_info and product_type are always empty: Instagram doesn't show them to logged-out visitors. Empty when the profile has fewer posts, is private or turned off embedding.
- `latest_post_4`: Latest post 4. The profile's 4th latest post: id, shortcode, url, type (GraphImage for a photo, GraphVideo for a video, GraphSidecar for a carousel), is_video, caption, likes (empty when the author hides them), likes_hidden, comments, posted_at (Unix time in seconds), display_url (the image), location (a place name), media_count (slides in a carousel), dimensions, is_paid_partnership (marked "Paid partnership"), paid_partners (the brands it names) and coauthors (the post's other authors on a collab, each with username and is_verified). views, tagged_users, audio_info and product_type are always empty: Instagram doesn't show them to logged-out visitors. Empty when the profile has fewer posts, is private or turned off embedding.
- `latest_post_5`: Latest post 5. The profile's 5th latest post: id, shortcode, url, type (GraphImage for a photo, GraphVideo for a video, GraphSidecar for a carousel), is_video, caption, likes (empty when the author hides them), likes_hidden, comments, posted_at (Unix time in seconds), display_url (the image), location (a place name), media_count (slides in a carousel), dimensions, is_paid_partnership (marked "Paid partnership"), paid_partners (the brands it names) and coauthors (the post's other authors on a collab, each with username and is_verified). views, tagged_users, audio_info and product_type are always empty: Instagram doesn't show them to logged-out visitors. Empty when the profile has fewer posts, is private or turned off embedding.
- `latest_post_6`: Latest post 6. The profile's 6th latest post: id, shortcode, url, type (GraphImage for a photo, GraphVideo for a video, GraphSidecar for a carousel), is_video, caption, likes (empty when the author hides them), likes_hidden, comments, posted_at (Unix time in seconds), display_url (the image), location (a place name), media_count (slides in a carousel), dimensions, is_paid_partnership (marked "Paid partnership"), paid_partners (the brands it names) and coauthors (the post's other authors on a collab, each with username and is_verified). views, tagged_users, audio_info and product_type are always empty: Instagram doesn't show them to logged-out visitors. Empty when the profile has fewer posts, is private or turned off embedding.

### Similar profiles

- `related_profiles`: Related profiles. Profiles Instagram suggests as similar, up to about 50, each with id, username, full_name, is_verified and profile_pic_url (is_private is always empty). Big accounts usually have them; small ones often have none.
- `discovered_from`: Discovered from. With Also get similar profiles (Discovery mode) on: the profile from your list this one was suggested for. Empty for the profiles you typed.
- `hop_depth`: Discovery level. 0 for the profiles you typed, 1 for the similar profiles found for them.

### Instagram IDs and link

- `profile_url`: Profile URL. Link to the profile on Instagram.
- `profile_id`: Instagram ID. Numeric Instagram user ID. It stays the same when the username changes.
- `fbid`: Facebook ID. The account's ID on Meta's Facebook side.

### Scrape details

- `scraping_time`: Scraped at. When the row was scraped, in UTC (ISO 8601).
- `source`: Source. How the row was read. Always "logged_out": what Instagram shows visitors who are not logged in.

## How it works

1. Paste the profiles. Usernames, @handles or profile links, one per line.
2. Each profile is read. Followers, bio, emails and phone numbers in the bio, website and social links, post and video counts, engagement rate, and the 6 latest posts with their likes, comments and paid partnerships. Private profiles come without posts.
3. Find similar profiles. Switch on Also get similar profiles to add the profiles Instagram suggests as similar, up to about 50 for each. Set Max profiles in total to keep the list small.
4. Download your list. As a spreadsheet, in Google Sheets, or into your CRM or influencer tool.

## Use cases

- Vetting influencers: Followers, engagement rate, recent posts and the brands they already do paid partnerships with, for every creator on your shortlist, before you reach out.
- Lead lists: Business accounts with their website and the emails and phone numbers in their bio, ready for outreach.
- Competitor tracking: The same list run on a schedule, to follow follower growth and posting activity over time.
- Mapping a niche: Also get similar profiles adds the accounts Instagram suggests as similar to each profile, so a few seed accounts open up a whole niche.

## Good to know

Instagram shows a business account's category, contact email, phone and address only to logged-in visitors, so you get the email from the bio and the links instead: about 6 profiles in 100 have an email in their bio, and 3 in 4 have a link. Similar profiles come mostly with big accounts. Image and video links expire, so download any media soon after the run.

## Questions

### How do I keep my first run small and cheap?

Paste a handful of profiles, or set 🛑 Max profiles in total (for example 20): the run stops there, similar profiles included. From November 26, when you turn on Get posts and reels, set 🔢 Max posts and reels per profile (the form suggests 50) or 📅 Posted after, so a big account's thousands of posts don't fill the run.

### How do I download everything in one file?

From November 26: when Get posts and reels is on, the run saves one Excel file with a sheet each for Profiles, Posts and Reels, plus Failed profiles when some failed. Open the Output tab, pick Download everything (Excel) in the dropdown at the top, and download it.

You can also download each table on its own: click Export, choose Profiles, Posts or Reels under Output, pick a format (Excel, CSV, JSON and more) and click Download. The Export window starts on Profiles, so pick Posts or Reels there when you want those.

### What does Retry blocked profiles do?

From November 26: sometimes Instagram won't show a profile to us. Turn on Retry blocked profiles and we try those profiles again through a home internet connection (a residential proxy), which Instagram almost always lets through. You pay an extra $1.50 per 1,000 for only the profiles that needed it; every other profile costs the normal price. Leave it off and blocked profiles are skipped and free, and the run's last message tells you how many there were.

### How far back can I get posts?

From November 26, with Get posts and reels: as far back as the profile goes. Posts come newest first, so to reach old dates the scraper has to page past all the newer posts, about 12 a second. For a profile with a few hundred posts that takes seconds; for a profile with tens of thousands, reaching posts from years ago can take a while. Posts passed over on the way are not charged.

### Is it legal to scrape Instagram profiles?

This scraper collects only data that Instagram shows publicly to visitors who are not logged in. Profiles can still contain personal data, which laws such as the GDPR and CCPA protect. Make sure you have a lawful reason to collect and use it, and don't scrape private information. If you are unsure, ask a lawyer.

### Do I need an Instagram account?

No. You don't need an Instagram account, a login or cookies, and the scraper never uses yours.

### Can I scrape private Instagram profiles?

Private profiles are returned with their counters, bio and badges, but without posts or reels, because Instagram does not show those to visitors.

### Can I get a business account's email and phone number?

Instagram shows a business account's contact email, phone number, category and address only to logged-in users, so those fields come back empty. What you can get instead:

- Emails and phone numbers in the bio (emails, phone_numbers): what the account wrote in its bio, including the number behind a WhatsApp link. Emails are on about 6 profiles in 100, phone numbers on about 2 in 100.
- Website (website): about half of all profiles link to their own site, where contact details usually are. Feed those websites to a contact details scraper to collect more emails and phone numbers.

### Can I find more profiles like the ones I have?

Yes. Turn on 👥 Also get similar profiles and the scraper also collects the profiles Instagram suggests as similar to each profile in your list, up to about 50 each. Each one it finds is a result you pay for, so set 🛑 Max profiles in total to keep the run small. Small profiles often have no suggestions, so fewer are found around them.

### Why are some fields empty?

Every row has what Instagram shows visitors who are not logged in (its source field says logged_out). See Always-empty fields under What to know about the data. Private profiles have no posts, and profiles that turned off embedding (about 1 in 100) have no latest posts or post count.

### What happens when a run fails?

The run's status message says why, and the log names each input that failed. If a mistake in the input stops the run before anything is scraped, the run ends without results and its status message says what to change. Either way the Actor sends us a short error report, so we can fix the cause even if you never open an issue: the Actor's build, the reason, the counts from the end-of-run summary, whether your Apify plan is free or paid and, for an unexpected error, where in our code it happened. Web addresses and @usernames are blanked out first. The report never includes your input, your account, the pages you scraped or your results. Every run, failed or not, also sends one usage line as it ends, so we can see which options people use, what runs cost us and how well the data is filled: how many of each charged event it used (for example 120 posts) and the price your plan paid for them; the run's platform cost by item and its resources (time, memory, proxy traffic); whether it stopped at your maximum cost or maximum items; how it was started (Console, API, schedule, saved task, and for API runs the kind of tool, such as Make, Zapier or the Apify client, never the tool's full identity); our scraper's own retry, request and timing counts; and for each output field, how many results had it filled (counts only, never the values). That never includes your input, your account, your IP address, the pages you scraped or your results either. If you need help with a run, open an issue on the Issues tab and attach the run, or email support@kekliktech.com with the run's link; the run's log ends with both.

## Run it

- In Apify Console: https://apify.com/keklik/instagram-profile-scraper
- With the Apify API: POST https://api.apify.com/v2/acts/keklik~instagram-profile-scraper/run-sync-get-dataset-items?token=APIFY_TOKEN with the input JSON as the body. It returns the rows.
- From an AI agent: connect the Apify MCP server (https://mcp.apify.com), add the Actor `keklik/instagram-profile-scraper` and call it with the input.
