npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lenadweb/ai-limits

v1.2.0

Published

CLI and TypeScript SDK to access AI agent limits and usage stats across multiple providers.

Readme

ai-limits

npm version license node

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 Quota                 │ ████████░░ 78%     │ in 2h 14m          │
│ 7-Day Quota                  │ ███░░░░░░░ 31%     │ in 5d 3h           │
│ 7-Day Fable Quota            │ █████░░░░░ 52%     │ in 5d 3h           │
└──────────────────────────────┴────────────────────┴────────────────────┘

Supported providers

| 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.

Installation

Install globally to use the CLI anywhere:

npm install -g @lenadweb/ai-limits

Or add it to a project to use the SDK:

npm install @lenadweb/ai-limits

Requires Node.js 18 or newer.

CLI usage

Show usage

# 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 antigravity

Each 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 login

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 antigravity

SDK usage

import { 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);

Available methods

| 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. |

Response shapes

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;
    windowSeconds?: number;
    resetAfterSeconds?: number;
    resetTime?: string | null;
    displayName?: string;
    // Set when the window covers only part of the account, e.g. Claude's weekly
    // limit for a single model: { model: "Fable", modelId?: string, surface?: string }
    scope?: { model?: string; modelId?: string; surface?: string };
  }>;
  credits?: {
    hasCredits: boolean;
    unlimited: boolean;
    overageLimitReached: boolean;
    balance: string | null;
    approxLocalMessages: number[] | null;
    approxCloudMessages: number[] | null;
  };
  rateLimitResetCredits?: {
    availableCount: number;
    applicableAvailableCount: number;
  };
  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.

Typed accessors per provider

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
await claude.getScopedWeeklyUsage("Fable"); // ModelUsage | null — the weekly limit for one model
await claude.listScopedWeeklyUsage();     // Record<bucketKey, ModelUsage> — every model-scoped limit

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(), getScopedWeeklyUsage(model), listScopedWeeklyUsage() | | 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.

Caching

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();

Custom configuration

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,
  },
});

How it reads credentials

This tool never asks for your passwords and never sends your tokens anywhere except to the matching provider's official API.

  • Claude: besides the session and account-wide weekly windows, Anthropic also reports weekly limits scoped to a single model, e.g. Fable. Which models an account reports is decided by Anthropic and changes as new ones ship, so they cannot be enumerated up front: each one becomes its own bucket, keyed 7d_<model>_quota and carrying scope.model. Read them with listScopedWeeklyUsage(), or filter perModel on scope. overallUsagePercent stays account-wide, since a scoped limit caps one model and cannot speak for the account. Accounts still on the older response shape keep exactly the buckets they had.
  • Claude: reads the token from the macOS Keychain entry Claude Code-credentials, or from ~/.claude/.credentials.json. Set useKeychain: false to force the file. When the OAuth token expires it is refreshed automatically through Anthropic's token endpoint and written back to the same credential store, so Claude Code stays logged in. Set autoRefresh: false to disable this.
  • 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_KEY environment variable, or the apiKey option.
  • OpenRouter: uses the OPENROUTER_API_KEY environment variable, or the apiKey option. Calls GET /api/v1/key to 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.

Development

npm install
npm run build      # bundle with tsup
npm run dev        # rebuild on change

License

MIT