/ services / api / usage.ts
usage.ts
 1  import axios from 'axios'
 2  import { getOauthConfig } from '../../constants/oauth.js'
 3  import {
 4    getClaudeAIOAuthTokens,
 5    hasProfileScope,
 6    isClaudeAISubscriber,
 7  } from '../../utils/auth.js'
 8  import { getAuthHeaders } from '../../utils/http.js'
 9  import { getClaudeCodeUserAgent } from '../../utils/userAgent.js'
10  import { isOAuthTokenExpired } from '../oauth/client.js'
11  
12  export type RateLimit = {
13    utilization: number | null // a percentage from 0 to 100
14    resets_at: string | null // ISO 8601 timestamp
15  }
16  
17  export type ExtraUsage = {
18    is_enabled: boolean
19    monthly_limit: number | null
20    used_credits: number | null
21    utilization: number | null
22  }
23  
24  export type Utilization = {
25    five_hour?: RateLimit | null
26    seven_day?: RateLimit | null
27    seven_day_oauth_apps?: RateLimit | null
28    seven_day_opus?: RateLimit | null
29    seven_day_sonnet?: RateLimit | null
30    extra_usage?: ExtraUsage | null
31  }
32  
33  export async function fetchUtilization(): Promise<Utilization | null> {
34    if (!isClaudeAISubscriber() || !hasProfileScope()) {
35      return {}
36    }
37  
38    // Skip API call if OAuth token is expired to avoid 401 errors
39    const tokens = getClaudeAIOAuthTokens()
40    if (tokens && isOAuthTokenExpired(tokens.expiresAt)) {
41      return null
42    }
43  
44    const authResult = getAuthHeaders()
45    if (authResult.error) {
46      throw new Error(`Auth error: ${authResult.error}`)
47    }
48  
49    const headers = {
50      'Content-Type': 'application/json',
51      'User-Agent': getClaudeCodeUserAgent(),
52      ...authResult.headers,
53    }
54  
55    const url = `${getOauthConfig().BASE_API_URL}/api/oauth/usage`
56  
57    const response = await axios.get<Utilization>(url, {
58      headers,
59      timeout: 5000, // 5 second timeout
60    })
61  
62    return response.data
63  }