Skip to content

feat: expose subscription rate-limit snapshots via rate_limits output#1519

Open
alperenuzun wants to merge 1 commit into
anthropics:mainfrom
alperenuzun:feature/rate-limits-output
Open

feat: expose subscription rate-limit snapshots via rate_limits output#1519
alperenuzun wants to merge 1 commit into
anthropics:mainfrom
alperenuzun:feature/rate-limits-output

Conversation

@alperenuzun

Copy link
Copy Markdown

Closes #1518

What

When Claude Code runs with claude.ai subscription (OAuth) authentication, the SDK stream emits rate_limit_event messages carrying the subscription window state (status, utilization 0-100, resetsAt, window type). This PR captures the latest snapshot per window during the existing message loop and exposes it as a new rate_limits action output (JSON, keyed by window type), on both the main action and the standalone base-action.

This lets workflows that bill to a Pro/Max subscription report how full the 5-hour and weekly windows are (the same numbers the /usage panel shows) and alert before reviews start failing with 429s — see #1518 for the paths that don't work today (statusline hooks never fire in SDK mode, GET /api/oauth/usage is unreliable, response headers are unobservable from inside the action).

Example

- uses: anthropics/claude-code-action@v1
  id: review
  with:
    claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
    prompt: "..."

- name: Report subscription usage
  if: always()
  run: echo '${{ steps.review.outputs.rate_limits }}' | jq .

Output value:

{
  "five_hour": { "status": "allowed", "rateLimitType": "five_hour", "utilization": 25, "resetsAt": 1752684000 },
  "seven_day": { "status": "allowed_warning", "rateLimitType": "seven_day", "utilization": 91, "resetsAt": 1753029000 }
}

Design notes

  • Only stable SDK surfaces are usedrate_limit_event is part of the SDKMessage union that the run loop already iterates. The experimental usage control API (usage_EXPERIMENTAL_MAY_CHANGE_DO_NOT_RELY_ON_THIS_API_YET) was deliberately avoided.
  • The output is set even when the run fails, since hitting a rate limit is itself a common failure cause and that's when the data matters most. This mirrors how the execution file is written on the error path.
  • Unset for API key / Bedrock / Vertex runs — no rate_limit_event messages are emitted there, mirroring the SDK's own semantics (rate_limits_available: false).
  • Setting the output inside run-claude-sdk.ts (same pattern as execution-file.ts) covers both entrypoints — src/entrypoints/run.ts and the standalone base-action — without duplicating logic.
  • Snapshot objects are passed through as reported by the SDK (no field renaming), keyed by rateLimitType with latest-wins semantics when multiple events arrive for the same window.

Testing

  • Two new unit tests in base-action/test/run-claude-sdk.test.ts: latest-snapshot-per-window capture across multiple events, and no rate_limits output when no events are received.
  • bun test: 801 pass (root) + 154 pass (base-action), 0 fail.
  • bun run typecheck clean in both packages; bun run format:check clean.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose subscription rate-limit status (5h / weekly utilization) as an action output

1 participant