Skip to content
Schedule Social Media

Press Esc to close.

Meta access tokens: Instagram, Facebook Page and Threads

Meta access tokens for Instagram, Facebook Pages and Threads: what Meta's own code and Postman collections show, and the token lifetimes they leave unstated.

An Instagram long-lived access token is the token your app stores to keep calling the API after the person has logged in, but Meta's public GitHub sources (its Postman collections, SDKs and sample apps) contain no Instagram request to get or refresh one, and they state no lifetime for any Instagram token. What they do show in full is the Threads chain: code, short-lived token, long-lived token with an example expires_in of 60 days, then refresh. For Facebook Login they show how a user token becomes a Page access token.

This guide belongs to Meta API use cases: which one your app needs. The sections under the table name the Meta file behind each row, and the gaps are marked as gaps.

What this means for you

A scheduling tool connected to your Instagram, Facebook Page or Threads account holds a token that lets it post for you. Tokens expire or stop working, and then you have to connect the account again. Reconnecting runs the login once more, which gives the tool a new token.

The network pages for Instagram, Facebook and Threads list every API change we track for each.

The details: Instagram long-lived access token and other Meta tokens

Meta access token types, side by side

TokenHow your code gets it in Meta's GitHubUsed onLifetime in Meta's GitHubRefresh in Meta's GitHub
Instagram User access token (Instagram Login)From Business Login for Instagram; no request in the collectiongraph.instagram.comNot statedNone
Facebook User access tokenPOST /oauth/access_token with the login codegraph.facebook.comNot statedNone
Facebook Page access tokenGET /me/accounts with the user tokengraph.facebook.comNot statedNone
Threads short-lived user tokenPOST /oauth/access_token, grant_type=authorization_codeThreads hostNot statedExchange it for a long-lived one
Threads long-lived user tokenGET /access_token, grant_type=th_exchange_tokenThreads hostexpires_in 5,184,000 seconds (60 days) in an example responseGET /refresh_access_token, grant_type=th_refresh_token
Threads app access tokenGET /oauth/access_token, grant_type=client_credentialsThreads hostNot statedNone
System user access tokenPOST /{business-id}/system_user_access_tokensgraph.facebook.comAn option to expire in 60 days; otherwise not statedNone

The Instagram token: what Meta's sources show

Meta's Instagram Platform Postman collection lists one token for the Instagram API with Instagram Login: the Instagram User access token. It appears in the requirements for publishing, comments, messaging and insights, each time on graph.instagram.com with Business Login for Instagram. Several sections add that it must be "requested from a person who can manage" messages (or, in one section, comments) on the professional account.

The collection sets that token as a bearer token for every request, and its sample calls send it either as an access_token query parameter or in an Authorization: Bearer header.

What the collection doesn't have, on either login path: a request that exchanges a login code, swaps a short-lived token for a long-lived one, or refreshes a token. We checked the current file and every earlier version of it in the repository's history, plus Meta's Python Business SDK (v26.0.4). None of them has an Instagram exchange or refresh call. So this page can't give you the Instagram refresh endpoint, how soon a token may be refreshed, or how long it lasts. Take those from Meta's Business Login documentation, and store the expires_in your own exchange returns instead of hard-coding a number.

Two things from our changelog matter for older code:

For which login gives which token, see Instagram Login or Facebook Login.

Facebook Page access token

On the Facebook Login path, your app starts with a Facebook User access token and trades it for Page tokens. Meta's Facebook Reels sample shows the whole flow:

  1. Send the person to the OAuth dialog with response_type=code and your scopes.
  2. Exchange the code: POST https://graph.facebook.com/oauth/access_token with client_id, redirect_uri, client_secret and code. The response's access_token is the user token.
  3. Call GET /me/accounts with that token. Each Page in the response carries its own access_token.
  4. Publish to the Page with that Page token.

Meta's Instagram collection does step 3 with fields=name,access_token,tasks,instagram_business_account and describes the result as a "Page Access Token, which acts on behalf of a Page that is linked to an Instagram profile". If you already have the Page ID, it requests GET /{page-id}?fields=name,access_token,instagram_business_account instead. Its Facebook Login reels requests then publish with that Page token, while its insights section asks for the Facebook User access token. In the Python Business SDK the same edge is User.get_accounts (/accounts), and access_token is a field on the Page object.

Neither the Reels sample nor the collection swaps the user token for a long-lived one before reading Page tokens, and neither says how long a Page token lasts.

Threads long-lived token: exchange and refresh

The Threads API is the one Meta path where its GitHub shows the whole exchange and refresh sequence. The Postman collection's Authorization folder has all four calls, and the sample app runs the first three:

1. https://www.threads.net/oauth/authorize?response_type=code&client_id=...&redirect_uri=...&scope=...
2. POST /oauth/access_token
     client_id, client_secret, code, grant_type=authorization_code, redirect_uri
   -> { "access_token": "...", "user_id": "..." }        (short-lived)
3. GET /access_token?grant_type=th_exchange_token&client_secret=...&access_token=<short-lived>
   -> { "access_token": "...", "token_type": "bearer", "expires_in": 5184000 }
4. GET /refresh_access_token?grant_type=th_refresh_token
     (sent with the unexpired long-lived token)
  • Step 2: the collection calls the result a short-lived access token and says the redirect_uri must be the same URI you sent to the authorization window, "or the request will be rejected".
  • Step 3: 5,184,000 seconds is 60 days, but it's an example response in the collection, not a stated lifetime. The sample app runs this exchange straight after the code exchange and keeps the long-lived token.
  • Step 4: the collection says you can "refresh unexpired long-lived Threads User Access tokens", and that threads_basic "is sufficient for the exchange or refresh process". Its example response for the refresh is empty, so it doesn't show what a refreshed token's expires_in is, or how soon after issue you may refresh.
  • App token: GET /oauth/access_token?grant_type=client_credentials with your Threads app ID and secret. The example response is TH|1234567890|abcd1234 with token_type bearer.

The host is where Meta's own sources disagree: the collection uses graph.threads.net and www.threads.net, while the sample app moved to graph.threads.com and www.threads.com in March 2026. Our entry on the Threads API opening to all developers has the commit. Publishing with the token is covered in Threads API publishing.

System user tokens

For a business's own integrations, Meta's Python Business SDK has Business.create_system_user_access_token: POST /{business-id}/system_user_access_tokens with system_user_id, asset, scope, fetch_only and set_token_expires_in_60_days. The SDK names the 60-day option but doesn't say how long a token lasts without it.

Meta's WhatsApp tech provider sample describes one more kind: its app_only_install option "allows partners to access WABAs via API using a granular token (BISU), without creating a system user access token (SUAT)". The WhatsApp Cloud API basics guide covers why Meta's samples tell you to replace the temporary dashboard token with a system user token.

Debugging a token

Meta's Threads sample has an "Inspect Access Token" page. It calls GET /debug_token?input_token=<token> with the same token and reads application, expires_at, issued_at, scopes and user_id from the response's data. The Threads collection has the same request in its troubleshooting folder. It also points you to Meta's access token debugger to check that the token has the right permissions and "hasn't expired".

If your server sends the app secret, Meta's Python SDK adds an appsecret_proof parameter to every call: an HMAC SHA256 of the access token, keyed with the app secret.

What Meta's GitHub doesn't state

  • How long a short-lived token lasts on any Meta API.
  • The Instagram long-lived exchange and refresh calls, on either login path.
  • When a Threads token may be refreshed, and how long a refreshed token lasts.
  • Whether a Facebook Page token expires.
  • How long a system user token lasts without the 60-day option.
  • What revokes a token before it expires.

For the permissions each token needs before you go live, see Meta App Review: the minimum rules to go live.

What to do next

  • Business owners: When a tool says your Instagram, Facebook or Threads connection has expired, reconnect it from the tool's settings. If it happens again and again, ask the vendor how it refreshes tokens.
  • Marketers and agencies: Keep a list of which tools hold tokens for which client accounts, and reconnect from the account owner's login when one drops. For WhatsApp, ask whether the tool uses a system user token or a temporary one.
  • Developers: Store each token with the expires_in its exchange returned and check it with debug_token. On Threads, exchange for a long-lived token at login and refresh before expiry. On Instagram, take the exchange and refresh calls from Meta's Business Login documentation, because Meta's GitHub doesn't include them.

Questions people ask

How long does an Instagram long-lived access token last?

Meta's public GitHub sources don't say. The Instagram Platform Postman collection names the Instagram User access token for every Instagram Login call but gives no lifetime and no long-lived exchange. The only 60-day figure in Meta's GitHub is an example Threads response with expires_in set to 5,184,000 seconds.

How do I refresh an Instagram access token?

Meta's GitHub has no Instagram refresh request on either login path, in the current Postman collection or in its history. The Threads collection has one (GET /refresh_access_token with grant_type=th_refresh_token), but that is the Threads API on its own host, so don't assume its grant type works on graph.instagram.com.

How do I get a Facebook Page access token?

Call /me/accounts with a Facebook User access token and ask for the access_token field. Meta's Instagram collection requests name, access_token, tasks and instagram_business_account, and calls the token it returns a Page Access Token that acts on behalf of the Page. If you know the Page ID, GET /{page-id}?fields=name,access_token,instagram_business_account returns just that Page's token.

How long does a Threads long-lived token last?

The example response in Meta's Threads Postman collection shows expires_in of 5,184,000 seconds, which is 60 days. That is an example response, not a stated lifetime. The collection says you can refresh unexpired long-lived tokens and that threads_basic is enough to exchange or refresh one.

Why did my Meta access token stop working?

Meta's GitHub sources don't list what ends a token early. Check it with debug_token, as Meta's Threads sample does, and read expires_at and scopes. In a unit test in Meta's WhatsApp tech provider sample, a rejected token comes back as an OAuthException with code 190 and the message Invalid access token.

Sources

Related

Get an email when a platform changes its API

One email per new entry: what changed, who is affected, and the deadline.

Follow with RSS, or per platform, for example X or Meta.