CLAUDE LABJP
OPUS — Claude Opus 4.7 is generally available, improving software engineering, long-running coding, and higher-resolution visionAPIKEY — You can now set an expiration on API keys in the Console, with email reminders before keys valid for 7+ days expireREFLECT — A monthly recap at Settings > Reflect shows your top topics, most active day, and peak hour (beta)M365 — The Microsoft 365 connector now supports write tools for email, calendar, and OneDrive/SharePoint filesCOWORK — Cowork expands to web and mobile, bringing Chat and Cowork into one shared home across devicesDESIGN — Claude Design, a new Anthropic Labs product, lets you co-create designs, prototypes, slides, and one-pagersOPUS — Claude Opus 4.7 is generally available, improving software engineering, long-running coding, and higher-resolution visionAPIKEY — You can now set an expiration on API keys in the Console, with email reminders before keys valid for 7+ days expireREFLECT — A monthly recap at Settings > Reflect shows your top topics, most active day, and peak hour (beta)M365 — The Microsoft 365 connector now supports write tools for email, calendar, and OneDrive/SharePoint filesCOWORK — Cowork expands to web and mobile, bringing Chat and Cowork into one shared home across devicesDESIGN — Claude Design, a new Anthropic Labs product, lets you co-create designs, prototypes, slides, and one-pagers
Articles/API & SDK
API & SDK/2026-07-12Intermediate

When You Give an API Key an Expiration Date, Expiry Becomes a Plan Instead of an Accident

The Console now lets you set expiration dates on API keys. Here is how to fold planned expiry into unattended operations — with overlapping dual keys and a local expiry ledger — so your nightly jobs never go dark.

Claude API113API Keys2Automation36Security7Indie Dev20

One morning, a single one of my nightly jobs across several sites had stopped with a lone 401 in the log. The cause was mundane: months earlier I had tidied up a workspace, deleted an old key, and forgotten to swap it into one of the jobs. A key you delete by hand is hard to trace back later — which job used it, and when.

The Console now lets you set expiration dates on both API keys and Admin API keys. You can choose a preset, a custom date, or no expiry at all, and keys set to live longer than seven days get an email warning before they lapse. For an indie developer who leaves work to a model unattended, this quietly changes the relationship: instead of "a key might expire someday without warning," you get "this key expires on a date I know." The catch is that an expiring key, if you design around it carelessly, will stop your nightly job exactly on schedule. This article covers how to treat expiry as a plan rather than an accident.

Expiration turns "someday it lapses" into "it lapses on this date"

Until now, an API key lived until you or an admin explicitly deleted it. That sounds safe, but in practice it left the questions of "which key is old?" and "is a disused key sitting around unnoticed?" to human memory. An abandoned old key widens the damage if it ever leaks.

An expiration date inverts that relationship. The key lapses on the date you chose, and if you set it more than seven days out, you get an email as the deadline approaches. So instead of "it was already dead by the time I noticed," you get "I can see, seven days ahead, that it is about to lapse."

But there is a reality to unattended operation: relying on the notification alone is risky. Miss the email, and the job fails with a 401 the day the key expires. Handling expiry safely takes both a way to receive the warning and a way to swap the key before it lapses.

The fragile pattern is the "single key, one-shot swap"

The most breakable setup is holding a single production key, then, when the deadline arrives, creating a new one and deleting the old. That approach has a seam. In the few minutes between deleting the old key and reflecting the new one into your environment variables — or in any window where you forget to reflect it — the job fails to authenticate.

For unattended nightly runs, those few minutes of seam translate directly into "the whole night's work falls over." And the failure is silent; you do not notice until you read the log the next morning. Diagnosing the auth error itself is covered in How to Fix Claude API 401 Invalid API Key Authentication Error, but with expiration the focus shifts to not creating the seam in the first place.

Overlapping dual keys remove the seam

The basic move for removing the seam is to always hold two valid keys — one in active service, and one prepared as the next generation.

The steps go like this. When the active key (key-A) reaches seven days before expiry and the notification arrives, issue a new key (key-B) and give it an expiration date as well. Point your environment variable or secret store at key-B, run the nightly job once, and confirm it returns 200. Up to here key-A is still alive, so if the switch misbehaves you can fall back to it. Finally, watch key-B run for a day or two before revoking key-A.

This "overlap first, remove second" order is the crux. Delete the old key first and the whole point of dual keys evaporates. The scoped-rotation design itself is discussed in Stop the Bill Before It Balloons: Designing API Key Blast Radius for Unattended Pipelines. It helps to think of expiration as the mechanism that gives that rotation a deadline — an answer to "when do I do it?"

Don't wait for the Console email — let the job watch its own expiry

The notification email is useful, but a human receives it. Assuming the human will sometimes miss it, it pays to have the nightly job itself check how many days its key has left.

The approach is plain: when you issue a key, write its label and expiry date into a local ledger, and at the start of each nightly run, compute the days remaining. The Console API will not hand back a key's secret value later, but you can record the expiry date yourself.

import json
from datetime import date, datetime
from pathlib import Path
 
LEDGER = Path.home() / ".claude_key_ledger.json"
WARN_DAYS = 10  # margin so you notice before the Console's 7-day email
 
def check_key_expiry(active_label: str) -> None:
    """Call at the start of the nightly job. Warn when days remaining drop below the threshold."""
    if not LEDGER.exists():
        print("[key-check] No ledger found. Record expires_at when you issue a key.")
        return
 
    ledger = json.loads(LEDGER.read_text())
    entry = ledger.get(active_label)
    if not entry:
        print(f"[key-check] Label {active_label} is not registered in the ledger.")
        return
 
    expires = datetime.strptime(entry["expires_at"], "%Y-%m-%d").date()
    remaining = (expires - date.today()).days
 
    if remaining < 0:
        raise SystemExit(f"[key-check] {active_label} lapsed {abs(remaining)} days ago. Stopping.")
    if remaining <= WARN_DAYS:
        print(f"[key-check] Warning: {active_label} lapses in {remaining} days. Prepare the next key.")
    else:
        print(f"[key-check] {active_label} has {remaining} days left. All clear.")
 
# Example ledger (append when you issue a key)
# {
#   "prod-nightly-2026-07": {"expires_at": "2026-10-10", "note": "nightly across four sites"}
# }
 
if __name__ == "__main__":
    check_key_expiry("prod-nightly-2026-07")

Put this as the first step of the nightly job, and even if you miss the Console email, the log records the days remaining every night. The threshold (WARN_DAYS) sits ahead of the Console's seven-day notice so you keep a margin to act on your own side. If the key has already lapsed, the job stops rather than blindly generating a pile of 401s.

Which expiry length to choose — a per-use rule of thumb

Set the expiry length by the key's purpose and the effort of swapping it. Shorter shrinks the survival window if it leaks, but raises how often you overlap keys. Here is the rule of thumb I use.

PurposeSuggested expiryReasoning
Production nightly batch60-90 daysA quarterly overlap is manageable. Watch it twice, via the email and the ledger.
Testing / experiments7-30 daysThese tend to get abandoned. Keep them short so they clean themselves up.
CI / temporary automationAvoid "no expiry" if you canConsider dropping the key entirely and moving to keyless auth.
Local chat / drafting30-60 daysEven for personal use, build the habit of rotating on a schedule.

For CI and temporary automation, it is worth considering not holding a long-lived static key at all. The idea of leaning on short-lived credentials issued per request is covered in Drop Your Static Claude API Keys: Moving CI and Production to Keyless Auth with Workload Identity Federation. Expiration is the safety valve when you keep using static keys; keyless auth is the move that reduces the number of keys altogether — different roles.

Wrapping up — your next step

Expiration turns a key from "a seed of worry that might lapse someday" into "a managed thing with a known expiry date." But since a human receives the notification, unattended operation benefits from pairing the overlapping dual-key swap with a local expiry ledger, so your nightly jobs stay up.

As a first step, set an expiry on the key you use in production today, write that date into a ledger, and add the script above to the front of your nightly job. Next time you rotate, you can practice the overlap-before-remove order. It is a small piece of groundwork, but it cuts down on that quiet failure where a lone 401 is all that is left in the morning log.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

API & SDK2026-07-10
Don't Trust the Confidence Score: Per-Class Calibration and Abstain Routing for Vision Classification
Overall accuracy looked fine while individual categories quietly collapsed. Here is how I calibrated Claude Vision's self-reported confidence per class and routed abstentions to a human queue.
API & SDK2026-06-16
Confirm Your Model Actually Responds Before a Scheduled Run Begins
A model you configured can be gone before your nightly job even wakes up. Tell retirement, withdrawal, and regional restriction apart with a single startup probe, then rewrite the run config to an eligible model — with complete, working TypeScript.
API & SDK2026-05-12
I Ran 1,000 App Store Reviews Through Claude API — Here's What My Data Was Hiding
Lessons from 10+ years of indie app development and 50M+ downloads: how to use Claude API to batch-analyze App Store reviews, auto-generate improvement priorities, and fix the blind spots human reading creates.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →