Amazon Ads Refresh Tokens: 365-Day Expiry Since July 30, No Reminder
Contents
Eleven days ago Amazon changed how long a refresh token for the Advertising API stays valid. If your integration authorised an advertiser on 1 August, that token stops working on 1 August 2027, no matter how often you use it in between. There is no email, no deprecation header, no warning in the console. The only party who will notice is you, at the moment a nightly export returns nothing. This article corrects a widely repeated wrong date and then builds the one thing that actually protects you: a token ledger with a cron job.
1. What changed, and the date everyone gets wrong
Amazon announced the change on 26 May 2026. It took effect on 30 July 2026. Refresh tokens issued on or after that date expire 365 days after the date the advertiser gave consent, not 365 days after the token was minted, and not 365 days after last use.
A wrong date is circulating persistently: 30 June. It shows up in forum posts, in vendor changelogs, and in search engine summaries that confidently answer “June 30” when you ask when Amazon refresh tokens started expiring. That is a full month off. If you planned a migration window around the end of June, or told a client their tokens were already covered, check your assumptions. The correct cut-off is 30 July 2026, and the first mass expiries will therefore land from 30 July 2027 onward.
- Announced: 26 May 2026.
- Effective: 30 July 2026, not 30 June.
- Lifetime: 365 days from the advertiser’s consent date.
- Recovery after expiry: a fresh advertiser consent, through an interactive dialog. There is no silent path back.
- Notification from Amazon: none.
2. Why using the token does not help
This is the part that catches experienced teams. In almost every OAuth deployment you have touched, a refresh token has either no expiry or a sliding one: each exchange for a new access token resets the idle timer, so an integration that runs every hour effectively never expires. Amazon’s clock does not slide. It is anchored to the consent date and runs to zero regardless of traffic. A pipeline that has refreshed successfully every 55 minutes for a year will fail on day 366 exactly like one that has been idle since day one.
The failure mode is therefore quiet and delayed. Your monitoring sees a healthy integration right up to the boundary, then a refresh call that no longer returns an access token, and the fix is not a retry or a redeploy. Someone at the advertiser has to click through a consent dialog again. On a Saturday, for a client in another timezone, that is not a five-minute recovery.
Worth noting in the same breath, since it hits the same codebases: six legacy account management endpoints have been closed to new use since July 2026 and will return HTTP 404 from July 2027. Their replacements have been generally available since 4 June 2026. Plan that migration in the same quarter as your consent renewals.
3. A token ledger on your side
Amazon will not tell you when a token dies, so store the only fact that matters, the consent date, and derive everything else. One table per environment is enough. The expiry column is generated, so it can never drift out of sync with the consent date.
CREATE TABLE amazon_ads_token_ledger (
advertiser_id text PRIMARY KEY,
account_name text NOT NULL,
region text NOT NULL CHECK (region IN ('na', 'eu', 'fe')),
consent_date date NOT NULL,
token_issued_at timestamptz NOT NULL,
expires_on date GENERATED ALWAYS AS (consent_date + 365) STORED,
last_refresh_ok timestamptz,
owner_email text NOT NULL,
status text NOT NULL DEFAULT 'active'
);
CREATE INDEX ON amazon_ads_token_ledger (expires_on) WHERE status = 'active';
Backfill it once, by hand if necessary. For every advertiser you onboarded on or after 30 July 2026, write the consent date you have in your onboarding records. For older tokens, record the date anyway and set status = 'legacy': the moment such an advertiser re-consents for any reason, the new rule applies to the replacement token, and you want the row ready.
4. The check script and the crontab line
The script prints nothing when everything is fine, which means cron stays silent. At 60 days it starts mailing you, and it exits non-zero once something has actually expired, so a wrapper or your monitoring can page on it.
#!/usr/bin/env python3
"""Warn about Amazon Ads consents approaching their 365-day limit."""
import sys
from datetime import date
import psycopg
DSN = "postgresql:///ops"
WARN_DAYS = 60
QUERY = """
SELECT advertiser_id, account_name, region, consent_date, expires_on,
expires_on - CURRENT_DATE AS days_left, owner_email
FROM amazon_ads_token_ledger
WHERE status = 'active'
AND expires_on - CURRENT_DATE <= %s
ORDER BY days_left
"""
def main() -> int:
with psycopg.connect(DSN) as conn:
rows = conn.execute(QUERY, (WARN_DAYS,)).fetchall()
if not rows:
return 0
print(f"Amazon Ads re-consent needed, checked {date.today()}:")
for aid, name, region, consent, expires, days, owner in rows:
state = "EXPIRED" if days < 0 else f"{days} days left"
print(f" {aid} {name} [{region}] consent={consent} "
f"expires={expires} {state} owner={owner}")
return 1 if any(r[5] < 0 for r in rows) else 0
if __name__ == "__main__":
sys.exit(main())
# crontab -e (run as the service user that owns the ops database role)
MAILTO=ads-ops@example.com
17 6 * * * /usr/bin/python3 /opt/ads/token_ledger_check.py
Sixty days is not arbitrary. Re-consent is a human process: you need to reach the right person at the advertiser, they need to find someone with the authority to approve, and that chain routinely takes four to six weeks. Update last_refresh_ok from your refresh job as well, so an unrelated breakage shows up in the same table.
5. Summary
You have built a ledger that records each advertiser’s consent date, derives the expiry as consent plus 365 days in the database rather than in application code, and a cron job that starts mailing you two months before anything breaks. Total footprint: one table, one index, forty lines of Python, one crontab entry. It replaces a class of outage that has no error budget, because the recovery path is a phone call rather than a deployment.
Two things to carry away. The cut-off is 30 July 2026, not 30 June, whatever a search summary tells you, so tokens issued from that day onward begin expiring on 30 July 2027. And usage does not extend the lifetime, which is why an integration that looks perfectly healthy today can be eleven months from a hard stop. Amazon documents the behaviour under access tokens in the Amazon Advertising API guides; treat that page as the source of truth and your ledger as the alarm clock Amazon does not provide.