You found a tutorial. It was clear, the code was short, and it worked.
For about twenty requests.
Then Instaloader started throwing 429 Too Many Requests. Or your Selenium script landed on a login wall. Or the JSON came back with "require_login": true and nothing else. You added a sleep(5). You tried a proxy. You logged in with a spare account, and a day later that account was asking you to confirm it's not a robot.
If that's how you got here, you didn't do anything wrong. The tutorials are just out of date. Most of the top-ranking "scrape Instagram with Python" guides were written in 2020–2022, and Instagram has changed a lot since.
This article covers what changed, what actually works now, and a small real project to prove it: a script that checks whether an influencer's audience is real.
Why your Instagram scraper keeps breaking
Instagram is one of the most aggressive sites on the internet when it comes to blocking automation. Here's what you're up against in 2026:
Datacenter IPs are blocked almost immediately. If your script runs on AWS, GCP, a VPS or a CI runner, Instagram knows it's not a phone in someone's hand. Many requests fail on the first try.
Your HTTP client is fingerprinted. Python's requests and httpx have a recognizable TLS signature. Instagram can tell it's not a real browser before it even reads your headers, so changing the User-Agent doesn't fool it.
Logged-out access is heavily limited. You used to be able to browse public profiles freely. Now you get a login prompt after a few pages, and logged-out rate limits are tight: roughly a couple of hundred requests per hour from a single clean IP.
Logging in makes it worse, not better. Scraping with a logged-in account triggers challenge_required and feedback_required errors, and eventually gets the account suspended. It also puts you squarely against Instagram's terms.
The internal endpoints move. Instagram's web app uses private GraphQL queries identified by doc_id values. Those rotate every few weeks. When they do, your scraper doesn't crash. It just quietly returns empty data, which is worse.
The official API won't help. Meta shut down the Instagram Basic Display API in December 2024. The Graph API that replaced it only returns data for Business and Creator accounts you own or manage. It can't look up a public profile you don't control.
The DIY fix is residential proxies (often $10+ per GB), sticky sessions, browser fingerprint spoofing, retry logic for four different error types, and someone to watch it all when Instagram ships a change. That's a real project, and for most people it's not the project they actually wanted to build.
What works instead: let an API deal with Instagram
The option that has quietly taken over is using an Instagram data API. You send one normal HTTPS request. The provider handles the proxies, sessions and breaking changes on their side, and you get clean JSON back.
Your code stops being a scraper and becomes a regular API client. That's the difference between maintaining something every week and forgetting it exists.
I'll use GramScraper in the examples because it's the one I know best (full disclosure: I work on it). The same pattern works with any similar provider. You get 100 free credits on signup, no credit card, and most lookups cost 1 credit.
Here's a public profile lookup:
import requests
r = requests.get(
"https://gramscraper.com/api/instagram/v1/user/by/username",
headers={"Authorization": "Bearer gs_your_api_key"},
params={"username": "natgeo"},
timeout=30,
)
profile = r.json()
print(profile["username"], profile["follower_count"])No browser, no proxy, no login, no doc_id. You get followers, following, post count, bio, verification status and the account's numeric ID. You'll need that ID to fetch posts.
A real project: is this influencer's audience real?
Profile data on its own is a bit boring. So let's build something useful with it.
Brands pay creators based on follower counts, and follower counts are easy to fake. The numbers that are much harder to fake are about engagement:
- Engagement rate: average likes plus comments per post, divided by followers. Around 1–3% is broadly normal on Instagram. Under about 0.3% is a red flag.
- Consistency: compare the average post to the median post. If the average is far above the median, one viral post is carrying the account.
- Comment-to-like ratio: real audiences talk. Bought engagement tends to be all likes and no conversation.
This script checks all three for a list of accounts:
import statistics, requests
API = "https://gramscraper.com/api/instagram"
HEADERS = {"Authorization": "Bearer gs_your_api_key"}
def get(path, **params):
r = requests.get(API + path, headers=HEADERS, params=params, timeout=30)
r.raise_for_status()
return r.json()
def vet(username):
profile = get("/v1/user/by/username", username=username)
followers = profile["follower_count"]
page = get("/v1/user/medias/chunk", user_id=profile.get("pk") or profile.get("id"))
posts = page if isinstance(page, list) else page.get("items", [])
posts = [p for p in posts if isinstance(p, dict)][:12]
if not posts or not followers:
return print(f"@{username}: not enough public data")
likes = [p.get("like_count", 0) for p in posts]
comments = [p.get("comment_count", 0) for p in posts]
er = (statistics.mean(likes) + statistics.mean(comments)) / followers * 100
spread = statistics.mean(likes) / max(statistics.median(likes), 1)
talk = sum(comments) / max(sum(likes), 1) * 100
flag = "⚠️ check" if er < 0.3 or spread > 3 else "✅ looks real"
print(f"@{username:<20} {followers:>11,} followers "
f"ER {er:5.2f}% avg/median {spread:4.1f}x "
f"comments/likes {talk:4.1f}% {flag}")
for name in ["natgeo", "nike", "creator_on_your_shortlist"]:
vet(name)Save it as vet.py, paste in your API key and run python vet.py. You get one line per account: follower count, engagement rate, how much the average post beats the median, how much people comment, and a flag.
Each account costs 2 credits (one for the profile, one for the posts), so the free tier covers about 50 creators.
A few notes:
- Private accounts return no posts. That's intentional. The script only reads what anyone can see without logging in.
- Twelve posts is a sample. The response includes an
end_cursor; pass it back as a parameter to page further through the account's history. - For video-first creators, use Reels.
/v1/user/clips/chunkreturns view counts, and views per follower often tells you more than likes. - Inspect a real response before you build on it. Print one raw payload and check the fields you rely on.
Handling errors like a grown-up
An API removes most of the pain, but you still write normal, careful HTTP code:
429: you're sending too fast. Back off with exponential delay and some randomness, then retry.5xx: usually temporary. Retry a limited number of times.401: your key is wrong or missing. Retrying won't fix it.402: you're out of credits. Retrying definitely won't fix it.
Only retry the errors that can succeed on a second try. Scripts that retry everything in a tight loop are how people burn through credits overnight.
When DIY scraping still makes sense
To be fair: if you need a few profiles once, from your own laptop on a home connection, Instaloader might still get you there. If you're a researcher who enjoys the cat-and-mouse and has time to maintain it, building your own is a great way to learn.
But if you're building a product, a dashboard or anything that has to keep working next month, the maths usually favors an API. A single residential proxy bill can cost more than a pack of credits, before you count the hours spent debugging.
Doing this responsibly
Stick to public data: the same things anyone sees when they open a profile without logging in. Don't try to get into private accounts, don't build profiles of private individuals, and check that what you're doing fits the privacy laws where you and your users are. Vetting a creator before a brand pays them is about as legitimate a use as it gets.
Wrapping up
The old tutorials aren't wrong so much as expired. Instagram closed the doors they walked through.
What works in 2026 is simpler: skip the browser, skip the proxies, call a data API, and spend your time on the part you actually care about. In this case, that's catching fake influencers in about thirty lines of Python.
Want to run the script? GramScraper gives you 100 free credits, with no credit card. The quickstart takes you from signup to your first request in a few minutes.
Stuck on a specific error? These go deeper:
- Instaloader 429 errors, and what to use instead
- Fixing Instagram's "login required" when scraping
- Instagram scraper rate limits explained
Vet your first creators free
100 free credits covers about 50 accounts. No credit card.
Get your API key →