Meta Graph API basics
Every Instagram and Facebook integration is built on the Meta Graph API. Four concepts are enough to understand it: token types, the appsecret_proof signature, permissions and rate limits. Instagram adds a choice of two paths — Facebook Login (Page token) and Instagram Login (no Page, Instagram User token) — and the token model depends on which one you pick.
Published: Updated:
The four core concepts
- User token
- Acts on behalf of a person and is used to list their Pages. A short-lived token is exchanged for a long-lived one.
- Page token
- Acts on behalf of one specific Page. On the Facebook Login path it powers Page posts and comments, plus Direct, publishing and comments of the linked Instagram account. On the Instagram Login path no Page is needed — an Instagram User token is used.
- appsecret_proof
- An HMAC-SHA256 of the access token keyed with the app secret. With “Require App Secret” enabled, calls without it fail — a leaked token is useless without the app secret.
- Permission
- Each action needs its own permission: pages_show_list, pages_read_engagement, instagram_manage_messages (instagram_business_manage_messages with Instagram Login), leads_retrieval and others.
Why App Review exists
Under Standard Access, permissions only work for people with a role on the app (admins, developers, testers). To serve anyone else, each permission needs Advanced Access, granted after App Review and Business Verification. It is the longest stage.
Practical advice: request only the permissions you genuinely need. Every extra one lengthens the review and raises the chance of rejection.
Second piece of advice: prepare a clear, reproducible scenario — the reviewing team needs to see with their own eyes how the permission is used.
Rate limits and how to live with them
The Graph API throttles requests: app-level calls are capped at 200 × number of users per hour, while Pages, Instagram and Marketing API calls use Business Use Case limits. Usage comes back in the X-App-Usage and X-Business-Use-Case-Usage headers; once a limit is hit, calls fail with codes 4, 17, 32, 613 or 800xx.
The right behaviour is retrying with exponential backoff, capping concurrency and caching repeated data. The SDK retries with backoff (RetryPolicy) and raises RateLimitError on codes 4/17/32/613; its retry_after is filled only from a Retry-After header. Meta reports the wait as estimated_time_to_regain_access in the X-Business-Use-Case-Usage header.
Check before you start integrating
- The Instagram account is professional (Business or Creator); on the Facebook Login path it is linked to a Facebook Page (Instagram Login needs no Page).
- The app secret is stored safely and appsecret_proof is being added.
- The permission list is kept to a minimum.
- There is a mechanism for tracking token expiry.
- Backoff and queuing are planned for rate limits.
- Requests pin an explicit Graph API version, and its expiry date is tracked.
Frequently asked questions
Are the Graph API and Instagram API different things?
Not exactly. The Instagram API is part of the Meta platform and is set up in a Meta developer app, but it has two paths: Facebook Login (Page token, Page required) and Instagram Login (Instagram User token, no Page). The token model depends on the path.
Is appsecret_proof mandatory?
It depends on your app settings, but adding it always is the right call for production. The SDK does it automatically.
Does the user have to log in again when a token expires?
An expired token cannot be renewed, so yes. Refresh before expiry instead: an Instagram Login token (60 days) can be refreshed once it is at least 24 hours old (ig.refresh_token); Page tokens obtained from a long-lived user token have no expiry date. You still need a “reconnect” state for revoked access.
How do I choose a Graph API version?
The version is part of the request path (for example /v26.0/me). Meta guarantees each version works for at least two years; calls to an expired version are redirected to the next supported one, so endpoints that changed may behave differently. In the SDK the version is set with graph_version=.