~ / guides / How to Scrape Instagram Profiles (2026)

How to Scrape Instagram Profiles (2026)

LH
Lena Hoff
Instagram data engineer · about the author
the short version
  • A public Instagram profile hands back five fields worth having without a login: biography (bio), edge_followed_by.count (followers), edge_owner_to_timeline_media.count (posts), external_url (the bio link), and is_verified. They all sit in one JSON document from the web_profile_info endpoint, not in the page HTML.
  • A plain requests.get on the profile page returns 200 and a login wall with none of that data inside it. Calling web_profile_info from a datacenter IP returned HTTP 429 with an empty body for me in July 2026.
  • A single residential IP is capped near 200 requests per hour before the 429s start, so a real profile scrape needs residential IPs, a slow cadence, and a browser-shaped request. Datacenter IPs are refused on sight.
  • For a list of accounts I skip the proxy work and send one request per handle to a managed Instagram profile scraper, which returns the same fields as JSON with the blocking handled server side.

I tried to scrape an Instagram profile the obvious way first: one requests.get against instagram.com/nasa/ from a cloud server, expecting the follower count and bio to sit in the HTML. The page came back 200 and served a login wall, with none of the profile data inside it. Then I called the endpoint Instagram’s own web app uses, web_profile_info, and got HTTP 429 with an empty body before I parsed a single field.

That is where any honest guide on how to scrape Instagram profiles starts in 2026. Below is what I ran in July 2026: the exact fields a profile hands back (bio, followers, post count, external link, verified), the Python that pulled them, why the request gets blocked, and the managed route I use when I need hundreds of profiles without babysitting proxies.

What data can you scrape from an Instagram profile?

You can scrape five core fields from a public Instagram profile without logging in: the bio, follower count, post count, external link, and verified status. Instagram returns all five in one JSON object, so a profile scrape is really one request and a handful of key lookups rather than a page parse.

Here is how each field maps to the JSON key the profile endpoint returns:

FieldJSON keyNotes
BiobiographyFree text, keeps line breaks and @mentions
Followersedge_followed_by.countExact integer, not the rounded “1.2M” the UI shows
Postsedge_owner_to_timeline_media.countLifetime post count for the account
External linkexternal_urlThe clickable bio link, null when unset
Verifiedis_verifiedBoolean, the blue-check flag

The same object carries full_name, the numeric id, profile_pic_url_hd, and the private-vs-public is_private flag. Professional accounts add a public contact block, business_email, business_phone_number, and category_name, though Instagram withholds those for many large accounts. What you cannot get logged out is the follower roster or Stories, because the official Instagram Graph API only returns data for Business and Creator accounts you manage and never an arbitrary public profile. All five fields live in a single document, so the next thing to know is where that document comes from.

Where does Instagram keep a profile’s data?

Instagram keeps a profile’s data in a JSON document served by the web_profile_info endpoint, not in the page HTML your browser loads first. The profile page is a React shell that hydrates its numbers from a background call, so parsing the HTML source returns a login wall with no edge_followed_by in it.

The endpoint is a GET request:

https://i.instagram.com/api/v1/users/web_profile_info/?username=nasa

It returns the profile under data.user, with every field from the table above. You can watch the web app make this exact call: open a public profile in Chrome, open DevTools, go to the Network tab, filter for web_profile_info, and reload. The request that appears carries an x-ig-app-id: 936619743392459 header, which is the Instagram web client id the endpoint expects. Reproducing that request in code is the whole job, which is the next step.

How do you scrape an Instagram profile with Python?

You scrape an Instagram profile with Python by requesting the web_profile_info endpoint with the web app’s headers, then reading the five fields out of data.user. Before the version that works, here is the naive HTML call that fails, so you can recognize it in your own code:

import requests

ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " \
     "(KHTML, like Gecko) Chrome/126.0 Safari/537.36"
r = requests.get("https://www.instagram.com/nasa/", headers={"User-Agent": ua}, timeout=20)
print(r.status_code)                       # -> 200
print("edge_followed_by" in r.text)        # -> False, profile JSON is not in the HTML
print("login" in r.text.lower())           # -> True, a login wall is served instead

Now the call that returns real data. It hits web_profile_info with the web headers and reads the five fields into a plain dictionary:

import requests

USERNAME = "nasa"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/126.0 Safari/537.36",
    "x-ig-app-id": "936619743392459",   # Instagram web client app id
}
url = f"https://i.instagram.com/api/v1/users/web_profile_info/?username={USERNAME}"

# Route this through a residential proxy in production: proxies={"https": "..."}
resp = requests.get(url, headers=headers, timeout=20)
print(resp.status_code)   # 200 on a clean residential IP, 429 from a datacenter IP

user = resp.json()["data"]["user"]
profile = {
    "username":     user["username"],
    "bio":          user["biography"],
    "followers":    user["edge_followed_by"]["count"],
    "posts":        user["edge_owner_to_timeline_media"]["count"],
    "external_url": user["external_url"],
    "verified":     user["is_verified"],
}
print(profile)

From a clean residential IP this returns the nasa profile with is_verified set to True, a follower count over 100 million, and external_url pointing at nasa.gov. From a datacenter IP in July 2026 the same request returned 429 with an empty body, which is the block this guide keeps coming back to. One catch worth flagging: a plain requests call can also be refused at the TLS handshake before the headers matter, and the fix is a fingerprint-matching client. I walk through that in my guide on how to scrape Instagram with Python. One profile is easy; a list of them is where the rate limit starts to bite.

How do you scrape a list of Instagram profiles into a CSV?

You scrape a list of Instagram profiles into a CSV by looping the same web_profile_info request over your usernames and writing each profile’s fields to a row. The only additions to the single-profile code are a delay between requests and a check that skips accounts that returned a block:

import csv, time, requests

USERNAMES = ["nasa", "instagram", "natgeo"]
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
                  "(KHTML, like Gecko) Chrome/126.0 Safari/537.36",
    "x-ig-app-id": "936619743392459",
}

rows = []
for name in USERNAMES:
    url = f"https://i.instagram.com/api/v1/users/web_profile_info/?username={name}"
    r = requests.get(url, headers=headers, timeout=20)
    if r.status_code != 200:
        print(name, "->", r.status_code)   # 429 means the IP tripped the rate gate
        continue
    u = r.json()["data"]["user"]
    rows.append({
        "username":     u["username"],
        "bio":          u["biography"],
        "followers":    u["edge_followed_by"]["count"],
        "posts":        u["edge_owner_to_timeline_media"]["count"],
        "external_url": u["external_url"],
        "verified":     u["is_verified"],
    })
    time.sleep(5)   # stay well under the ~200 requests/hour/IP ceiling

with open("profiles.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.DictWriter(f, fieldnames=["username", "bio", "followers",
                                      "posts", "external_url", "verified"])
    w.writeheader()
    w.writerows(rows)

The time.sleep(5) is not decoration. Firing the loop as fast as Python can send requests is the quickest way to turn every row after the first few into a 429. At five seconds per request a single IP handles a few hundred profiles in an hour before it hits the ceiling, and past that you need to spread the list across a pool of IPs. That block is the part most people underestimate, so it is worth measuring directly.

Why do Instagram profile scrapers get blocked?

Instagram profile scrapers get blocked at the IP and request-rate level: a datacenter IP returns HTTP 429 with an empty body before any profile JSON comes back. The 429 with a zero-length text/plain body and no retry-after value is Instagram refusing the request at the edge, not a normal rate-limit response, which would hand back a wait time and some content.

Three defenses stack up in front of the profile endpoint:

For plain profile fields the rotating GraphQL doc_id that breaks post and comment scrapers is not in your way, since web_profile_info is a stable REST endpoint. The block you fight for profiles is almost entirely IP reputation and rate. I go deeper on the warning signs and limits in my guide to avoiding Instagram scraping blocks. Knowing what triggers the block tells you exactly which levers move it.

How do you scrape Instagram profiles without getting blocked?

You scrape Instagram profiles without getting blocked by changing the IP reputation and slowing the request rate, or by handing the job to a scraper API that carries the proxies and retries for you. The levers that moved the result in my testing, in order of impact:

Doing all of that yourself means buying a residential proxy pool, rotating it, and retrying the blocks, which becomes a standing project past a few thousand profiles. The alternative is one authenticated request to a managed Instagram profile scraper API that handles the pool, the headers, and the retries server side. This matters because the official read-only route is gone: Meta retired the Instagram Basic Display API on December 4, 2024, so a scraper API is what fills the gap for arbitrary public profiles. The request is a plain GET with your API key:

curl "https://chocodata.com/api/v1/instagram/profile?username=nasa&api_key=$CHOCO_API_KEY"

The Python version returns the same five fields you would otherwise parse out of web_profile_info, without a proxy or a login on your side:

import requests, os

resp = requests.get(
    "https://chocodata.com/api/v1/instagram/profile",
    params={"username": "nasa", "api_key": os.environ["CHOCO_API_KEY"]},
    timeout=30,
)
user = resp.json()["data"]["user"]
print(user["biography"])
print(user["edge_followed_by"]["count"], "followers")
print(user["edge_owner_to_timeline_media"]["count"], "posts")
print(user["external_url"], user["is_verified"])

The endpoint returns Instagram’s documented profile shape, so the field names match the DIY code above and you can drop it in as a fallback when your own scraper starts seeing 429s. For a one-off pull of a few public profiles, the web_profile_info script from your own machine is free and fine. Once you need thousands of profiles or continuous collection, the maintenance costs more time than the API costs money. I rank the managed options head to head in my best Instagram scrapers in 2026 roundup.

Scraping public Instagram profiles sits in a contested but increasingly defensible area in the US, and it is not a blanket yes. The distinction that matters is whether you are logged in. Meta’s Platform Terms prohibit collecting data by automated means without prior permission, and those terms bind you once you authenticate, so scraping from a logged-in account is the riskier path.

Scraping while logged out is treated differently. In Meta Platforms v. Bright Data, a federal court ruled in January 2024 that Meta’s terms do not bar logged-off scraping of public data, because a logged-out scraper never agrees to them. That reasoning is specific to public data and does not touch private accounts. Separately, a profile’s fields can be personal data under the GDPR and CCPA regardless of how public they are, so collecting bios, contact details, or follower counts on identifiable people carries its own obligations. None of this is legal advice: scrape only public fields, respect the rate limits, and take advice for your own use case before you collect at scale.

FAQ

How do you scrape Instagram profiles without coding?

You scrape Instagram profiles without coding by using a hosted actor or a no-code scraper that takes a list of usernames and returns a spreadsheet, or by calling a scraper API with a tool like a browser extension or a no-code HTTP client. Apify and PhantomBuster run cloud actors that export profile fields to CSV, and a scraper API returns the same biography, edge_followed_by.count, and is_verified fields as JSON that a no-code tool can map. The tradeoff is cost per profile against the maintenance of a DIY script.

Can you scrape an Instagram profile's email and phone number?

You can read an Instagram profile's public business_email and business_phone_number only when the account is a professional (Business or Creator) account that chose to display a contact button, and even then Instagram withholds the contact block for many large accounts. Personal accounts do not expose these fields at all. When they are present they arrive in the same web_profile_info response as the bio and follower count, which is what an Instagram email scraper reads, but you should treat any email or phone as personal data under privacy law.

How many Instagram profiles can you scrape per hour?

For logged-out scraping, a single residential IP handles roughly 200 profile requests per hour before Instagram starts returning HTTP 429, a ceiling reported across community testing and Scrapfly's engineering guide. A datacenter IP is throttled far harder and is often refused within minutes. To scrape thousands of profiles you spread the load across a residential proxy pool with a slow per-IP cadence, or you send the job to a scraper API that manages the pool for you.

Can you scrape a profile's full follower list?

No, the profile endpoint returns a follower count number, not the list of accounts behind it. The edge_followed_by.count field gives you the exact follower total, but the roster of individual followers comes from a separate app endpoint that Instagram gates behind a logged-in session, so a logged-out profile scrape cannot page it. The official Instagram Graph API is the same: it exposes followers_count and no member list.

LH
Lena Hoff
I've built Instagram data pipelines for years. On instagramscraperapi.com I run Instagram scraping methods against live pages and publish what actually holds up.