A CLI and TypeScript SDK to check usage limits and quotas across multiple AI coding assistants from one place.
It reuses the credentials that the official tools already store on your machine, so for most providers there is nothing to configure. Run one command and see how much of your plan is left and when it resets.
Provider: CLAUDE
Overall Usage: ββββββββββ 78%
Next Reset: in 2h 14m
ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββ¬βββββββββββββββββββββ
β Model/Bucket β Usage β Reset Time β
ββββββββββββββββββββββββββββββββΌβββββββββββββββββββββΌβββββββββββββββββββββ€
β 5-hour window β ββββββββββ 78% β in 2h 14m β
β 7-day window β ββββββββββ 31% β in 5d 3h β
ββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββ΄βββββββββββββββββββββ
| Provider | Source | Where credentials come from |
|---|---|---|
| Claude | Anthropic / Claude Code CLI | macOS Keychain or ~/.claude/.credentials.json |
| ChatGPT / Codex | ChatGPT backend API | ~/.codex/auth.json |
| Gemini | Google Cloud Code Assist | ~/.gemini/oauth_creds.json |
| Antigravity | Google Cloud Code Assist | OAuth login built into this CLI |
| MiniMax | MiniMax OpenPlatform API | MINIMAX_API_KEY environment variable |
| OpenRouter | OpenRouter API key endpoint | OPENROUTER_API_KEY environment variable |
For Claude, ChatGPT and Gemini the credentials are created by the providers' own CLIs and IDE plugins. If those tools already work on your machine, this one works too. Antigravity is the only provider that needs an explicit login through this CLI.
OpenRouter has no plan-based quota. Instead it reports the per-key spend limit (e.g. $3 / month): if the key has a limit set, you get an overall usage bar and reset time; if the key is unlimited, the spend is shown for information only.
Install globally to use the CLI anywhere:
npm install -g @lenadweb/ai-limitsOr add it to a project to use the SDK:
npm install @lenadweb/ai-limitsRequires Node.js 18 or newer.
# All providers at once
ai-limits show
# A single provider
ai-limits show claude
ai-limits show chatgpt
ai-limits show gemini
ai-limits show minimax
ai-limits show openrouter
ai-limits show antigravityEach provider is printed with an overall usage bar, the next reset time, and a per-model or per-window breakdown when the provider exposes one.
Antigravity uses Google OAuth. Authenticate once and the tokens are cached locally:
# Open the browser and complete the OAuth flow
ai-limits login antigravity
# Remove the cached tokens
ai-limits logout antigravityimport { LimitsClient } from "@lenadweb/ai-limits";
const client = new LimitsClient();
// Usage for every provider, keyed by provider name
const all = await client.fetchAllUsage();
console.log(all.claude.overallUsagePercent);
// Usage for a single provider
const claude = await client.fetchUsage("claude");
console.log(claude.overallUsagePercent, claude.overallResetTime);| Method | Returns | Description |
|---|---|---|
fetchUsage(provider) |
StandardUsageResult |
Normalized usage for one provider. |
fetchAllUsage() |
Record<Provider, StandardUsageResult> |
Normalized usage for every provider in parallel. |
fetchSummary(provider) |
UsageSummary |
Compact status with flags and a ready to print line. |
fetchAllSummaries() |
Record<Provider, UsageSummary> |
Summaries for every provider. |
fetchRawUsage(provider) |
any |
The provider's raw API response, unmodified. |
fetchAllRawUsage() |
Record<Provider, any> |
Raw responses for every provider, errors captured per provider. |
getProvider(name) |
BaseProvider |
The underlying provider instance, for example to call login() on Antigravity. |
fetchUsage returns a normalized result that is the same for every provider:
interface StandardUsageResult {
provider: string;
overallUsagePercent: number | null;
overallResetTime: string | null; // ISO timestamp
perModel?: Record<string, {
usagePercent: number | null; // null for informational rows (e.g. OpenRouter spend)
remainingAmount?: number;
limitAmount?: number;
resetTime?: string | null;
displayName?: string;
}>;
error?: { code: "AUTH" | "API" | "CONN" | number; message: string };
}fetchSummary returns a smaller object that is handy for status bars and alerts:
interface UsageSummary {
provider: string;
overallUsagePercent: number | null;
overallResetTime: string | null;
isExhausted: boolean;
isRateLimited: boolean;
needsAuthentication: boolean;
formattedText: string;
}When a provider fails, fetchUsage resolves with the error field set instead of throwing, so a single broken provider never breaks the whole batch.
Instead of iterating perModel with string keys, each provider exposes named, typed methods for its specific windows. Get the typed instance with getProvider<T>(name). All accessors share the same cached fetch, so calling several in a row makes a single request.
import { LimitsClient, ProviderName, ClaudeProvider, OpenRouterProvider } from "@lenadweb/ai-limits";
const client = new LimitsClient();
const claude = client.getProvider<ClaudeProvider>(ProviderName.Claude);
await claude.getFiveHourUsage(); // ModelUsage | null
await claude.getSevenDayUsage(); // ModelUsage | null
const or = client.getProvider<OpenRouterProvider>(ProviderName.OpenRouter);
await or.getLimit(); // OpenRouterLimit | null β { amount, interval, used, remaining, usagePercent, resetTime }
await or.getMonthlySpend(); // number | null
await or.fetchDetails(); // OpenRouterUsage β structured limit + spend| Provider | Methods |
|---|---|
| Claude | getFiveHourUsage(), getSevenDayUsage(), getSonnetWeeklyUsage() |
| ChatGPT | getPrimaryWindow(), getSecondaryWindow() |
| MiniMax | getDailyUsage(), getWeeklyUsage() |
| Gemini | getModelUsage(modelId), getModels() |
| Antigravity | getModelUsage(modelId), getModels() |
| OpenRouter | getLimit(), getTotalSpend(), getDailySpend(), getWeeklySpend(), getMonthlySpend(), fetchDetails() |
Window accessors return ModelUsage | null (null when that window is absent). Every provider also inherits listBuckets() to discover the raw bucket keys.
Each provider caches its normalized usage internally, so several accessor calls in a row (for example getFiveHourUsage() then getSevenDayUsage()) make a single network request. The default TTL is 30 seconds.
Control it through config. Set cacheTtlMs globally or per provider; 0 disables caching entirely. A per-provider value overrides the global one.
// Global default for every provider
const client = new LimitsClient({ cacheTtlMs: 10000 });
// Disable globally, but keep a 60s cache for OpenRouter only
const client2 = new LimitsClient({
cacheTtlMs: 0,
openrouter: { apiKey: process.env.OPENROUTER_API_KEY, cacheTtlMs: 60000 },
});
// Force a fresh fetch on the next call
client.getProvider(ProviderName.Claude).clearCache();Every provider accepts overrides, which is useful for non standard credential locations, custom OAuth clients, or passing a key directly:
import { LimitsClient } from "@lenadweb/ai-limits";
const client = new LimitsClient({
antigravity: {
tokenPath: "/custom/path/antigravity_oauth.json",
clientId: process.env.ANTIGRAVITY_CLIENT_ID,
clientSecret: process.env.ANTIGRAVITY_CLIENT_SECRET,
},
claude: {
credentialsPath: "/custom/path/.credentials.json",
useKeychain: false,
},
chatgpt: {
authPath: "/custom/path/auth.json",
},
gemini: {
credentialsPath: "/custom/path/oauth_creds.json",
projectId: "your-gcp-project",
},
minimax: {
apiKey: process.env.MINIMAX_API_KEY,
},
openrouter: {
apiKey: process.env.OPENROUTER_API_KEY,
},
});This tool never asks for your passwords and never sends your tokens anywhere except to the matching provider's official API.
- Claude: reads the token from the macOS Keychain entry
Claude Code-credentials, or from~/.claude/.credentials.json. SetuseKeychain: falseto force the file. - ChatGPT / Codex: reads the access token and account id from
~/.codex/auth.json. - Gemini: reads Google OAuth credentials from
~/.gemini/oauth_creds.json. - Antigravity: runs a local OAuth flow and caches tokens in
~/.limits-streamdeck/antigravity_oauth.json. Tokens are refreshed automatically. - MiniMax: uses the
MINIMAX_API_KEYenvironment variable, or theapiKeyoption. - OpenRouter: uses the
OPENROUTER_API_KEYenvironment variable, or theapiKeyoption. CallsGET /api/v1/keyto read the key's spend limit and usage.
For Gemini and Antigravity the package uses the same public OAuth client identifiers that the official Google CLIs ship with. These are public desktop clients protected by PKCE, not private secrets. You can swap in your own client through the configuration options above.
npm install
npm run build # bundle with tsup
npm run dev # rebuild on changeMIT