Works with all AI agents

Connect your AI agent

Choose your assistant and connect in seconds.

Add to Claude

Works on every Claude plan, including free. Full Claude guide

  1. 1

    Add to Claude

    One click from Claude’s connector directory.

  2. 2

    Sign up or sign in

    Takes seconds, then you’re sent straight back.

  3. 3

    Ask Claude

    “Plan and schedule my posts for next week.”

OAuth for Social Media APIs: The Developer's Guide

Token lifetimes are where social publishing breaks. Authorization code flow, PKCE, refresh tokens and the specific reason you cannot store an access token for a post scheduled next week.

Jamie Partridge
Jamie Partridge
Founder··9 min read
OAuth authentication flow for social media APIs explained

Social media OAuth is not conceptually hard. Nearly every network implements the same authorization code flow, and once you have written it once the second is largely copy and paste.

What breaks projects is what happens afterwards. Access tokens expire on wildly different schedules, refresh tokens behave inconsistently, and one design decision that seems obvious will silently destroy your scheduling feature: storing the access token alongside the scheduled post.

This covers the flow, the token lifetimes that differ per platform, and the patterns that keep a publishing queue working past the first hour.

The Flow, Once

Every network here implements the authorization code flow from RFC 6749. Four steps.

One. Redirect the user to the platform's authorize endpoint with your client ID, redirect URI, requested scopes and a state value.

Two. They approve, and the platform redirects back to your URI with a short-lived code and your state echoed back. Verify the state matches what you sent. This is not optional decoration, it is your CSRF defence.

Three. Exchange the code server-side for tokens, sending your client secret. You get an access token, usually a refresh token, an expires_in, and the scopes actually granted.

Four. Call the API with Authorization: Bearer <access_token>.

The shape is identical on X, LinkedIn, Google and Discord. What differs is everything after step four.

Getting to the point where you can run this flow at all is a separate problem, covered in how to get social media API access. One detail people skip: check the scopes actually granted, not the ones you asked for.

Users can decline individual permissions on several platforms, so the token you receive may not do what your code assumes. Read the granted scopes from the token response and fail loudly rather than discovering it at publish time.

PKCE and Why It Is Not Optional Any More

PKCE, from RFC 7636, adds a one-time secret to the exchange. You generate a random code_verifier, send its SHA-256 hash as code_challenge on the authorize request, and send the original verifier when exchanging the code.

It exists because an intercepted authorization code is useless without the verifier. Originally aimed at mobile and single-page apps that cannot hold a client secret, it is now required or strongly recommended nearly everywhere, and X requires it for OAuth 2.0.

Implement it even on a server-side app where you technically have a secret. It costs a few lines and closes a real class of interception attack, and it is table stakes for any social media API integration handling other people's accounts.

Scopes Are Not Interchangeable

Requesting the wrong scope is the most common cause of a 403 that looks like a bug in your code.

Publishing scopes are consistently separate from read scopes, and on several platforms writing on behalf of an organization is separate again from writing as a person. LinkedIn distinguishes w_member_social from w_organization_social, and the organization one additionally requires the authenticated user to hold a specific role on the company page.

Ask for the narrowest set that does the job. Over-broad scope requests get rejected in app review, and they make the consent screen more alarming, which costs you conversions at exactly the wrong moment.

The per-platform requirements are mapped in our guide to getting social media API access.

Not the problem you want to own. PostEverywhere handles OAuth, refresh and revocation across all top platforms behind one API key. See the API docs.

Token Lifetimes by Platform

This is where the platforms diverge sharply, and where assumptions carried from one network break on the next.

Platform Access token Refresh
X (Twitter) ~2 hours Refresh token, rotates on use
Meta ~1 hour, exchangeable for ~60 days Re-exchange long-lived token
LinkedIn ~60 days Refresh token, partner-dependent
Google / YouTube ~1 hour Refresh token, long-lived
TikTok ~24 hours Refresh token
Pinterest ~30 days Refresh token
Telegram Never expires Static bot token
Discord webhook Never expires Static URL

Two things follow. There is no safe universal assumption. Code that works against Pinterest's 30-day token will fail against X's two hours. And the easy platforms are easy here too: Telegram and Discord webhooks hold static credentials that never expire, which is why they are the fastest to prototype against, as our Telegram and Discord guides show.

Meta's model deserves a note because it is genuinely different: you exchange a short-lived token for a long-lived one rather than using a conventional refresh token, and the long-lived token is itself re-exchanged before expiry.

The Scheduling Trap

Here is the design mistake that costs people a weekend.

You build a scheduler. A user connects their account, you receive an access token, and you store it on the scheduled post so the worker can publish later. It works perfectly in testing, because you always test by scheduling something a few minutes out.

Then a user schedules a post for next Tuesday. Tuesday arrives, the worker fires, and the token expired six days ago.

Never store an access token with a scheduled job. Store the refresh token against the account, and mint a fresh access token at publish time. On a platform with a two-hour token like X, anything scheduled more than two hours ahead is otherwise guaranteed to fail.

The same applies to retries. A post that fails and retries four hours later needs a fresh token, not the one captured when the job was created. Our scheduling API guide covers the surrounding queue design and the worker side.

Refresh Token Patterns That Work

Refresh at publish time, not on a timer. A cron job refreshing every token every hour wastes calls and multiplies failure modes. Mint on demand, immediately before you need it.

Handle rotation. Several platforms, X included, issue a new refresh token each time you use the old one and invalidate the previous. If you do not persist the new one atomically, the next refresh fails and the account disconnects.

This is a genuinely common bug and its symptom is "it worked for a while and then stopped".

Serialise refreshes per account. Two workers refreshing the same account concurrently will race, and with rotation one of them writes a refresh token the platform has already invalidated. Take a lock per account.

Never refresh in a loop on failure. A rejected refresh token will not become valid on the third attempt. Mark the account as needing reconnection and stop, or you will hit rate limits on top of an auth failure.

When Tokens Die Anyway

Even correct refresh logic loses tokens, because users do things.

Changing a password invalidates tokens on several platforms. So does revoking your app in account settings, losing the Page or organization role your permissions depended on, or a platform-side security action.

Meta permissions can also require periodic re-review, and LinkedIn's versioned APIs sunset on a schedule, so an untouched integration eventually breaks by itself.

Design for it rather than treating it as exceptional. Detect the auth failure, mark the connection as broken, stop retrying, and tell the user which account needs reconnecting and why.

The worst outcome is silently failing to publish while appearing healthy, which is what a naive retry loop produces. Our multi-account view exists largely to surface exactly this state.

Storing Credentials Safely

Refresh tokens are long-lived credentials to somebody else's account. Treat them accordingly.

Encrypt at rest with a key that is not in the same store as the data. Never log them, including in error handlers, which is where they most often leak.

Never expose them to the browser: the exchange and every refresh belong server-side. Scope database access so the publishing worker can read them and nothing else can.

A unified API removes most of this surface, but if you are building it yourself, revoke properly on disconnect. Deleting your row leaves a live token on the platform side, which is a bad answer to give a user asking whether you still have access. Call the platform's revocation endpoint.

If none of this is your product's core value, it is reasonable not to build it. A unified publishing API holds one credential per customer and handles every platform's token behaviour behind it.

Teams building agents typically use that or MCP rather than implementing eight refresh strategies, which our agent framework comparison goes into.

One key instead of eight token flows. Publishing across all top platforms, auth handled. From $9/mo with a 7-day trial and card required. See pricing or browse connectors.

FAQ: Social Media OAuth

Why do my scheduled posts fail with an auth error?

Almost always because an access token was stored alongside the scheduled job and expired before the job ran. Store the refresh token against the account instead and mint a fresh access token at publish time.

On X, where access tokens last around two hours, anything scheduled further ahead than that will otherwise fail.

How long do social media access tokens last?

It varies enormously. X is around two hours, Google and Meta around one hour, TikTok around 24 hours, Pinterest around 30 days, and LinkedIn around 60 days. Telegram bot tokens and Discord webhook URLs never expire.

There is no safe universal assumption, so handle each platform's lifetime explicitly.

What is PKCE and do I need it?

PKCE, defined in RFC 7636, adds a one-time code_verifier to the authorization code exchange so an intercepted code cannot be redeemed alone. It was designed for apps that cannot hold a client secret, but it is now required or strongly recommended nearly everywhere, and X requires it for OAuth 2.0. Implement it even server-side.

What is a rotating refresh token?

A refresh token that is replaced each time it is used, with the previous one invalidated. Several platforms including X do this. If your code does not persist the new refresh token atomically, the following refresh fails and the account disconnects, which typically presents as an integration that worked for a while and then stopped.

Why did my token stop working without my code changing?

Users invalidate tokens by changing passwords, revoking app access, or losing the Page or organization role your permissions relied on. Platforms also invalidate tokens for security reasons, Meta permissions can require re-review, and LinkedIn's versioned APIs sunset on a schedule.

Detect it, mark the connection broken, and prompt a reconnect rather than retrying.

Can I store an access token in the browser?

No. Access and refresh tokens are credentials to a user's social account and belong server-side only. The code exchange requires your client secret, which must never reach the browser, and every refresh should happen server-side too.

Use PKCE for public clients that genuinely cannot hold a secret.

Do I need separate scopes to post as a company page?

Usually yes. LinkedIn separates w_member_social from w_organization_social, and the organization scope additionally requires the authenticated user to hold an administrator or content admin role on the page. Meta similarly distinguishes Page permissions from user permissions. Request the narrowest scope that does the job.

How should I handle a failed token refresh?

Stop, do not retry in a loop. A rejected refresh token will not become valid on a later attempt, and retrying compounds an auth failure with a rate limit problem.

Mark the connection as needing reconnection, surface which account and why, and stop attempting to publish for it until the user reconnects.

Jamie Partridge
Written by Jamie Partridge

Founder & CEO of PostEverywhere. Writing about social media strategy, publishing workflows, and analytics that help brands grow faster.

Related articles

Developers

How to Get Social Media API Access on Every Major Platform

Getting approved to publish is harder than writing the code. Account types, permissions, app review and audits for Instagram, TikTok, LinkedIn, YouTube, X and more, plus which platforms need no approval at all.

11 min read
Developers

Social Media API Rate Limits Compared (2026)

Every major social API meters you differently: requests per window, points per hour, quota units per day, or a daily post cap. Here is what each one actually allows, with the figures from their own docs.

12 min read

Social media scheduler

Post everywhere from one dashboard

Schedule a month of posts in one sitting, tailor each one per platform, and let your AI agent handle the rest.

  • Unlimited posts to all top platforms
  • AI Studio for posts, images and video
  • Connect ChatGPT, Claude or any AI agent

7-day free trial on every plan. $0 due today, cancel anytime.

  • Lite

    2 social accounts

    $9/mo

  • Starter

    5 social accounts

    $19/mo

  • GrowthRecommended

    10 social accounts

    $29/mo

  • Scale

    20 social accounts

    $39/mo

Compare every plan