Instagram Scraping API Guide: Python, Node, cURL (2026)

Last Updated on September 24, 2026
by InsightSocial Team
11 min read
Follow:
I

Most Instagram data projects start the same way: someone needs follower counts for a list of creators, or every comment on a launch post, or the last month of posts under a hashtag. Then they discover that Instagram has no public endpoint for any of that, and the afternoon turns into a week of headless browsers and rotating proxies.

This guide skips that week. It walks through reading public Instagram data with the InsightSocial API: one HTTP header, JSON back, the same response shape for every endpoint. You get code in cURL, Python and Node for profiles, posts, reels, comments, followers and hashtag search, a pagination loop you can reuse, and a costed example job so you know what a real run spends before you start it.

If you don't write code, the Instagram scraper Chrome extension does the same jobs from your browser and exports to CSV or Excel. Everything below is for developers.

Why not use Instagram's official API?

Meta's Instagram Platform is built for accounts you manage. Your app's users must have a professional (business or creator) account, and the app acts on their behalf. There are two partial exceptions, and both are narrow:

  • Business Discovery reads basic public metrics of other professional accounts, but not personal accounts, and not age-gated ones.
  • Hashtag search needs App Review for Public Content Access and is capped at 30 unique hashtags per account in a rolling 7-day window (as of September 2026).

There is no official way to list someone's followers, read comments on a post you don't own, or pull reels from an arbitrary public profile. If your use case is "analyse accounts I don't control", you need a scraping API. We compare the options in Best Instagram data APIs for developers.

How do I get set up?

Sign in, open the API section of the dashboard, and your first key is created for you. It starts with isk_live_ and is shown once, so store it right away. The quickstart has the full walkthrough.

export INSIGHTSOCIAL_API_KEY="isk_live_..."

That header is the whole credential. There's no OAuth flow, no Instagram login, and no Meta app to register. The free plan includes 500 credits a month, and your first 10 calls priced at 200 credits or less cost nothing.

Every response uses the same envelope: your payload under data, plus credits_used, credits_remaining and a charge_reason that tells you why the call cost what it did.

How do I scrape an Instagram profile?

GET /v1/instagram/profile takes a handle without the @ and costs 20 credits. It returns the bio, the exact follower count, following count, verification status, and a public email when one sits in the bio.

curl "https://api.insightsocial.app/v1/instagram/profile?handle=natgeo" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY"
import os
import requests

BASE = "https://api.insightsocial.app/v1"
HEADERS = {"x-api-key": os.environ["INSIGHTSOCIAL_API_KEY"]}

body = requests.get(
    f"{BASE}/instagram/profile", headers=HEADERS, params={"handle": "natgeo"}
).json()

if body["success"]:
    author = body["data"]["author"]
    print(author["username"], author["followers"], author.get("verified"))
    print("cost:", body["credits_used"], "left:", body["credits_remaining"])
else:
    print(body["error"]["type"], body["error"]["message"])
const BASE = "https://api.insightsocial.app/v1";
const headers = { "x-api-key": process.env.INSIGHTSOCIAL_API_KEY };

const res = await fetch(`${BASE}/instagram/profile?handle=natgeo`, { headers });
const body = await res.json();

if (body.success) {
  const { username, followers } = body.data.author;
  console.log(username, followers, "cost", body.credits_used);
} else {
  console.log(body.error.type, body.error.message);
}

Two variants are worth knowing. /v1/instagram/profile/about (20 credits) adds the account's country, join month and lifetime post count. /v1/instagram/profile/full (100 credits) returns the profile plus its latest 12 posts and computed metrics like engagement rate and posting cadence. If you'd otherwise call profile and then a page of posts, profile/full does both with the maths already done.

How do I get an account's posts and reels?

GET /v1/instagram/profile/posts returns recent posts with caption, likes, comments, media URL, type and timestamp. GET /v1/instagram/profile/reels does the same for reels, with view counts. Each row in data.items holds a post object in our unified post schema, so the same parser works on both.

body = requests.get(f"{BASE}/instagram/profile/reels",
                    params={"handle": "natgeo"}, headers=HEADERS).json()

for item in body["data"]["items"]:
    post = item["post"]
    eng = post.get("engagement") or {}
    print(post["url"], eng.get("views"), eng.get("likes"), post.get("published_at"))

Both endpoints are metered: posts are 20–340 credits a call and reels 20–360. What moves a call up its range is how much it reads and any extras you switch on, such as additional labels or include hydration. Add dry_run=1 to get an estimate in data.estimate for 0 credits before you commit.

Share counts aren't on the plain lists. If you need them, the /full variants (profile/posts/full, profile/reels/full, 100–500 credits) attach a per-item share count where one can be found. For a single post, /v1/instagram/post (20 credits) returns everything except shares, and /v1/instagram/post/stats (100–180) adds them.

Only fetch what's new

For a daily poll you don't need to re-read the whole feed. Pass since=YYYY-MM-DD, or stop_at_id with the newest post you already have, and the walk stops at the boundary:

curl "https://api.insightsocial.app/v1/instagram/profile/posts?handle=natgeo&since=2026-09-01" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY"

If nothing new was posted, the first page is the whole bill.

How do I scrape comments on an Instagram post?

Give GET /v1/instagram/post/comments a post link (/p/, /reel/ or /tv/) as url and it returns comments with text, author, like count, reply count and timestamp. It costs 100–380 credits a call.

The sort parameter matters more than it looks. The default, top, follows Instagram's popularity order, and Instagram only ranks the most-liked head of a thread, so a deep top walk starts repeating itself. When you want the whole thread, use sort=recent.

const url = new URL(`${BASE}/instagram/post/comments`);
url.searchParams.set("url", "https://www.instagram.com/p/POST_SHORTCODE/");
url.searchParams.set("sort", "recent");

const body = await (await fetch(url, { headers })).json();
for (const item of body.data.items ?? []) {
  const c = item.comment;
  console.log(c.author?.username, c.engagement?.likes, c.text);
}

Every comment page also carries free sentiment, question, purchase-intent and complaint labels under computed.labels, which saves a round trip to an LLM if you're triaging a comment section. Replies to a single comment come from /v1/instagram/post/comment/replies (20–100 credits).

How do I get an account's followers?

GET /v1/instagram/followers takes a handle or numeric user_id and returns accounts with username, display name, avatar, verification status and profile URL. /v1/instagram/following is the mirror image. Both cost 100–200 credits a page.

body = requests.get(f"{BASE}/instagram/followers",
                    params={"handle": "natgeo"}, headers=HEADERS).json()

for item in body["data"]["items"]:
    a = item["author"]
    print(a["username"], a.get("verified"))

Pass user_id instead of handle when you have it; it's faster. On a big list, coverage=full merges several reads so no account appears twice within a page, and data.total reports the profile's follower count.

How do I search a hashtag?

GET /v1/instagram/search/hashtag costs a flat 100 credits a call and returns public posts carrying the tag, with caption, media, engagement and author. The # is optional.

Pick the ranking with type: top (the default), recent, or clips for reels only. Only recent pages. top and clips are a single ranked page each and come back with has_more: false.

curl "https://api.insightsocial.app/v1/instagram/search/hashtag?hashtag=streetphotography&type=recent&max_pages=3" \
  -H "x-api-key: $INSIGHTSOCIAL_API_KEY"

max_pages (1 to 5) walks several pages in one request, which uses one slot of your rate limit. Each page walked is still billed as a call. You can also filter server-side with min_views or max_age_days, but filtering happens after each page is fetched, so discarded rows still cost their page. For keyword search rather than hashtags, look at /v1/instagram/search/reels and /v1/instagram/search/profiles.

How does pagination work?

Every list endpoint pages the same way: read pagination.next_cursor, send it back as cursor, and stop when pagination.has_more is false. Cursors start with is2. and are opaque, so pass them back byte for byte. Here's a generator that works for posts, reels, comments, followers and hashtags:

import time

def paginate(path, params, max_pages=None):
    cursor, pages = None, 0
    while True:
        query = dict(params, **({"cursor": cursor} if cursor else {}))
        res = requests.get(f"{BASE}/{path}", params=query, headers=HEADERS)
        if res.status_code in (429, 503):
            time.sleep(float(res.headers.get("Retry-After", 2)))
            continue  # retry the same cursor
        body = res.json()
        if not body["success"]:
            raise RuntimeError(f'{body["error"]["type"]} ({body["request_id"]})')

        yield from body["data"]["items"]
        pages += 1

        page = body.get("pagination") or {}
        if not page.get("has_more") or (max_pages and pages >= max_pages):
            break
        cursor = page["next_cursor"]


for item in paginate("instagram/followers", {"handle": "natgeo"}, max_pages=5):
    print(item["author"]["username"])

Don't stop on an empty page or a count. has_more is the only reliable signal. Always cap pages on a big account, because a follower list can run into the millions and every page is a billed call. More detail in the pagination docs.

What are the rate limits?

Each key gets 60 requests per minute and 10 requests in flight. Go over either and you get a 429 with Retry-After, RATE_LIMITED or CONCURRENCY_LIMIT respectively, and nothing is charged. In practice: at most 10 workers per key, about one request a second overall, and pages of one list run sequentially because each needs the previous cursor. There's no daily quota. Your credit balance is the real volume limit.

What does a real Instagram scraping job cost?

Say you're auditing one brand account: its profile, its last five pages of posts, comments on three posts (two pages each), five pages of followers, and three pages of a campaign hashtag. Prices come straight from the endpoint pricing table. Metered calls are shown at their floor and their ceiling:

StepCallsCredits per callFloorCeiling
Profile1202020
Posts5 pages20–3401001,700
Comments3 posts × 2 pages100–3806002,280
Followers5 pages100–2005001,000
Hashtag (recent)3 pages100300300
Total211,5205,300

A plain run with no extras switched on lands near the floor. That's too much for one month of the free plan (500 credits), but well inside Pro's 10,000 credits a month ($9.99, or $7.99 billed yearly). At Pro's rate of about $0.001 a credit, the audit comes to roughly $1.50 to $5.30.

A few rules push the real number down:

  • Failed and empty calls cost 0. A private account, a deleted post or a timeout isn't billed.
  • Repeats are free inside the owned window. Run the exact same call again within 1 hour (search and hashtags), 6 hours (lists) or 24 hours (profiles) and it costs nothing, so re-running a crashed script is free.
  • Shared-cache hits cost 5 credits. If someone else fetched the same public data moments ago, you pay 5 instead of the full price.
  • Free calls. Your first 10 calls with a ceiling of 200 or less are free. The profile, follower and hashtag calls above qualify. The posts and comments calls, with ceilings above 200, don't.
  • Retries are safe. Add an Idempotency-Key header and a replayed call costs 0.

Before a metered call runs, we hold its ceiling against your balance and release what it didn't use. If your balance can't cover the ceiling, you get a 402 before anything runs. Check your balance any time with GET /v1/credits, which is free. Full rules are in credits.

Can an AI agent do this for me?

Yes. GET /v1/endpoints is a public, free catalogue of every endpoint with its price and parameters, so a coding agent can pick the right call without guessing. The AI agents guide covers what to give Claude, Cursor or a similar tool so it writes the integration against real endpoints.

FAQ

Do I need an Instagram account or login to use the API?

No. The API reads public data, so you never hand over Instagram credentials. Your x-api-key header is the only credential, and only public data is returned.

Which Instagram data can I get?

Profiles, posts, reels, single posts with stats, comments and replies, followers and following, hashtag, location and keyword search, stories, highlights, tagged posts, audio and trending reels, and transcripts. The Instagram platform page lists all 38 endpoints with prices.

How do I scrape all followers of a large account?

Walk /v1/instagram/followers with the cursor loop above, one page at a time, and cap the page count. Every page is billed, so price the job with the follower count first. For most analysis, a few thousand followers is a representative sample.

Is Python or Node better for scraping Instagram?

Neither matters much. The API does the scraping, and your code only makes HTTP requests and parses JSON. Use whichever your pipeline already runs. cURL is fine for one-off checks.

What happens if a call fails halfway through a pagination walk?

Nothing is charged for the failed call. Wait for Retry-After and retry with the same cursor. Never skip past a page you didn't read. Pages you already paid for are free to re-read inside the owned window.

Is there a free tier?

Yes. 500 credits a month plus 10 free calls. The balance is shared with InsightSocial's Chrome extension exports. See pricing for Pro and credit packs.

#api#instagram#developers#tutorial#python#nodejs