From ceb112777c651244eb82a64908be078db0696f6f Mon Sep 17 00:00:00 2001 From: xiangboit Date: Fri, 6 Mar 2026 07:55:11 +0800 Subject: [PATCH] fix(usage-api): change User-Agent to avoid Anthropic 429 rate limiting (#168) * fix(usage-api): change User-Agent to avoid Anthropic 429 rate limiting Anthropic applies stricter rate limits to non-official User-Agent strings. The current 'claude-hud/1.0' consistently receives 429 responses while 'claude-code/2.1' succeeds for the same token. Co-Authored-By: Claude Opus 4.6 * test(usage-api): cover user-agent constant --------- Co-authored-by: Claude Opus 4.6 Co-authored-by: Jarrod Watts --- src/usage-api.ts | 3 ++- tests/usage-api.test.js | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/usage-api.ts b/src/usage-api.ts index c4f9b72..347c78b 100644 --- a/src/usage-api.ts +++ b/src/usage-api.ts @@ -48,6 +48,7 @@ const CACHE_FAILURE_TTL_MS = 15_000; // 15 seconds for failed requests const KEYCHAIN_TIMEOUT_MS = 3000; const KEYCHAIN_BACKOFF_MS = 60_000; // Backoff on keychain failures to avoid re-prompting const USAGE_API_TIMEOUT_MS_DEFAULT = 15_000; +export const USAGE_API_USER_AGENT = 'claude-code/2.1'; interface CacheFile { data: UsageData; @@ -662,7 +663,7 @@ function fetchUsageApi(accessToken: string): Promise { headers: { 'Authorization': `Bearer ${accessToken}`, 'anthropic-beta': 'oauth-2025-04-20', - 'User-Agent': 'claude-hud/1.0', + 'User-Agent': USAGE_API_USER_AGENT, }, timeout: timeoutMs, agent: proxyUrl ? createProxyTunnelAgent(proxyUrl) : undefined, diff --git a/tests/usage-api.test.js b/tests/usage-api.test.js index bde6232..1c6c46b 100644 --- a/tests/usage-api.test.js +++ b/tests/usage-api.test.js @@ -9,6 +9,7 @@ import { getUsageApiTimeoutMs, isNoProxy, getProxyUrl, + USAGE_API_USER_AGENT, } from '../dist/usage-api.js'; import { mkdtemp, rm, mkdir, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; @@ -505,6 +506,10 @@ describe('getUsage', () => { }); }); +test('usage API user agent matches the working Claude Code identifier', () => { + assert.equal(USAGE_API_USER_AGENT, 'claude-code/2.1'); +}); + describe('getKeychainServiceName', () => { test('uses legacy default service name for default config directory', () => { const homeDir = '/tmp/claude-hud-home-default';