Handling Token Expiry at Scale: Architecture for Thousands of Connected Accounts

Refresh load is driven by token lifetime, not account count. How to size the job, spread the herd you created at signup, and keep failures from amplifying.

Ronak Shah
Growth at Phyllo
October 12, 2026
Purple ticket with a mint refresh badge facing a blue server stack, illustrating OAuth token refresh at scale
Summarize this article with AI
GeminiChatGPTClaudePerplexityGrok

Refresh load is driven by token lifetime, not account count. How to size the job, spread the herd you created at signup, and keep failures from amplifying.

This is some text inside of a div block.
  • Refresh load comes from token lifetime, not account count: at 10,000 connections, TikTok can be 30% of accounts and 59% of refresh traffic.
  • Tokens issued together expire together, so a launch week of signups comes due again 2 months later.
  • Refresh at about 80% of lifetime, jittered 10% either way: on a 60 day token, 2,000 same-day refreshes become about 167 a day.
  • Make each refresh single-flight, since 2 workers refreshing 1 connection can invalidate the token you just stored.
  • Retry only failures you can fix, prompt reconnects for revoked connections, and alert on rates and age, not single failures.

Token refresh is easy with 10 connections and a cron job. It gets interesting somewhere around a few thousand, and the thing that breaks is rarely the refresh call itself.

What breaks is the shape of the load. Connections you onboarded together expire together, so a quiet system produces a spike 2 months later for reasons nobody remembers. And the platform with the fewest connections can easily generate the most traffic.

Here is how to size the job properly, spread the load, and handle the failures that no amount of retrying will fix. The per-platform lifetimes and refresh mechanics are in the token lifecycle guide, so this one is just about scale.

Where does the load actually come from?

Not from how many accounts you have. From how often each one needs refreshing, which is set by the platform.

Take 10,000 connections split across the major platforms and work out the daily refresh demand.

PlatformConnectionsToken lifeRefreshes per day
Instagram4,00060 daysAbout 67
TikTok3,00024 hoursAbout 3,000
YouTube2,000About 1 hourAbout 2,000 at a 12 hour cadence
LinkedIn1,00060 daysAbout 17
Total10,000About 5,083

TikTok is 30% of the connections and roughly 59% of the refresh traffic, because its access tokens last 24 hours. Instagram is 40% of the connections and about 1.3% of the traffic, because its long lived tokens last 60 days.

So capacity planning starts with your platform mix. Adding 1,000 TikTok creators costs 1,000 refreshes a day. Adding 1,000 Instagram creators costs about 17.

What is the herd problem?

Tokens issued at the same time expire at the same time, and your signup pattern decides when.

Say a launch brings in 2,000 Instagram creators in a week. Those tokens last 60 days, so 2 months later the same 2,000 refreshes come due inside a week. If 40% of the signups landed on 1 day, that is 800 refreshes in a single 24 hour window on a platform that normally needs 67.

It gets worse if you refresh at a fixed point in the lifetime, because the herd then recreates itself on every cycle. Refresh all 800 on the same day and they all expire on the same day again 60 days later.

Before sizing any of this, check which platforms your creators are actually on. Per-platform coverage is public. See the coverage list

How do you spread the load?

Jitter. Pick a random point inside a refresh window instead of a fixed percentage, and store it per connection.

import random

# Refresh at 80% of lifetime, +/- 10%. Store the target per connection
# so it stays stable across runs.

def schedule_refresh(conn):
    life_s = (conn.expires_at - conn.issued_at).total_seconds()
    frac   = 0.80 + random.uniform(-0.10, 0.10)   # 70% to 90%
    conn.refresh_at = conn.issued_at + timedelta(seconds=life_s * frac)
    return conn.refresh_at

# On a 60 day token that spreads each cohort across about 12 days.
# 2,000 same day signups -> roughly 167 refreshes a day instead of 2,000.
#
# 2 details that matter:
#   1. store refresh_at, do not recompute it each run, or the
#      randomness re-rolls and the schedule never settles.
#   2. Instagram will not refresh a token younger than 24 hours,
#      so clamp the lower bound above that.

The 70% to 90% band is deliberate. It leaves 6 days of margin at either end of a 60 day token. Anything tighter than 90% eats the buffer you need when a run fails.

How do you keep workers from fighting each other?

By making the refresh single flight per connection. On most platforms a refresh issues a new token and invalidates the old one, so 2 workers refreshing the same connection can race and leave you storing a token the platform has already replaced.

def refresh(conn_id):
    # 1. Take a short lock on the connection, not the whole job.
    with lock(f"refresh:{conn_id}", ttl=60) as got:
        if not got:
            return            # another worker has it. Not an error.

        conn = load(conn_id)

        # 2. Re-check after acquiring. It may already be done.
        if conn.refreshed_recently():
            return

        new = platform_refresh(conn.refresh_token)

        # 3. Write the new token and the next schedule together.
        with db.transaction():
            conn.apply(new)
            schedule_refresh(conn)

The re-check after the lock is the step people skip. Without it, a connection queued twice gets refreshed twice, and the second call can invalidate the first result.

Which failures are worth retrying?

Only the ones caused by your side or by transient conditions. The rest need the creator, and retrying them wastes quota and hides real problems.

FailureRetry?What to do
Network or 5xxYesExponential backoff with jitter. Cap the attempts
Rate limitedYes, laterBack off hard. On Meta, failed calls still consume quota
Expired access token, refresh token validYesThis is the normal path. Refresh and move on
Revoked by the creatorNoMark dead, surface it in your interface, prompt a reconnect
Refresh token expiredNoSame as revoked. The creator has to return
Scope withdrawnNoPartial function. Degrade the interface rather than failing

Row 2 is the one that bites at scale. A spike of failures triggers retries, retries consume quota, and on Meta a failed request costs the same as a successful one. A naive retry loop turns a small incident into a quota outage, which is the same amplification problem we covered in webhooks versus polling.

Rows 4 and 5 are the ones that quietly eat capacity. Dead connections retried daily are pure waste, and at a few thousand connections they add up. Classification detail is in the token lifecycle guide.

What failure rate should you expect?

A non-zero one, permanently, and some of it is not yours to fix.

  • Google revokes refresh tokens carrying YouTube scopes when a user changes their account password. Nothing your app does causes it or prevents it, so across a few thousand channels it is a steady drip rather than an incident. More in the YouTube scope reference.
  • Creators disconnect apps in platform settings without telling you, and you find out when a call fails.
  • Instagram tokens that pass 60 days cannot be refreshed at all. If a run fails repeatedly on a connection near the end of its life, it dies permanently.
  • LinkedIn refresh is not available to every app, so some integrations have no refresh path and the 60 day expiry is a hard re-consent cycle. Detail in the LinkedIn scope reference.

The useful response is not to drive the number to zero but to measure your own baseline, then alert when it moves. A revocation rate that doubles in a week is a signal. A revocation rate above zero is just Tuesday.

What should you monitor?

  1. Refresh success rate per platform. Blended numbers hide the platform that broke.
  2. Connections past 80% of lifetime with no successful refresh. This is your early warning, and on Instagram anything past day 50 deserves a page.
  3. Revocation rate as a trend, not as events.
  4. Queue age. If refresh jobs are waiting longer than they used to, capacity is tightening before anything has failed.
  5. Dead connections not yet surfaced to the user. A revoked connection showing stale numbers is a product bug, and it is invisible unless you count it. The reconnect flow is covered in account connection UX.

Summary

Size the refresh job from your platform mix rather than your user count, because a 24 hour token generates 60 times the traffic of a 60 day one. Then look at your signup history, because that is what decides when the load arrives.

Add jitter so cohorts spread out instead of arriving together, refresh at around 80% of the lifetime so failures have room to retry, and lock per connection so workers do not invalidate each other.

After that, most of the work is telling apart the failures you can fix from the ones that need the creator to come back. Retry the first group, surface the second, and alert on rates rather than on events.

Want token refresh handled across every platform, including the herd and the jitter? Get a demo

How many token refreshes will my system need?

Divide each platform's connections by its token lifetime in days and add them up. 10,000 connections across the major platforms need about 5,000 refreshes a day, mostly from the shortest-lived tokens.

What is the thundering herd problem with OAuth tokens?

Tokens issued at the same time expire at the same time. 2,000 creators onboarded in a week produce 2,000 refreshes due in one week 60 days later, and refreshing them together repeats the spike.

How do you spread out token refreshes?

Add jitter. Pick a random point between 70% and 90% of the token lifetime and store it per connection. On a 60 day token that spreads a cohort across about 12 days.

When should you stop retrying a token refresh?

As soon as the failure needs the creator to act. A revoked or expired refresh token will never succeed, so retrying wastes quota. Mark the connection dead, surface it, and prompt a reconnect.

Why do connections fail even when my refresh job works?

Some revocations have nothing to do with you: creators disconnect apps in platform settings without notice, and platforms revoke tokens by their own rules. Measure your baseline and alert on changes.

Table of Content
See Phyllo in action
  • No Credit card required
  • GDPR and SOC Compliant
  • 30-min Onboarding
Book a Demo →

Be the first to get insights and updates from Phyllo. Subscribe to our blog.

Ready to get started?

Sign up to get API keys or request us for a demo