Skip to content

Instantly share code, notes, and snippets.

@tomo3141592653
Created March 11, 2026 04:49
Show Gist options
  • Select an option

  • Save tomo3141592653/816300f5777ff77b04a89265b7ac90f4 to your computer and use it in GitHub Desktop.

Select an option

Save tomo3141592653/816300f5777ff77b04a89265b7ac90f4 to your computer and use it in GitHub Desktop.
Auto-switch Claude Code model (opus/sonnet) based on remaining token usage
#!/usr/bin/env python3
"""
claude_model_switcher.py — Auto-switch Claude Code model based on token usage.
Reads remaining tokens from Anthropic API, then writes the chosen model
("opus" or "sonnet") into ~/.claude/settings.json.
Logic:
- Use opus if token_remaining% > 30 AND token_remaining% >= time_remaining%
- Otherwise use sonnet (to conserve tokens)
Usage:
python claude_model_switcher.py # switch and show result
python claude_model_switcher.py --dry-run # show decision, don't write
Setup:
Requires a valid ~/.claude/.credentials.json with a Claude Pro OAuth token.
"""
import json
import sys
import urllib.request
from datetime import datetime, timezone
from pathlib import Path
SETTINGS_FILE = Path.home() / ".claude" / "settings.json"
CREDENTIALS_FILE = Path.home() / ".claude" / ".credentials.json"
def fetch_usage() -> dict:
token = json.loads(CREDENTIALS_FILE.read_text())["claudeAiOauth"]["accessToken"]
req = urllib.request.Request(
"https://api.anthropic.com/api/oauth/usage",
headers={"Authorization": f"Bearer {token}", "anthropic-beta": "oauth-2025-04-20"},
)
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read())
def pick_model(data: dict) -> tuple[str, int, int]:
token_rem = 100 - round(data["seven_day"]["utilization"])
reset_at = datetime.fromisoformat(data["seven_day"]["resets_at"])
remaining_sec = max(0, (reset_at - datetime.now(timezone.utc)).total_seconds())
time_rem = round(remaining_sec / (7 * 24 * 3600) * 100)
model = "opus" if token_rem > 30 and token_rem >= time_rem else "sonnet"
return model, token_rem, time_rem
def set_model(model: str):
settings = json.loads(SETTINGS_FILE.read_text()) if SETTINGS_FILE.exists() else {}
settings["model"] = model
SETTINGS_FILE.write_text(json.dumps(settings, indent=2, ensure_ascii=False))
def main():
dry_run = "--dry-run" in sys.argv
try:
data = fetch_usage()
except Exception as e:
print(f"Error fetching usage: {e}", file=sys.stderr)
sys.exit(1)
model, token_rem, time_rem = pick_model(data)
print(f"token remaining: {token_rem}%, time remaining: {time_rem}% -> {model}")
if not dry_run:
set_model(model)
print(f"Written to {SETTINGS_FILE}: model = {model!r}")
else:
print("(dry-run, no changes written)")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment