fix(subscription): raise default OAuth usage poll interval to 300s

The Anthropic subscription window tracker polled GET /api/oauth/usage
every 10s by default and kept polling even when the proxy was idle
(via the cached OAuth token in ~/.claude/.credentials.json). On Pro
accounts this blew through Anthropic's metadata rate limit within
minutes and produced a steady stream of 429s.

Raise the default interval to 300s, raise the upper cap to 3600s, and
expose --subscription-poll-interval / --no-subscription-tracking CLI
flags (with matching HEADROOM_SUBSCRIPTION_POLL_INTERVAL and
HEADROOM_NO_SUBSCRIPTION_TRACKING env vars) so users can tune further
or turn the poller off entirely.

Fixes #242

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
chopratejas 2026-04-23 00:30:14 -07:00
parent 1b70e5c1b0
commit a1d9832e27
3 changed files with 30 additions and 4 deletions

View file

@ -57,6 +57,26 @@ from .main import main
@click.option("--no-optimize", is_flag=True, help="Disable optimization (passthrough mode)")
@click.option("--no-cache", is_flag=True, help="Disable semantic caching")
@click.option("--no-rate-limit", is_flag=True, help="Disable rate limiting")
@click.option(
"--no-subscription-tracking",
is_flag=True,
envvar="HEADROOM_NO_SUBSCRIPTION_TRACKING",
help=(
"Disable the Anthropic Claude Code subscription usage poller "
"(GET /api/oauth/usage). Env: HEADROOM_NO_SUBSCRIPTION_TRACKING."
),
)
@click.option(
"--subscription-poll-interval",
type=int,
default=None,
envvar="HEADROOM_SUBSCRIPTION_POLL_INTERVAL",
help=(
"Seconds between Anthropic subscription usage polls (13600, default 300). "
"Lower values give fresher /stats but risk 429s from Anthropic. "
"Env: HEADROOM_SUBSCRIPTION_POLL_INTERVAL."
),
)
@click.option(
"--retry-max-attempts",
type=int,
@ -254,6 +274,8 @@ def proxy(
no_optimize: bool,
no_cache: bool,
no_rate_limit: bool,
no_subscription_tracking: bool,
subscription_poll_interval: int | None,
retry_max_attempts: int | None,
connect_timeout_seconds: int | None,
anthropic_pre_upstream_concurrency: int | None,
@ -381,6 +403,10 @@ def proxy(
optimize=not no_optimize,
cache_enabled=not no_cache,
rate_limit_enabled=not no_rate_limit,
subscription_tracking_enabled=not no_subscription_tracking,
subscription_poll_interval_s=(
subscription_poll_interval if subscription_poll_interval is not None else 300
),
retry_max_attempts=retry_max_attempts if retry_max_attempts is not None else 3,
connect_timeout_seconds=connect_timeout_seconds
if connect_timeout_seconds is not None

View file

@ -218,7 +218,7 @@ class ProxyConfig:
# Subscription Window Tracking (Anthropic OAuth accounts)
subscription_tracking_enabled: bool = True
subscription_poll_interval_s: int = 10
subscription_poll_interval_s: int = 300
subscription_active_window_s: int = 60
# Stateless mode — disable all filesystem writes for read-only / container deployments

View file

@ -43,7 +43,7 @@ from headroom.subscription.models import (
logger = logging.getLogger(__name__)
_DEFAULT_POLL_INTERVAL_S = 10
_DEFAULT_POLL_INTERVAL_S = 300
_DEFAULT_ACTIVE_WINDOW_S = 60
_PERSIST_FILE_ENV = _paths.HEADROOM_SUBSCRIPTION_STATE_PATH_ENV
_DEFAULT_PERSIST_DIR = ".headroom"
@ -70,7 +70,7 @@ class SubscriptionTracker(QuotaTracker):
alongside the Codex and Copilot trackers.
Args:
poll_interval_s: Seconds between polls while active (1300).
poll_interval_s: Seconds between polls while active (13600, default 300).
active_window_s: Seconds since last notify_active call that keeps
polling alive (default 60 = 1 minute).
enabled: Set to ``False`` to disable tracking (mirrors
@ -92,7 +92,7 @@ class SubscriptionTracker(QuotaTracker):
client: SubscriptionClient | None = None,
) -> None:
self._enabled = enabled
self._poll_interval_s = max(1, min(poll_interval_s, 300))
self._poll_interval_s = max(1, min(poll_interval_s, 3600))
self._active_window_s = max(5.0, active_window_s)
self._persist_path = persist_path or _get_persist_path()
self._client = client or SubscriptionClient()