1. 2scraper
  2. Social media & video
  3. Instagram
Open source · MITFree to run · paid add-ons optional

Instagram Scraper

Public profiles, posts and Reels, read logged out — no account, no cookies, nothing to get banned.

  • Playwright
  • Selenium
  • Puppeteer
  • Scraping Browser API
  • JSON
  • CSV
instagram-scraper
# Profile plus recent posts
$ python3 playwright_scraper.py --url natgeo --posts 12 --format csv --out natgeo.csv
✓ 4 sample rows · 39 columns in sample_output.json
0accounts or logins needed
12posts per profile a logged-out page embeds
27 of 29pages read in a mixed live run (2 were dead links), Oct 2026
3browser engines sharing one fetch loop
Available Instagram scrapers

What the Instagram scraper collects

Give it a username, a profile URL, a post or Reel URL, or a file of them, and it returns one row per profile (name, bio, links, followers, following, verified, private) and one row per post (date, likes, comments, caption, hashtags, mentions, co-authors, tagged accounts, every carousel image or video URL). It reads the JSON Instagram embeds for any logged-out visitor and writes JSON or CSV, with change monitoring, incremental runs and resumable checkpoints.

Profile plus recent posts

Input: A username, @username or profile URL

One profile row (bio, links, followers, following, verified, private) plus one row per embedded post, pinned posts first.

Single post or Reel

Input: A /p/ or /reel/ URL

One post row with date, likes, comment count, full caption, hashtags, mentions, co-authors, tagged users and every media URL.

Batch from a file

Input: A text file with one username, profile or post URL per line

Every line scraped in one run; dead usernames and shortcodes are reported as not found rather than as empty rows.

Only new posts

Input: A profile, a --since date and an earlier run

Refreshes the profile and opens only posts the earlier run did not already have, from the given date on.

Quick start

From zero to Instagram data in 3 steps

  1. Install

    Clone the repository and install one browser engine. Python 3.9+.

    $ git clone https://github.com/2scraper/instagram-scraper.git && cd instagram-scraper
    $ python3 -m venv .venv && source .venv/bin/activate
    $ pip install -r requirements-playwright.txt
    $ playwright install chromium
  2. Run a scraper

    Pick what you need and run it. Every flag is documented in the README.

    $ python3 playwright_scraper.py --url natgeo --posts 12 --format csv --out natgeo.csv
  3. Get structured data

    Rows land in JSON or CSV (--format), with a .meta.json sidecar recording status, pages fetched and why the run stopped.

Output

Instagram data fields

39 columns per row in a predictable schema. The most useful ones:

FieldWhat it holds
skuinstagram-user-{id} for a profile, instagram-post-{shortcode} for a post
categoryprofile or post (a Reel is a post with product_type clips)
usernameThe profile's handle, or a post's owner
full_nameDisplay name
is_verifiedVerified badge
biographyProfile bio text
bio_links_jsonReal link targets from the bio, not Instagram's redirect
follower_countExact follower count
following_countAccounts followed
posted_atPost time in UTC
like_countLikes; null when the author hides them
comment_countNumber of comments
captionFull post caption
hashtags_jsonHashtags used in the caption
media_typeimage, video or carousel
media_urls_jsonEach carousel slide's image or video URL
[
  {
    "sku": "instagram-user-787132",
    "source": "instagram.com",
    "category": "profile",
    "title": "National Geographic",
    "brand": null,
    "price": null,
    "currency": null,
    "price_source": null,
    "product_url": "https://www.instagram.com/natgeo/",
    "image_url": "https://scontent.cdninstagram.com/v/t51.82787-19/683576066_18653628823019133_9051036240…",
    "scraped_at": "2026-10-04T08:38:08Z",
    "username": "natgeo",
    "user_id": "787132",
    "full_name": "National Geographic",
    "is_verified": true,
    "is_private": false,
    "biography": "Step into wonder and find your inner explorer with National Geographic 🌎",
    "bio_links_json": "[\"http://visitstore.bio/natgeo\", \"https://ngmdomsubs.nationalgeographic.com/servlet/Ord…",
    "follower_count": 268456941,
    "following_count": 194,
    "recent_posts_json": "[\"DdG4RIxIPyf\", \"DbIdlf0j6uA\", \"DeBJRyaDIyP\", \"DeCbRyHgmlu\", \"DeCOar9gqof\", \"DeAZ0uAgNu…",
    "shortcode": null,
    "media_type": null,
    "product_type": null,
    "posted_at": null,
    "like_count": null,
    "comment_count": null,
    "likes_hidden": null,
    "caption": null,
    "hashtags_json": null,
    "mentions_json": null,
    "alt_text": null,
    "coauthors_json": null,
    "tagged_users_json": null,
    "media_count": null,
    "media_urls_json": null,
    "video_url": null,
    "width": null,
    "height": null
  },
  {
    "sku": "instagram-post-DeAZ0uAgNuA",
    "source": "instagram.com",
    "category": "post",
    "title": "Presented by @Rolex. When Matt Shirley first shone his flashlight into a cave in Gabon,…",
    "brand": null,
    "price": null,
    "currency": null,
    "price_source": null,
    "product_url": "https://www.instagram.com/p/DeAZ0uAgNuA/",
    "image_url": "https://instagram.fakx4-1.fna.fbcdn.net/v/t51.82787-15/829585622_18703580971019133_8641…",
    "scraped_at": "2026-10-04T08:38:37Z",
    "username": "natgeo",
    "user_id": "787132",
    "full_name": "National Geographic",
    "is_verified": true,
    "is_private": null,
    "biography": null,
    "bio_links_json": null,
    "follower_count": null,
    "following_count": null,
    "recent_posts_json": null,
    "shortcode": "DeAZ0uAgNuA",
    "media_type": "image",
    "product_type": "feed",
    "posted_at": "2026-10-02T21:05:27Z",
    "like_count": 47594,
    "comment_count": 154,
    "likes_hidden": false,
    "caption": "Presented by @Rolex. When Matt Shirley first shone his flashlight into a cave in Gabon,…",
    "hashtags_json": "[\"Rolex\", \"AfricaEarthsWildHome\", \"PerpetualPlanet\"]",
    "mentions_json": "[\"Rolex\", \"hammond_robin\", \"NatGeoTV\", \"DisneyPlus\", \"hulu\"]",
    "alt_text": "Photo by National Geographic on October 02, 2026. May be an image of crocodile and text.",
    "coauthors_json": null,
    "tagged_users_json": null,
    "media_count": 1,
    "media_urls_json": "[\"https://instagram.fakx4-1.fna.fbcdn.net/v/t51.82787-15/829585622_18703580971019133_86…",
    "video_url": null,
    "width": 1350,
    "height": 1688
  }
]

First 2 of 4 rows from a real run — sample_output.json. Long values shortened here.

Built for Instagram

Handles what breaks naive Instagram scrapers

Exact numbers, not rounded previews

Open Graph tags round counts to '268M Followers'; the scraper reads the JSON the page embeds and matches each block to the URL's own username or shortcode.

Throttling reported as blocked, not empty

A rate-limited client gets the page without its data or the login page. Both are reported as blocked (stop_reason rate_limited), and without a proxy pool the run stops after 3 blocked answers in a row.

Not-found pages recognised under HTTP 200

Instagram serves 'page isn't available' with status 200; dead usernames and shortcodes are reported as not found instead of being written as rows.

Monitoring that does not mislead

diff_runs.py shows old and new values with deltas, marks posts that slid out of the 12-post window as left_selection rather than removed, and refuses to compare mismatched runs.

Everything built in

Scraping infrastructure included

The same tested core runs every 2scraper repository, so the hard parts work the same way on every site.

CAPTCHA handling

Detects the challenge a page really serves and, with a 2Captcha key, solves it only when it blocks the data — a capped number of solves per page.

Proxy rotation

Single proxy or a pool from a file, rotated on blocks with a fresh browser per exit. Credentials stay out of argv and logs.

Fingerprints

Optional complete device identity from the 2Captcha Fingerprint API — user agent, client hints, locale and timezone that agree with each other.

Scraping Browser API

Point --cdp-endpoint at a managed Chromium with its own exit country — no browser infrastructure to run yourself.

Honest run metadata

Every run writes a .meta.json sidecar and distinct exit codes: complete, blocked, empty and partial are never confused, and a failed run never overwrites good data.

Diff two runs

diff_runs.py compares two complete Instagram snapshots by id — new, removed and changed rows for monitoring.

Use cases

What teams do with Instagram data

Influencer analytics

Track follower counts, posting cadence and engagement on recent posts for a list of public creators.

Brand and campaign monitoring

Collect captions, hashtags, mentions and co-authors on posts to see who tags a brand and how posts perform.

Engagement change alerts

Run on a schedule and use diff_runs.py --fail-on-change to flag follower or like movements in cron or CI.

Content archiving

Save captions, alt text and every carousel media URL of public posts — download media soon, as CDN links expire.

Access & pricing

Free to run. Pay only for what the site requires.

Free to run · paid add-ons optional

No account, key or proxy is needed for a normal run: verified live on 2026-10-04 from an ordinary residential IP with all three engines. Instagram limits logged-out reading per address, so for volume a rotating pool of 2Captcha residential proxies (--proxy-file) is what helps; a datacentre address may get the login page from the first request (not yet measured). The Scraping Browser API and the Scraper API are optional — the Scraper API read posts and Reels but not profiles in the README's tests.

Optional 2Captcha services — captcha solving, the Scraping Browser API, residential proxies and fingerprints — work behind one key.

Good to know

  • Only the 12 posts a logged-out profile page embeds; no comments, follower lists, stories, search or hashtag pages.
  • No exact post count, Reel play count or location.
  • Private profiles return a profile row with is_private true and no posts.

2scraper vs. hosted scraper APIs

2scraperTypical hosted API
CodeOpen source, MIT — read and patch itClosed
CostFree; optional services billed only when usedSubscription or per request / per result
Runs onYour machine, server or CIThe vendor's infrastructure
OutputJSON/CSV files + run metadataAPI responses
When the site changesFix it yourself or open an issue / PRWait for the vendor
FAQ

Instagram scraper FAQ

Do I need an Instagram account?

No. It reads only what Instagram shows any logged-out visitor — no password, no session, no cookies.

How many posts can I get per profile?

Up to 12: that is what a logged-out profile page embeds, pinned posts first. The rest of the timeline needs a login and is out of scope.

Do I need a proxy?

Not for small runs from a residential IP. Instagram throttles logged-out reading per address, so rotating residential proxies are the way to read more.

Did it meet a captcha?

No. In 10 test runs on 2026-10-04 no page carried a captcha; a throttled client gets the login page or a data-less page instead, which is reported as blocked.

Can I resume an interrupted run?

Yes. Pass --checkpoint to save progress after every URL and rerun with --resume to keep finished rows and retry the rest.

Is scraping Instagram allowed?

Instagram's robots.txt disallows all crawlers and Meta's terms forbid automated collection without permission. Whether your use is permitted depends on your jurisdiction and purpose — check first, and do not use it to track individuals.