GraphQL alternative

Instagram GraphQL API

Instagram's web app loads profiles, posts, and comments through an internal GraphQL endpoint. It isn't a public API: query IDs rotate, requests need session tokens, and it rate-limits hard. Here's how it works, and a stable REST API that returns the same public data.

50 free credits on signup · no card required

curl "https://api.profilequery.com/v1/profile?handle=nike" \
  -H "Authorization: Bearer $INSTAGRAMAPI_KEY"

What you get

Structured JSON, not raw HTML

  • ▹The same public data the GraphQL endpoint serves: profiles, posts, reels, comments, likers
  • ▹Stable, versioned REST paths instead of rotating doc_ids and query_hashes
  • ▹No session cookies, csrftoken, or x-ig-app-id headers to forge
  • ▹Flat, documented JSON instead of deeply nested edges/node wrappers
  • ▹Cursor pagination that works the same on every list endpoint
  • ▹Sessions, proxies, and retries handled for you

Under the hood

How Instagram's GraphQL endpoint works

Instagram's website doesn't use a public REST API. When you open a profile or scroll a feed, the browser sends POST requests to instagram.com/graphql/query (older builds used GET /graphql/query with a query_hash). Instead of sending a GraphQL query string, the client sends the ID of a persisted query plus a JSON blob of variables.

POST https://www.instagram.com/graphql/query
Content-Type: application/x-www-form-urlencoded
X-IG-App-ID: <web app id>
X-CSRFToken: <csrftoken cookie>
Cookie: csrftoken=...; sessionid=...; ...

doc_id=<numeric persisted query id>
&variables={"username":"nike", ...}

The response is a deeply nested GraphQL payload: data.user.edge_owner_to_timeline_media.edges[].node and similar, with pagination in page_info.end_cursor.

Why GraphQL scrapers keep breaking

  • ▹doc_id and query_hash values are build artifacts. They change when Instagram ships a new web bundle, and old IDs start returning errors.
  • ▹Most queries now need a logged-in session. Anonymous requests get redirected to the login wall or rate-limited after a handful of calls.
  • ▹Logged-in scraping puts the account at risk: checkpoints, challenges, and bans.
  • ▹Rate limits are per IP and per session, so scale means proxy pools and account rotation.
  • ▹Response shapes drift: fields move between edges, get renamed, or disappear without notice.

The same data over a stable REST API

This API sits in front of all of that. You call a versioned endpoint with an API key, and it handles sessions, proxies, query IDs, and retries, then returns flat JSON. Here's a profile's recent posts, the equivalent of the timeline-media GraphQL query:

import os, requests

r = requests.get(
    "https://api.profilequery.com/v1/profile/posts",
    params={"handle": "nike"},
    headers={"Authorization": f"Bearer {os.environ['INSTAGRAMAPI_KEY']}"},
)
data = r.json()["data"]
for post in data["items"]:
    print(post["shortcode"], post["like_count"], post["comment_count"])

# next page: pass data["next_cursor"] back as cursor=

GraphQL query → REST endpoint

  • ▹Profile info (user by username) → GET /v1/profile?handle=…
  • ▹Timeline media (profile posts) → GET /v1/profile/posts?handle=…&cursor=…
  • ▹Clips / reels tab → GET /v1/profile/reels?handle=…
  • ▹Followers / following edges → GET /v1/profile/followers, /v1/profile/following
  • ▹Post / shortcode media → GET /v1/post?post=…
  • ▹Comments on a post → GET /v1/post/comments?post=…
  • ▹Likers → GET /v1/post/likers?post=…
  • ▹Hashtag top / recent media → GET /v1/hashtag/top, /v1/hashtag/recent

Example response

What comes back

{
  "data": {
    "id": "13460080",
    "username": "nike",
    "full_name": "Nike",
    "biography": "Just Do It.",
    "is_verified": true,
    "is_private": false,
    "is_business": true,
    "category": null,
    "followers": 291952375,
    "following": 264,
    "posts": 1657,
    "external_url": "http://empli.fi/nike",
    "profile_pic_url": "https://instagram.com/.../nike.jpg",
    "public_email": null,
    "public_phone_number": null
  }
}

Response fields

Fields returned by GET /v1/profile

FieldDescription
idNumeric account id (stable even if the username changes).
usernameThe account's @handle.
full_nameDisplay name shown on the profile.
biographyThe profile's bio text.
is_verifiedWhether the account has a verified badge.
is_privateWhether the account is private.
is_businessWhether this is a business/professional account.
categoryBusiness category label when set (e.g. "Sportswear"); null otherwise.
followersNumber of accounts following this account.
followingNumber of accounts this account follows.
postsTotal number of posts on the profile.
external_urlThe clickable link in the bio, if any.
profile_pic_urlURL of the account's profile picture.
public_emailPublic contact email, if the account exposes one; null otherwise.
public_phone_numberPublic contact phone number, if exposed; null otherwise.

No login required

Nothing to authenticate but your API key

No OAuth flow

One static API key in an Authorization: Bearer header. No redirect, no refresh tokens, no expiring session.

No Meta app review

Never touch Meta's developer platform, Business Verification, or a connected Facebook Page. Sign up directly with Instagram API.

Public data only

Every endpoint returns only what's already visible on Instagram without logging in. Private accounts are never accessible.

Pricing

1 credit per request. Credits start at $8.00 per 1,000 and drop to $6.00 per 1,000 at volume — no subscription, no minimums. New accounts get 50 free credits.

See full pricing

FAQ

Instagram GraphQL API, explained

Is the Instagram GraphQL API public?

No. It's the private endpoint Instagram's own web app uses. It's undocumented, query IDs change without notice, and most queries require a logged-in session. Meta's only official public API is the Graph API (not GraphQL), which is limited to accounts you manage.

What happened to query_hash?

Older versions of the web app sent GET requests to /graphql/query with a query_hash. Newer builds send POST requests with a numeric doc_id. Both are persisted-query IDs that rotate with each web release, which is why hard-coded values eventually stop working.

Is the Instagram Graph API the same as GraphQL?

No. The Graph API is Meta's official REST-style API for business and creator accounts you've connected through a Facebook app. GraphQL here refers to Instagram's internal web endpoint. They have different data, different auth, and different rules.

Do I need an Instagram account or session cookie?

No. You authenticate with your API key only. There's no sessionid, csrftoken, or Instagram login on your side, and no account of yours is at risk.

How much does it cost?

Profile lookups are 1 credit; posts and comments pages are 2 credits. Credits start at $8.00 per 1,000 and new accounts get 50 free.

Start pulling graphql api data

Create a free key and make your first request in under a minute — 50 credits on us.