How to get a Facebook Page access token
To get a Facebook Page access token, the user goes through Facebook Login and grants pages_show_list plus the pages_* permissions you need. Your server exchanges the short-lived user token for a long-lived one (about 60 days) and calls /me/accounts, which returns a token for each Page. Page tokens obtained this way have no expiration date, but they can still be invalidated.
Updated:
In short: how do you get a Page access token?
You never request a Page token directly — you derive it from a user token. The chain is: Facebook Login → code → short-lived user token → server-side exchange with fb_exchange_token for a long-lived user token → GET /me/accounts. Each Page in the response carries an access_token. Call /me/accounts with the long-lived token and the Page token has no expiration date.
The classic mistake is listing Pages with the short-lived token. The Page token then dies within hours and the integration "randomly" breaks the next morning.
The second mistake is reading "no expiration date" as "forever". Meta's docs say these tokens only expire or get invalidated under certain conditions — so your code must expect token errors and support reconnecting an account.
Meta token types and lifetimes
Who each token acts for and how long it lives, per Meta's documentation.
| Token | Acts on behalf of | Lifetime | How you get it |
|---|---|---|---|
| Short-lived user token | User | Typically 1–2 hours | Exchange the code after Facebook Login |
| Long-lived user token | User | About 60 days | Server-side grant_type=fb_exchange_token |
| Page token (from short-lived user token) | Page | Hours | /me/accounts with the short-lived token |
| Page token (from long-lived user token) | Page | No expiration date, but can be invalidated | /me/accounts with the long-lived token |
| System user token | Business portfolio (Business Manager) | Never expires, or 60 days with set_token_expires_in_60_days=true | Business Settings or POST /{system-user-id}/access_tokens |
| App token | App | For app settings and service calls | Server-side with the app secret, e.g. for debug_token |
Meta warns that these lifetimes may change without notice and tokens can expire early. Key your logic off API errors, not dates.
Getting a never-expiring Page token in 5 steps
Configure the app
Add Facebook Login in the Meta App Dashboard and register the exact redirect URI. In a Business-type app (Facebook Login for Business) permissions live in a configuration, and the login URL carries
config_idinstead ofscope.Send the user to login
Add a random
stateto the URL (CSRF protection) and keep it in the session. The minimum ispages_show_list; add the rest per use case (pages_manage_postsfor publishing, and so on).Exchange the
codeon the serverIn the callback, compare
state, exchangecodefor a short-lived user token, then usefb_exchange_tokento get a long-lived one. The app secret stays on the server — Meta requires server-side token generation anyway.Fetch the Page tokens
Call
GET /me/accountswith the long-lived user token. Each Page comes back withid,name,access_tokenand thetasksthe user can perform.Inspect and store encrypted
Check
expires_at,data_access_expires_atandscopesin the Access Token Debugger or viadebug_token. Store the token encrypted and keep it out of the frontend and logs.
Python: from login to Page token
With the Socialift SDK the whole flow is four methods: login_url, exchange_code, granted_scopes, pages.
import secrets
from socialift import Facebook
REDIRECT = "https://example.com/facebook/callback"
SCOPES = ["pages_show_list", "pages_read_engagement", "pages_manage_posts"]
def login_link(fb: Facebook) -> tuple[str, str]:
state = secrets.token_urlsafe(32) # keep it in the session
return fb.login_url(redirect_uri=REDIRECT, state=state, scopes=SCOPES), state
async def on_callback(code: str) -> None:
async with Facebook.from_env() as fb:
user = await fb.exchange_code(code, redirect_uri=REDIRECT) # ~60 days
granted = await fb.granted_scopes(user.access_token)
missing = set(SCOPES) - set(granted)
if missing:
raise RuntimeError(f"Declined permissions: {missing}")
for page in await fb.pages(user.access_token):
save_page(page.id, page.name, page.access_token) # encryptexchange_code performs two calls: code → short-lived token → long-lived token. The redirect_uri must match the login URL character for character.
granted_scopes reads what was actually granted via debug_token — users can untick permissions in the dialog.
Business-type apps pass config_id instead of scopes. save_page is your storage function: the SDK stores nothing.
Which permissions a Page integration needs
Dependencies from the Permissions Reference — request the dependencies along with the permission.
| Permission | What it allows | Depends on |
|---|---|---|
pages_show_list | List the Pages a person manages | no |
pages_read_engagement | Page posts, photos, videos, follower data and metadata | pages_show_list |
pages_manage_posts | Create, edit and delete Page posts | pages_read_engagement, pages_show_list |
pages_read_user_content | Read user-generated posts, comments and ratings | pages_show_list |
pages_manage_engagement | Create, edit and delete comments on the Page | pages_read_user_content, pages_show_list |
pages_manage_metadata | Webhook subscriptions and Page settings | pages_show_list |
read_insights | Page insights | pages_read_engagement, pages_show_list |
yesno
Notes and limits
With Standard Access only people who hold a role on the app can grant these. Serving other people's Pages requires Advanced Access, which goes through App Review and Business Verification.
Permissions granted, still can't post? Check Page tasks
A Page token is limited by the user's tasks on the Page, not just by permissions. Per the Pages API docs: CREATE_CONTENT publishes as the Page, MODERATE replies to and deletes comments, ANALYZE views insights, ADVERTISE creates ads, MESSAGING sends messages and MANAGE assigns tasks.
Rule of thumb: store the tasks array from /me/accounts at connect time and surface it in your UI. If a teammate with only ANALYZE connects the Page, insights work and publishing fails — and that is not a bug in your code.
Why a Page token stops working
The Graph API distinguishes token problems by error_subcode. The common ones:
| Subcode | Cause | What to do |
|---|---|---|
| 460 | The user changed their password | Ask them to log in again and fetch new tokens |
| 463 | The token expired or was revoked | Re-login; delete the old token |
| 467 | Invalid token (revoked or expired) | Re-login |
| 458 | The user removed your app | Mark the connection as broken |
| 492 | The token owner lacks the required Page role | Ask a Page admin for a role or reconnect via another admin |
The top-level code is usually 190. The Socialift SDK raises code 190 as AuthError — catch it and move the account to a "reconnect required" state.
Data access expiration (90 days) is a separate clock
Beyond token lifetime, Meta has data access expiration: if a user hasn't been active in the last 90 days, they can still log in, but your app loses access to their data. This runs independently of token expiry.
Server-side you can read the date from data_access_expires_at in the debug_token response. To restore access, ask the user to re-grant permissions (auth_type=reauthorize).
System user tokens: server-to-server without a human
A system user is a programmatic identity inside a business portfolio. Its token doesn't depend on an employee's password or session, which makes it the right fit for automating your own company's Pages and ad accounts.
There are two kinds: the admin system user (creates other system users and assigns access — Meta advises against using it to work with assets) and the regular system user (only the assets it's been given). Standard Access allows 1 regular and 1 admin system user; Advanced Access allows 10 regular and 1 admin.
Tokens are generated in Business Settings or via POST /{system-user-id}/access_tokens. The app must belong to the same business, and the requested permissions must have passed App Review. A token either never expires or lasts 60 days with set_token_expires_in_60_days=true; Meta recommends expiring tokens for integrations and they are refreshed with fb_exchange_token. Revoke with /oauth/revoke.
Which to pick: a SaaS serving customers' Pages uses Facebook Login (Facebook Login for Business can also issue a Business Integration System User token, which never expires by default). Automation of your own business assets uses a system user.
Disconnecting: revoke the token properly
Deleting the token from your database isn't enough — the permission stays alive on Facebook's side.
from socialift import AuthError, Facebook
async def disconnect(user_token: str, account_id: int) -> None:
async with Facebook.from_env() as fb:
print("Granted:", await fb.granted_scopes(user_token))
try:
await fb.revoke(user_token) # DELETE /me/permissions
except AuthError:
pass # token already invalid — nothing left on Meta's side
delete_tokens(account_id) # remove user and Page tokens from your DBWhen all permissions are revoked, the person's user tokens are invalidated and they must grant permissions again on the next connect.
For manual checks, the Access Token Debugger (developers.facebook.com/tools/debug/accesstoken) shows a token's owner, expiry and permissions.
Production checklist
- Derive Page tokens only from a long-lived user token.
- Check
granted_scopesand show declined permissions in your UI. - Store tokens encrypted; keep them out of the frontend, logs and error reports.
- Send
appsecret_proofwith every call (the SDK adds it automatically). - On error 190, flag the account as "reconnect required" and notify the owner.
- On disconnect, call
revokeand delete the tokens.
Frequently asked questions
Is there a Facebook Page access token that never expires?
Yes: a Page token obtained with a long-lived user token has no expiration date. It is still invalidated by a password change, a lost Page role or the app being removed, so you must watch for token errors.
Why does my Page token expire after an hour or two?
It was fetched with a short-lived user token. Exchange the user token for a long-lived one with fb_exchange_token on your server, then call /me/accounts again.
Can I use a token from Graph API Explorer in production?
Explorer is a testing tool. In production, tokens come from an OAuth flow on your server: the app secret must never reach the browser, and every customer gets their own token.
What's the difference between a system user token and a Page token?
A Page token comes from the permission of a person who manages the Page and breaks if that person loses their role. A system user token belongs to the business portfolio, isn't tied to an employee and is designed for automating your own business assets.
Does deleting the token from my database revoke access?
No. The permission stays on Facebook's side. Call DELETE /{user-id}/permissions (fb.revoke in the SDK) and then delete the token.