How to scrape Instagram
Extract public Instagram profile and post metadata as typed JSON. Login-gated data is Enterprise only. Heavy anti-bot, SPA rendering, ToS-aware.
- Site:
- instagram.com
- Difficulty:
- Hard
- Rendering:
- Single-page app
- Anti-bot:
- managed anti-bot bypass layer (Cloudflare, DataDome, PerimeterX style protections handled for you), aggressive login wall after a handful of unauthenticated views, aggressive per-IP rate limiting and throttling applied to unauthenticated traffic, GraphQL response obfuscation and frequently rotated internal endpoints, device and behavioral fingerprinting on the web client
What you can extract from Instagram
These are the fields teams most often pull from instagram.com, and why a hand-written selector for each one tends to break over time.
| Field | Type | Why selectors break |
|---|---|---|
| username | string | The handle is rendered by Instagram's React SPA into hashed, build-specific class names that rotate on every deploy, so a CSS selector pinned to today's div class silently returns null after the next frontend release. |
| fullName | string | Display names sit in the same obfuscated component tree as the username and bio, and Instagram frequently swaps the wrapping element between a header span and a heading tag, so a positional selector grabs the wrong text or nothing at all. |
| followerCount | number | Follower totals are shown abbreviated (12.3M) in the visible DOM while the exact integer lives only in an embedded GraphQL/JSON blob, so a CSS selector captures a lossy string you then have to parse and un-abbreviate by hand. |
| followingCount | number | This count shares an identical markup pattern with followers and posts in a three-item header list with no stable labels, so an index-based selector breaks the moment Instagram reorders or A/B tests that header row. |
| postCount | number | The post total is injected after hydration and is missing from the first server response, so a selector that runs before the SPA finishes mounting sees an empty node and a guessed scrape returns zero instead of the real value. |
| isVerified | boolean | Verification is conveyed only by a decorative SVG badge with no text and no stable aria attribute, so there is no reliable CSS selector for a true/false value without inferring presence of an icon whose class hash changes between builds. |
| externalUrl | string | Bio links are wrapped in an l.instagram.com redirect with tracking parameters and the real destination is encoded in a query string, so a selector reading the href returns the tracker rather than the actual URL the profile points to. |
| isPrivate | boolean | Privacy state is expressed as the absence of a grid plus a localized text notice, so a selector keyed to English copy fails entirely on non-English locales and cannot be trusted as a structured flag. |
The schema-first approach
Instead of maintaining selectors, you hand Extracto a JSON schema describing the shape you want. Here is a schema that works for Instagram:
{
"username": "string",
"fullName": "string",
"biography": "string",
"followerCount": "number",
"followingCount": "number",
"postCount": "number",
"isVerified": "boolean",
"isPrivate": "boolean",
"externalUrl": "string",
"profilePicUrl": "string",
"category": "string"
} - 1
Describe the Instagram fields you want
Write a small JSON schema listing the fields to pull from instagram.com (for example username, fullName, followerCount). No CSS selectors or XPath needed.
- 2
Send the URL and schema to Extracto
POST the Instagram page URL and your schema to the Extracto API. Extracto renders the page in a real browser, handling JavaScript and dynamic content automatically.
- 3
Get schema-validated JSON back
Extracto returns JSON that matches your schema exactly, validated before it leaves the API, so your pipeline never receives broken or partial data.
Common pitfalls when scraping Instagram
- Treating Instagram as fully scrapable: most surfaces are login-gated, and a sign-in wall routinely triggers after only a handful of unauthenticated views, so plan your extraction around the thin public metadata layer rather than feeds, followers or Stories.
- Trusting abbreviated counts: the visible DOM shows 12.3M while the precise integer lives in an embedded JSON payload, so define followerCount as a number in your schema and let Extracto return the exact value or null instead of parsing rounded strings yourself.
- Confusing private with empty: a private account returns profile metadata but no posts, which can look like an extraction failure. Add isPrivate as a boolean so you can distinguish a locked account from a genuinely missing field.
- Scraping personal data at scale: harvesting follower lists or building profiles of individuals violates Meta's Terms and likely GDPR or CCPA. Keep volume modest, scrape only accounts you own or are permitted to analyze, and route any login-gated need through the Enterprise plan.
- Hardcoding CSS selectors against the React build: every Instagram frontend deploy rotates hashed class names, so selector-based scrapers break weekly. Extracto works from a JSON schema with no selectors, so a redesign does not silently null your fields.
Is it legal to scrape Instagram?
Instagram is one of the harder targets to scrape responsibly, and you should treat it as login-gated by default. Meta's Terms of Use prohibit automated collection of data without prior written permission, and instagram.com/robots.txt disallows most crawler paths while explicitly naming approved partners. Instagram routinely forces a sign-in wall after a small number of unauthenticated profile or post views, so even "public" pages are only briefly visible without an account. With Extracto you can reliably reach the small surface that remains public to logged-out visitors: top-level profile metadata and some post-level metadata rendered into the initial document. Anything behind login (full follower lists, private accounts, Stories, DMs, the logged-in feed) needs authenticated session cookies and is available only on the Enterprise plan, where access is reviewed case by case. Do not scrape personal data at scale, do not build shadow profiles of individuals, and respect GDPR and CCPA when any field could identify a person. Keep request volume modest, scrape only your own accounts or accounts you have permission to analyze, and prefer Meta's official Graph API and Instagram Basic Display alternatives for production use where they fit your need.
Extracto follows robots.txt by default and rate-limits politely. You are responsible for compliance with Instagram's terms and applicable law (GDPR, CCPA) in your use case.