Errors & blockers

YouTube Data API: invalid_grant, refresh token expired

Why a Google OAuth refresh token stops working and returns invalid_grant, and what actually needs to happen to restore the connection (hint: it isn't just retrying).

Quick answer

invalid_grant means the refresh token is permanently dead — retries, waiting, or restarts won't fix it; the channel owner must re-authorize through Google's consent screen. The four causes: the user revoked access, six months of refresh inactivity, exceeding the 50-refresh-tokens-per-client-per-user limit, or an OAuth client stuck in Testing publishing status, which expires every token after 7 days.

The YouTube Data API authenticates entirely through Google's general-purpose OAuth 2.0 system — there is no YouTube-specific token format or expiry rule. When a refresh call to https://oauth2.googleapis.com/token returns invalid_grant, Google is telling you the refresh token you sent is no longer usable, full stop. Unlike a normal 401 on an access token, there is no automatic recovery here: the refresh token itself is dead, and the only way to get a working connection back is to send the channel owner through Google's consent screen again from scratch.

PostGate handles OAuth, publishing and delivery evidence for every network through one API. Start free or read the docs.

01

What specifically kills a Google refresh token?

  • The user revoked access. Either directly from Google Account > Security > Third-party access, or as a side effect of changing their Google Account password in some account-security scenarios. This is the most common real-world cause and the one worth checking first.
  • Six months of inactivity. A refresh token that goes unused for six consecutive months expires automatically. Critically, calling the YouTube API with the resulting access token does not reset this clock — only an actual token-refresh request against the refresh token counts as activity, so an integration that mints one long-lived access token and never refreshes can still lose the refresh token silently in the background.
  • The 50-token-per-client-per-user limit. Google allows at most 50 live refresh tokens for a given OAuth client ID and Google Account combination. Once that ceiling is hit, issuing a 51st refresh token — which happens every time that user completes the consent flow again — silently invalidates the oldest one. Integrations that re-run the full OAuth flow on every reconnect (instead of reusing a stored token) hit this far more than expected, and the failure lands on whichever other, unrelated integration happened to be holding that oldest token.
  • The app is stuck in Testing publishing status. An OAuth client on Google Cloud Console with an External audience and "Testing" publishing status has every refresh token it issues expire after exactly 7 days, regardless of activity. This is easy to miss because everything works correctly in initial development and then starts failing a week after each test connection — the fix is moving the OAuth consent screen to "In production," which removes the 7-day ceiling.
02

What does not fix an invalid_grant?

Retrying the same refresh request will not succeed — the token is not in a transient bad state, it is permanently invalidated. Requesting a new access token with the same refresh token, waiting, or restarting your server all leave the underlying refresh token exactly as invalid as before. The only path back to a working connection is a fresh authorization: the channel owner has to go through Google's consent screen again, at which point your app receives a brand-new refresh token to store in place of the dead one.

03

How do you design an integration around this reality?

  • Refresh tokens on a real, predictable cadence — well inside six months — so the inactivity-expiry clock never has a chance to run out, and treat a scheduled refresh as the source of truth for "is this connection alive," not a wait-and-see approach.
  • Never re-run the full OAuth authorization flow just to get a fresh access token for a channel that already has a valid refresh token — that behavior is exactly what burns through the 50-token limit and can knock out someone else's working connection as a side effect.
  • Confirm the OAuth consent screen's publishing status is "In production" before shipping — a client left in Testing status will look completely fine for a week and then start failing every connection made during development or an early rollout.
  • Treat invalid_grant as a terminal, non-retryable state that requires user action, not a transient error to back off and retry.
04

How does PostGate handle this?

PostGate refreshes YouTube connections proactively on a fixed schedule rather than reactively on demand, and because PostGate's own hosted Google Cloud project runs in production publishing status, the 7-day testing-mode expiry never applies to a channel connected through PostGate. When a refresh does return invalid_grant — most often because the channel owner revoked access — PostGate marks the profile reauth_required instead of letting the next scheduled upload fail against a dead token. See profile health and the YouTube quota model, a separate mechanism that can also block an upload but for a completely different reason.

Frequently asked questions

Can I retry a Google OAuth invalid_grant error?

No. The refresh token is not in a transient bad state — it is permanently invalidated. Retrying the same refresh request, requesting a new access token with the same refresh token, waiting, or restarting your server all leave it exactly as invalid as before. The only path back is a fresh authorization: the channel owner goes through Google’s consent screen again and your app stores the brand-new refresh token it receives.

Why does my Google refresh token expire after exactly 7 days?

Because the OAuth client is stuck in Testing publishing status. A client on Google Cloud Console with an External audience and "Testing" status has every refresh token it issues expire after exactly 7 days, regardless of activity. Everything works in initial development and then fails a week after each test connection — the fix is moving the OAuth consent screen to "In production," which removes the 7-day ceiling.

Does calling the YouTube API keep my refresh token alive?

No. A refresh token unused for six consecutive months expires automatically, and calling the API with the resulting access token does not reset that clock — only an actual token-refresh request against the refresh token counts as activity. Refresh on a predictable cadence well inside six months, and treat the scheduled refresh as the source of truth for whether the connection is alive.

What is Google's 50 refresh token limit?

Google allows at most 50 live refresh tokens per OAuth client ID and Google Account combination. Issuing a 51st — which happens every time the user completes the consent flow again — silently invalidates the oldest one. Integrations that re-run the full OAuth flow on every reconnect instead of reusing a stored token hit this far more than expected, and the failure lands on whichever unrelated integration held the oldest token.

How does PostGate handle a YouTube invalid_grant?

PostGate refreshes YouTube connections proactively on a fixed schedule rather than reactively on demand, and its hosted Google Cloud project runs in production publishing status, so the 7-day testing-mode expiry never applies. When a refresh does return invalid_grant — most often because the channel owner revoked access — PostGate marks the profile reauth_required instead of letting the next scheduled upload fail against a dead token.

Build it with PostGate

One API for every social platform your customers care about — sandboxed, documented, and ready today.

Start building Read the docs