OAuth flows for every social media platform compared

Side-by-side breakdown of OAuth implementations across all eight platforms — token lifetimes, refresh mechanics, required scopes, and common pitfalls.

OAuth flows for every social media platform compared

Every platform uses OAuth. None of them agree on how.

If you are building a social media integration, you will authenticate with OAuth 2.0 on every platform. That is where the consistency ends. Token lifetimes range from 15 minutes to 60 days. Some platforms give you refresh tokens automatically, some require a specific scope, one does not offer them at all. One platform requires you to discover the authorization server dynamically per user.

This is the side-by-side comparison we wanted when we started building Postproxy.

The overview

PlatformAccess Token TTLRefresh Token TTLPKCERefresh Available
X (Twitter)2 hours6 monthsRequiredYes (requires offline.access scope)
Facebook1–2 hours / 60 daysN/ANoNo (uses token exchange)
Instagram1 hour / 60 daysN/ANoNo (uses token exchange)
LinkedIn60 days365 daysNoPartners only
TikTok24 hours365 daysMobile/desktop onlyYes
YouTube (Google)1 hourNo fixed expirationOptionalYes (requires access_type=offline)
Pinterest30 days365 daysNoYes
Bluesky (AT Protocol)5–60 minutes2 weeks to unlimitedRequiredYes

That table hides a lot of complexity. Here is what each platform actually requires.

X (Twitter)

Flow: OAuth 2.0 Authorization Code with PKCE (mandatory).

X requires PKCE on all OAuth 2.0 flows. You generate a code_verifier, derive a code_challenge using S256, and include it in the authorization request. Plain method is not accepted.

Endpoints:

  • Authorization: https://x.com/i/oauth2/authorize
  • Token: https://api.x.com/2/oauth2/token

Scopes for posting:

ScopePurpose
tweet.readRead tweets
tweet.writeCreate and delete tweets
users.readRead user profile
offline.accessReceive a refresh token
media.writeUpload media

Refresh mechanics: Access tokens expire after 2 hours. Refresh tokens last 6 months but are single-use — every refresh returns a new refresh token and invalidates the old one. If you fail to store the new token, the session is lost.

Pitfalls:

  • If you omit offline.access from the initial authorization, you get no refresh token. The user must re-authenticate every 2 hours
  • Confidential clients authenticate with HTTP Basic (client_id:client_secret base64-encoded). Public clients pass client_id in the request body
  • The free API tier returns 403 on POST /2/tweets even with correct scopes. You need at least the Basic plan ($200/month) for reliable write access

Facebook

Flow: OAuth 2.0 Authorization Code Grant.

Facebook does not use refresh tokens. Instead, it uses a token exchange model: you get a short-lived token from the OAuth flow, then exchange it server-side for a long-lived token.

Endpoints:

  • Authorization: https://www.facebook.com/v22.0/dialog/oauth
  • Token: https://graph.facebook.com/v22.0/oauth/access_token
  • Long-lived exchange: https://graph.facebook.com/v22.0/oauth/access_token?grant_type=fb_exchange_token

Scopes for posting:

ScopePurpose
pages_manage_postsPublish to Facebook Pages
pages_show_listList managed Pages
pages_read_engagementRead Page engagement data
business_managementManage business assets

Refresh mechanics: Short-lived tokens last 1–2 hours. Exchange them for long-lived tokens that last 60 days. Long-lived tokens can be re-exchanged once per day (must be at least 24 hours old). Page access tokens derived from a long-lived user token never expire.

Pitfalls:

  • You cannot get a long-lived token directly from the OAuth dialog. The exchange must happen server-side
  • Page tokens require a two-step process: get the user token, then call GET /{page-id}?fields=access_token
  • App Review is required for most permissions beyond public_profile. Getting pages_manage_posts approved requires screencasts and can take weeks
  • Graph API versions deprecate roughly every two years. Always pin to a specific version

Instagram

Flow: OAuth 2.0 Authorization Code Grant (via Facebook’s infrastructure).

Instagram uses the same token exchange model as Facebook. The Instagram Basic Display API was sunset in December 2024 — only Business and Creator accounts work via the API now.

Endpoints:

  • Authorization: https://www.facebook.com/v22.0/dialog/oauth (same as Facebook)
  • Short-lived token: https://api.instagram.com/oauth/access_token
  • Long-lived exchange: https://graph.facebook.com/v22.0/oauth/access_token?grant_type=fb_exchange_token

Scopes for posting:

ScopePurpose
instagram_basicRead profile and media
instagram_content_publishPublish content
pages_show_listRequired for Instagram Business accounts
business_managementManage connected business assets

Refresh mechanics: Identical to Facebook. Short-lived tokens (1 hour), exchanged for long-lived tokens (60 days), re-exchangeable once per day.

Pitfalls:

  • Instagram Business accounts are connected through Facebook Pages. You need both Facebook and Instagram permissions
  • instagram_content_publish requires App Review with screencasts demonstrating the full flow
  • The container-based publishing model means you interact with Facebook’s Graph API, not a separate Instagram API

LinkedIn

Flow: OAuth 2.0 Authorization Code Grant.

LinkedIn is straightforward to authenticate but has a significant limitation: most developers do not get refresh tokens.

Endpoints:

  • Authorization: https://www.linkedin.com/oauth/v2/authorization
  • Token: https://www.linkedin.com/oauth/v2/accessToken

Scopes for posting:

ScopePurpose
openidOpenID Connect (required for new apps)
profileBasic profile info
emailEmail address
w_member_socialPost on behalf of a member
w_organization_socialPost on behalf of an organization (partner approval required)

Refresh mechanics: Access tokens last 60 days. For most developers, that is the end of the story — when it expires, the user goes through the full OAuth flow again. If the user is still logged into LinkedIn, the consent screen is skipped and the redirect happens silently. Programmatic refresh tokens (365-day TTL) are only available to approved partners.

Pitfalls:

  • Scopes are tied to “Products” in the developer portal. You must apply for “Share on LinkedIn” before w_member_social becomes available
  • r_liteprofile and r_emailaddress were deprecated in August 2023. New apps must use openid, profile, email
  • Requesting different scopes than previously granted invalidates all existing access tokens for that user
  • Authorization codes expire in 30 minutes
  • Access token strings can be up to 1,000 characters. Plan your storage accordingly

TikTok

Flow: OAuth 2.0 Authorization Code Grant. PKCE is required for mobile and desktop apps, optional for web server apps.

Endpoints:

  • Authorization: https://www.tiktok.com/v2/auth/authorize/
  • Token: https://open.tiktokapis.com/v2/oauth/token/

Scopes for posting:

ScopePurpose
user.info.basicBasic user profile (added by default)
video.uploadUpload video content
video.publishPublish uploaded videos
video.listRead user’s video list

Refresh mechanics: Access tokens expire after 24 hours. Refresh tokens last 365 days. Each refresh may return a different refresh token — you must always store the newly returned token.

Pitfalls:

  • video.upload and video.publish are separate scopes. Both are needed to post. Uploading alone does not publish
  • Authorization codes are URL-encoded in the callback. You must decode them before exchanging for tokens
  • The redirect_uri in the token request must exactly match what was used in the authorization request
  • TikTok uses client_key as the parameter name, not client_id
  • Both scopes require TikTok approval for the Content Posting API, which can take multiple review rounds

YouTube (Google)

Flow: OAuth 2.0 Authorization Code Grant.

Google’s OAuth is well-documented but has subtle behavior around refresh tokens that catches most developers.

Endpoints:

  • Authorization: https://accounts.google.com/o/oauth2/v2/auth
  • Token: https://oauth2.googleapis.com/token

Scopes for posting:

ScopePurpose
youtube.uploadUpload videos
youtubeFull YouTube account management
youtube.force-sslRead/write over SSL
youtube.readonlyRead-only access

All scopes are prefixed with https://www.googleapis.com/auth/.

Refresh mechanics: Access tokens expire after 1 hour. Refresh tokens have no fixed expiration for published production apps, but expire if unused for 6 months, if the user revokes access, or if the maximum of 50 refresh tokens per user per client is exceeded (oldest are silently invalidated).

Pitfalls:

  • Google only issues a refresh token on the first authorization. If you lose it, you must pass prompt=consent to force re-consent
  • access_type=offline is not the default. Omitting it means no refresh token
  • Apps in “Testing” mode on Google Cloud Console have tokens that expire in 7 days and are limited to 100 test users
  • YouTube API has a daily quota of 10,000 units by default. A single video upload costs 1,600 units, so you get roughly 6 uploads per day before requesting a quota increase
  • Scope verification/audit is required for sensitive scopes and can take weeks

Pinterest

Flow: OAuth 2.0 Authorization Code Grant.

Endpoints:

  • Authorization: https://www.pinterest.com/oauth/
  • Token: https://api.pinterest.com/v5/oauth/token

Scopes for posting:

ScopePurpose
boards:readRead boards
boards:writeCreate, update, delete boards
pins:readRead pins
pins:writeCreate, update, delete pins
user_accounts:readRead user account info

Refresh mechanics: Access tokens last 30 days. Refresh tokens last 365 days. The token endpoint uses HTTP Basic authentication (client_id:client_secret base64-encoded).

Pitfalls:

  • Scopes are comma-separated in the authorization URL, not space-separated. This differs from most OAuth implementations
  • The token endpoint requires Basic auth in the header, not client credentials in the POST body
  • Rate limits are per-user and restrictive for bulk operations

Bluesky (AT Protocol)

Flow: OAuth 2.0 Authorization Code with mandatory PKCE (S256), mandatory DPoP (Demonstrating Proof-of-Possession), and mandatory Pushed Authorization Requests (PAR).

This is the most complex OAuth implementation of the group by a significant margin.

Endpoints: Dynamic per user. Bluesky is a federated protocol, so the authorization server depends on which PDS (Personal Data Server) hosts the user’s account. You discover endpoints by resolving the user’s handle to their PDS, then fetching /.well-known/oauth-authorization-server.

Scopes:

ScopePurpose
atprotoRequired base scope for all AT Protocol operations
transition:genericTransitional scope with full read/write (same as App Password)
transition:chat.bskyAccess to chat/DM features

Granular per-Lexicon scopes are being rolled out but not yet finalized for production use.

Refresh mechanics: Access tokens last 5–60 minutes depending on server configuration. Refresh tokens are rotated on every use. Every token request requires a valid DPoP proof — a signed JWT proving possession of the client’s private key. The server issues DPoP nonces that must be included in subsequent proofs.

Pitfalls:

  • DPoP is mandatory, not optional. Most generic OAuth libraries do not support it
  • PAR is mandatory. You must POST authorization parameters to the PAR endpoint first, receive a request_uri, then redirect the user with that URI. You cannot put parameters directly on the authorization URL
  • The client_id is a URL where your client metadata JSON is hosted publicly. Localhost development requires special http://localhost patterns
  • The authorization server is discovered dynamically per user, so your code must handle arbitrary endpoints
  • The transition:generic scope is explicitly temporary and will be replaced by granular scopes. Plan for migration
  • The Bluesky team recommends using @atproto/oauth-client-node or @atproto/oauth-client-browser rather than building from scratch

The pattern across platforms

A few things become clear when you lay these implementations side by side.

Token storage is not one-size-fits-all. Facebook uses token exchange with no refresh tokens. Google gives you effectively permanent refresh tokens but only issues them once. Bluesky rotates everything aggressively. LinkedIn gives most developers no refresh mechanism at all. A multi-platform token management layer has to handle all of these models.

Scope naming has no standard. X uses short names (tweet.write). Google uses full URLs (googleapis.com/auth/youtube.upload). LinkedIn uses prefixed names (w_member_social). Bluesky uses protocol identifiers (atproto). Pinterest uses resource-based names (pins:write). There is no pattern to memorize — you read the docs for each one.

App review is the real bottleneck. Most platforms require some form of app review or verification before you can access publishing scopes in production. Meta requires screencasts. TikTok requires multiple review rounds. Google requires scope verification. LinkedIn requires product approval. The OAuth implementation might take a day; getting approved to use it can take weeks.

What Postproxy handles

Postproxy manages OAuth for all eight platforms so your system does not have to:

  • Eight different OAuth flows with automatic token refresh and renewal
  • Token exchange for Meta platforms, PKCE for X and Bluesky, DPoP for Bluesky
  • Per-platform scope management and token storage
  • Silent re-authentication detection and user notification
  • Refresh token rotation tracking across platforms that rotate

Your system authenticates once through Postproxy. We handle every platform-specific OAuth implementation, token lifecycle, and renewal edge case.

Connect your accounts and start publishing through the Postproxy API.

Ready to get started?

Start with our free plan and scale as your needs grow. No credit card required.