August 2026 · Quota watch series

How to monitor GitHub personal access token expiration

GitHub’s documentation is thorough about setting an expiration on a personal access token and thin on what happens next: an email to the token owner’s inbox as the date approaches, and then a wall of 401s. That email goes to one person, not to the channel where your incidents live, and the person who created the token is rarely the person on call when the nightly sync starts failing. Here is how to see expiry coming, and how to catch the failure the moment it lands.

The manual route: GitHub tells you the expiry date on every call

Any authenticated API request made with a token that has an expiration set returns the exact expiry timestamp in a response header, github-authentication-token-expiration. You can read it without consuming any rate limit:

curl -sI https://api.github.com/rate_limit \ -H "Authorization: Bearer YOUR_PAT" \ | grep -i token-expiration

We point this at /rate_limit on purpose: it is the one authenticated endpoint that is free and does not count against your quota, which makes it the right target for anything that polls. A complete manual monitor is a daily job that reads the header, computes days remaining, and posts to Slack when it drops below seven. Note the header only appears when the token actually has an expiration; a classic PAT set to no expiration returns nothing, and your script should treat that as its own finding.

The trap in naive checkers: treating every non-200 as “token expired”. GitHub uses 401 for an invalid or revoked token, but 403 is ambiguous: it can mean the token lacks a permission, or that you have exhausted the rate limit and the token is perfectly fine. The tell is the x-ratelimit-remaining header: 403 with 0 remaining is a rate-limit problem, 403 with budget left is a permissions problem. We learned to split those cases building our own probe, because a monitor that pages you to rotate a healthy token is worse than no monitor.

The automated route: a dead man’s switch in one JSON block

Datasource Pulse’s pre-wired GitHub connector runs that exact free /rate_limit probe and turns the outcomes above into stable verdict strings:

{ "type": "credential", "id": "github-ci-pat", "label": "GitHub PAT · CI pipeline", "vendor": "github", "apiKey": "YOUR_GITHUB_PAT" }

An expired or revoked PAT comes back as state: failing with dead_token, a permissions downgrade surfaces as dead_token with a message naming the 403, and an exhausted rate limit is its own class, rate_limited, with the reset time in the alert. Healthy runs report remaining core and GraphQL budget, so quota creep is visible before it becomes a 403. One honest scope note: this fires when the token stops working or the budget runs dry, not days before an expiry date. That is what a dead man’s switch is for, and it also catches the two failures no calendar reminder ever will: an admin revoking the token early, and a permission being quietly removed from it.

Each check costs $0.02, billed per run through Apify with no subscription; one PAT checked daily is about $0.61 a month. The daily key and quota watchdog template for n8n emails you on anything unhealthy, and the Zapier and Make routes do the same with webhooks.

Which route should you take?

Honest answer, including about the competition. If your only need is advance reminders for source-control tokens, TokenTimer is a small product that does exactly that job for SCM tokens, and the header script above gets you the same thing for free. The strongest setup pairs prevention with detection: a seven-day heads-up from the expiration header, plus a daily liveness check that tells you the moment the token actually stops working, whatever the reason. Datasource Pulse earns its keep on the detection side, and because a GitHub PAT is rarely your only credential: the same one-block-per-check pattern watches your OpenAI keys, SerpAPI quota, Stripe keys, and the rest of the 35+ pre-wired vendors from a single daily run, with recovery notices when things come back.

And whichever route you take, trip the alarm once on purpose: run a check with a scrambled token, watch dead_token arrive where you expect it, then swap the real one back in. An alert you have never seen fire is a guess, not a monitor.

Datasource Pulse watches the data sources your product depends on: credentials, quotas, and scraper output quality. One alert when something degrades, one notice when it recovers. GitHub is one of 35+ pre-wired vendors, and any endpoint the list is missing is one generic_http block away.