Skip to content

Repository files navigation

CCLimitPing icon

CCLimitPing (limitping)

English | 中文

License: MIT CI Release Go Platform

Start the next Claude Code or Codex rate-limit window the moment the previous one resets.

Claude Code and Codex subscription limits run on 5-hour rolling windows (plus a weekly cap). A fresh 5h window does not start just because the previous one reset; it starts when you send the first billable request. If that happens hours later, the gap is wasted and your window schedule drifts.

limitping watches the reset time and sends one tiny request through the official provider CLI right after rollover. Run it once, keep watch in the foreground, or start a detached bg watcher that keeps your window chain alive after the terminal closes.

claude  ✓ pinged (6.6s)
codex   ✓ pinged (14s, 19,426 tok (in 19,414 / out 12), $0.0023)

Highlights

  • Keeps 5h windows continuous by pinging as soon as a reset is safely available.
  • Runs the way you do: one-shot ping, foreground watch, or detached bg start with bg status, bg logs -f, and bg stop.
  • Shows 5h and weekly usage, reset countdowns, and background watcher state from read-only usage endpoints.
  • Triggers Claude Code and Codex through their official CLIs using your existing logged-in credentials.
  • Detects active Claude/Codex turns via CLI hooks, so a ping never interrupts a session that is already about to start the window itself.
  • Auto-resumes parked tasks: limitping continue <provider> proxies the official CLI and types your continue message the moment the 5h limit recovers, so an overnight task doesn't sit at the limit until morning.
  • Spends Codex reset credits before they lapse: limitping redeem cashes one in by hand, and auto_redeem = true lets watch / continue spend one on its own once it is close to expiring — a banked reset is worth nothing after it expires. Off by default, because redeeming is irreversible.
  • Types short: every command also works as lmp (e.g. lmp s, lmp w).
  • Includes dry-run modes, weekly-limit guards, reset buffers, cheap-model defaults, macOS notifications, local config, and no telemetry.

Quick start

curl -fsSL https://raw.githubusercontent.com/wavever/CCLimitPing/main/install.sh | sh
limitping config init
limitping status
limitping ping --dry-run
limitping watch                # foreground, low-power (Ctrl-C to stop)
# ...or run it in the background, freeing your terminal:
limitping bg start
limitping bg status
limitping bg logs -f

Use dry-run first if you want to inspect what would happen without consuming provider quota: limitping ping --dry-run, limitping watch --dry-run, or limitping bg start --dry-run.

Supported providers

Provider Read usage (zero-quota) Trigger Auth
Claude Code …/api/oauth/usage interactive Claude Code CLI OAuth (Keychain / ~/.claude)
Codex …/backend-api/wham/usage codex exec --ephemeral OAuth (~/.codex/auth.json)

How it works

Two cleanly separated jobs:

Job Mechanism Cost
Trigger a new window the official CLI (interactive Claude Code / headless codex exec) a tiny slice of quota (this is the point)
Read usage & reset times zero-quota usage endpoints (the same ones CodexBar / community plugins use) none — never starts a window

When watch sees a 5h window has reset, it first checks whether a Claude/Codex session is actively mid-turn. If one is, limitping waits and re-reads usage instead of sending its own ping, because that session's next model request will start the new window naturally. This check relies on the CLI hooks (installed automatically by the install script); without them, limitping skips the check and pings as soon as the window resets.

  • Claude: reads GET https://api.anthropic.com/api/oauth/usage using the OAuth token from the macOS Keychain (Claude Code-credentials) or ~/.claude/.credentials.json. Triggering uses a TTY-backed interactive claude "<prompt>" session, so it continues to start the Claude subscription-backed window after the headless print command moves to Agent SDK/API credits. If the usage endpoint returns an ambiguous 429, limitping uses the free token-counting endpoint (which does not create a Message) to distinguish a real endpoint throttle from Claude Code subscription access being disabled.
  • Codex: reads GET https://chatgpt.com/backend-api/wham/usage using the OAuth token from ~/.codex/auth.json. Triggering runs codex exec --ephemeral --json "<prompt>". --ephemeral is the reason the ping is headless: the interactive CLI cannot skip persisting a session, so every ping used to leave an "ok" conversation behind in codex resume and in the Codex Desktop thread list. --json makes the ping verifiable — its turn.completed event is the only local proof that a billable request went out, and it is where the reported token count and cost come from. The ping also runs with --disable hooks and --sandbox read-only: your hooks have no business firing for a synthetic session (it must not register itself as an active Codex session either), and nothing reviews what the model does on this path, unlike an interactive session with you at the keys.

Claude/Codex tokens are reused from the official tools (no separate login) and refreshed on 401.

Install

limitping ships as a single self-contained binary — no Go required.

One-line script (macOS / Linux):

curl -fsSL https://raw.githubusercontent.com/wavever/CCLimitPing/main/install.sh | sh

Downloads the right prebuilt binary from the latest release into /usr/local/bin (or ~/.local/bin). Override with LIMITPING_INSTALL_DIR.

Upgrade — replace the installed binary with the latest release:

limitping upgrade

upgrade checks first and says so if you are already current; --force reinstalls anyway. status, ping, bg status and continue also announce a new release before their own output, once per release:

✨ Update available!  0.9.0 -> 0.10.0
   Release notes: https://github.com/wavever/CCLimitPing/releases/latest

     1. Update now (runs `limitping upgrade`)
   ❯ 2. Skip
     3. Skip until next version

   ↑/↓ move · Enter confirm · Esc skip

Move with the arrow keys and confirm with Enter, the way the provider CLIs do it; the number keys still pick an option outright. The cursor starts on Skip because the notice interrupts the command you actually ran, so Enter, Esc and Ctrl-C all leave everything as it was. Option 3 records the release in ~/.config/limitping/version.json and stays quiet until the next one. The check runs at most once a day, never blocks for more than two seconds, and is skipped entirely without an interactive terminal — so --json, the hook callback and background watchers stay silent.

Aliases: limitping up, limitping update.

Uninstall — remove the installed binary plus config/cache:

limitping uninstall

Aliases: limitping rm, limitping remove.

Use limitping uninstall --keep-config to preserve ~/.config/limitping (or $XDG_CONFIG_HOME/limitping).

Manual download — grab the archive for your platform from the Releases page (.tar.gz for macOS/Linux, .zip for Windows):

tar -xzf limitping_darwin_arm64.tar.gz
sudo mv limitping /usr/local/bin/

Homebrew (macOS / Linux) — brew install wavever/tap/limitping (works once the Homebrew tap is set up — see .goreleaser.yaml).

From source (developers, needs Go 1.25+):

go install github.com/wavever/CCLimitPing/cmd/limitping@latest
# or, from a clone:
go build -o bin/limitping ./cmd/limitping

Each provider you enable needs its own credentials: the claude / codex CLIs logged in.

Usage

limitping config init          # write ~/.config/limitping/config.toml
limitping status               # show 5h/weekly % + reset countdowns (alias: s)
limitping status --json        # machine-readable JSON for each provider
limitping status -v            # also print raw JSON
limitping ping                 # trigger all enabled providers now (alias: p)
limitping ping claude          # Claude only
limitping ping codex           # Codex only
limitping ping --dry-run       # show the commands without sending
limitping watch                # foreground daemon: ping each window at reset (alias: w)
limitping watch claude         # watch only one provider (claude|codex)
limitping watch --live         # optional live heartbeat/status line
limitping watch --dry-run      # log when pings would fire, without sending
limitping schedule codex --at 05:00 --at 13:00  # ping at daily local times
limitping schedule --every 5h  # ping on a fixed interval instead of reset time
limitping redeem --dry-run     # show which Codex reset credit would be spent
limitping redeem               # spend it now (irreversible)
limitping continue codex       # proxy the CLI; auto-resume the task on 5h recovery
limitping continue codex --yolo             # flags after the provider pass through
limitping continue claude --dangerously-skip-permissions
limitping bg start             # run watch in the background, freeing the terminal
limitping bg status            # running? + each watched provider's usage (alias: limitping bg)
limitping bg logs -f           # follow the background watcher's log
limitping bg stop              # stop the background watcher
limitping hooks install        # install active-session detection hooks (claude|codex|all)
limitping hooks uninstall      # remove those hooks
limitping version              # print the version (aliases: v, ver)
limitping upgrade              # update to the latest GitHub release (aliases: up, update)
limitping uninstall            # remove limitping plus config/cache (aliases: rm, remove)

Short aliases are also available for config commands: limitping c i for config init and limitping c p for config path.

Command aliases

limitping --help lists aliases inline, for example ping, p, and its own Aliases: line shows both binary names so either one is discoverable from the other.

The binary itself has a short name too: the installer symlinks lmp next to limitping, so lmp status, lmp w, and limitping status are the same command. The installer skips the link if lmp already exists or resolves to another command on your PATH — a symlink in /usr/local/bin shadows anything it collides with. (Building from source? ln -s limitping /usr/local/bin/lmp.)

Command Aliases
status s, stat
ping p
watch w
schedule sched
redeem r
background bg
config c, cfg
config init c i
config path c p
version v, ver
upgrade up, update
uninstall rm, remove

ping shows the exact command and a live timer (a spinner on a terminal). The Codex ping reports the turn's tokens and an equivalent API cost, read from codex exec --json; Claude's interactive trigger session exposes no reliable machine-readable per-ping usage, so it shows elapsed time only:

claude  → claude --model haiku .
claude  ✓ pinged (6.6s)
codex   → codex exec --ephemeral --json --skip-git-repo-check --disable hooks --sandbox read-only -c model_reasoning_effort=low -m gpt-5.6-luna ok
codex   ✓ pinged (14s, 19,426 tok (in 19,414 / out 12), $0.0023)

ping and the watch log always name the model. In the rare case limitping cannot pick one — no catalog on disk, or no recognizable budget tier in it — the Codex CLI chooses instead, and the model is reported alongside the command so you still see what the ping spent quota on:

codex   → codex exec --ephemeral --json … -c model_reasoning_effort=low ok  (model: gpt-5.6-sol)

A ping ends by printing the same window view status gives, for the providers it pinged — a ping is far too small to move the used percentage, so its own output cannot show whether a window started. Reading usage costs nothing and never starts a window. --dry-run skips it: nothing was sent, so there is no new state to report.

Example status:

claude
  5h     [█████░░░░░]  51.0% used      resets in 3h14m    (Sun 00:10 UTC+8)
  weekly [█████░░░░░]  54.0% used      resets in 7h04m    (Sun 04:00 UTC+8)

codex (plus)
  5h     [██░░░░░░░░]  24.0% used      resets in 3h15m    (Sun 00:11 UTC+8)
  weekly [████░░░░░░]  37.0% used      resets in 111h57m  (Thu 12:53 UTC+8)
  reset credits 1 reset available
    - available, granted Jun 17 17:38, expires Jul 17 17:38 UTC+8 (in 24d6h)

Text status defaults to used percentage. Set usage_display = "remaining" if you prefer the same mental model as Codex's "Usage remaining" UI.

status --json returns the same data as a JSON array (one object per provider), for scripts and dashboards. Progress chatter is suppressed so stdout stays a single valid document; a provider that fails to read becomes {"provider": "...", "error": "..."} and the command exits non-zero. Add -v to embed each provider's raw response under raw.

A window key (five_hour / weekly) is omitted when the provider does not currently enforce that limit — e.g. OpenAI temporarily removed Codex's 5h window on 2026-07-12, leaving only the weekly cap. Text mode prints not currently enforced for such a window, and watch schedules its ping at the weekly reset instead of every 5h.

[
  {
    "provider": "codex",
    "plan": "plus",
    "five_hour": {
      "used_percent": 24,
      "remaining_percent": 76,
      "active": true,
      "resets_at": "2026-06-17T05:51:45+08:00",
      "remaining_seconds": 11700,
      "window_seconds": 18000
    },
    "weekly": {
      "used_percent": 37,
      "remaining_percent": 63,
      "active": true,
      "resets_at": "2026-06-24T00:51:45+08:00",
      "remaining_seconds": 403020,
      "window_seconds": 604800
    },
    "credits": { "has_credits": false, "unlimited": false, "balance": "0" },
    "reset_credits": {
      "available_count": 1,
      "credits": [
        {
          "status": "available",
          "granted_at": "2026-06-17T17:38:38Z",
          "expires_at": "2026-07-17T17:38:38Z"
        }
      ]
    },
    "limit_reached": false,
    "fetched_at": "2026-06-17T01:00:43+08:00"
  }
]

Configuration

~/.config/limitping/config.toml (honors $XDG_CONFIG_HOME):

weekly_threshold = 0.99   # skip pinging when weekly usage >= this (0..1), until weekly reset
reset_buffer     = "10s"  # wait this long after a reset before pinging (ensures rollover)
notify           = true   # macOS notifications on ping/skip/failure
usage_display    = "used" # text status: "used" or "remaining"

[claude]
enabled    = true
prompt     = "."
model      = "haiku"      # cheapest tier; triggering doesn't need a SOTA model
extra_args = []           # extra Claude CLI args; print/headless-only flags are ignored
align_start = ""          # optional RFC3339 anchor for the first window; empty = start ASAP
continue_prompt = "continue"  # message `continue` injects on 5h recovery; empty = "continue"

[codex]
enabled          = true
prompt           = "ok"
model            = ""     # empty = pick the cheapest model your plan offers
reasoning_effort = "low"  # "minimal" is rejected when web_search/image_gen tools are enabled
extra_args       = []     # extra Codex CLI args; exec-only flags such as --json are ignored
align_start      = ""
continue_prompt  = "continue"  # message `continue` injects on 5h recovery; empty = "continue"

Top-level keys:

  • weekly_threshold — when the weekly window is at/above this, watch stops pinging and waits for the weekly reset (unless usable credits exist).
  • reset_buffer — how long to wait after a window's reset time before pinging, so the window has definitely rolled over.
  • usage_display — whether text status / bg status renders each window as used percentage or remaining percentage.
  • align_start (per provider) — pin the phase of your windows: set to a future RFC3339 time to delay the very first ping until then; afterwards windows chain automatically every ~5h.

Why a cheap model

Triggering a window doesn't depend on the model — any billable request starts the 5h clock — so the ping uses each provider's cheapest model to eat the least of your budget:

  • Claude → haiku: also avoids the separate weekly Opus bucket.
  • Codex → empty, resolved per ping: limitping reads the Codex CLI's own catalog (~/.codex/models_cache.json) and picks the cheapest model your plan offers — the budget tier OpenAI marks "Fast and affordable", never your working model, which is usually a far pricier tier. Because the choice is made at ping time it survives OpenAI retiring and adding models. Name a model to pin one instead; a pinned model that has since been retired is rejected with the current list rather than failing as an opaque server error at rollover.

Claude/Codex don't expose per-model prices at runtime (Anthropic's local cost cache is empty; Codex's model cache has no price field), so the cheapest model is a sensible default rather than a live price lookup. Override model per provider if you prefer.

Active-session detection (hooks)

At a window reset, watch avoids pinging while you're actively working — that turn would start the next window on its own. This relies on CLI hooks, which the install script sets up for you. If they aren't installed, limitping skips the check entirely and pings right at reset (it never guesses from the process list).

The install script runs this automatically; to (re)install manually:

limitping hooks install        # both providers (or: limitping hooks install claude)

This registers limitping's hooks in ~/.claude/settings.json and ~/.codex/hooks.json (your existing settings are preserved; a .bak backup is written). The hooks invoke the hidden limitping hook <provider> command on UserPromptSubmit / PreToolUse / PostToolUse / Stop (Claude also SessionEnd) to record whether a session is mid-turn under ~/.config/limitping/activity/.

Note

Claude Code loads its hooks automatically — nothing to do there. Codex gates custom command hooks behind a one-time trust step: run /hooks inside Codex once to enable them. Remove everything later with limitping hooks uninstall (also done automatically by limitping uninstall).

Scheduled pings

Use schedule when you want wall-clock pings instead of reset-aligned window chaining. It keeps running in the foreground and fires ping at the next configured interval or daily local time:

limitping schedule codex --at 05:00
limitping schedule codex --at 05:00 --at 13:00 --at 21:00
limitping schedule --at 05:00,13:00,21:00
limitping schedule codex --every 5h --dry-run

You can combine --every and --at; whichever next occurrence comes first is used. --at values are daily local times in HH:MM or HH:MM:SS form.

Run watch in the background

watch runs in the foreground. To free your terminal, run it as a detached background process with the built-in bg command:

limitping bg start          # start watch detached from the terminal
limitping bg status         # running? pid, uptime, log + each provider's usage (alias: limitping bg)
limitping bg logs -f        # follow the watcher's log (-n N for last N lines)
limitping bg stop           # stop it

watch defaults to low-power log output. Add --live if you want a foreground heartbeat/status line. bg start takes the same optional [provider] argument and --dry-run flag as watch. Only one watcher (foreground or background) runs at a time, and background output is written to ~/.config/limitping/bg.log (honors $XDG_CONFIG_HOME). The process detaches into its own session, so it survives the shell closing — but it does not restart on reboot.

For start-at-login on macOS, use a launchd agent instead. Create ~/Library/LaunchAgents/com.limitping.watch.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>com.limitping.watch</string>
  <key>ProgramArguments</key>
  <array>
    <string>/ABSOLUTE/PATH/TO/limitping</string>
    <string>watch</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
  <key>StandardOutPath</key><string>/tmp/limitping.log</string>
  <key>StandardErrorPath</key><string>/tmp/limitping.err</string>
</dict>
</plist>
launchctl load ~/Library/LaunchAgents/com.limitping.watch.plist

Auto-continue a parked task

watch and bg keep your window chain warm, but they don't resume a task that has already stalled at the 5h limit. limitping continue <provider> does: it launches the provider's real interactive CLI through a PTY and passes your terminal straight through, so you drive Codex / Claude Code exactly as usual. In the background it polls usage and, the moment the 5h limit recovers after being hit, types your continue message into the session so a long task resumes itself instead of sitting parked until you come back.

limitping continue codex                       # drive Codex as usual; auto-resume on recovery
limitping continue codex --yolo                # flags after the provider pass through verbatim
limitping continue claude --dangerously-skip-permissions
  • The resume message is each provider's continue_prompt in config (default "continue"; set it to e.g. "继续任务"). Quit from inside the CLI to exit.
  • It only injects on a genuine recovery edge: the 5h window was maxed (or the endpoint reported limit_reached, or the CLI printed a limit message) and has since clearly reset, and the weekly window isn't also exhausted (per weekly_threshold, credits included) — so it won't resume straight into the weekly wall.
  • A diagnostic timeline is written to ~/.config/limitping/continue.log.
  • Unix only for now (needs a PTY); on Windows the command reports that it's unsupported.

Cost & caveats

  • See PRIVACY.md for local data handling and network behavior.
  • See SECURITY.md for vulnerability reporting and credential handling notes.
  • Triggering consumes a little quota (~one ping per 5h ≈ 33/week). The ping uses a minimal prompt and low reasoning, so the cost is tiny but non-zero.
  • The usage endpoints are unofficial and could change; they're read-only and isolated per provider for easy patching.
  • macOS-first: Keychain reads and notifications are macOS-only. Codex auth.json is cross-platform; Claude on Linux uses ~/.claude/.credentials.json; notifications are a no-op off macOS.

Layout

cmd/limitping            CLI entry
internal/config          TOML config
internal/usage           normalized usage model
internal/auth            Claude (Keychain) + Codex (auth.json) tokens
internal/provider        per-provider ReadUsage (endpoint) + Trigger (CLI)
internal/activity        hook-based active-session state (shared by the hook cmd + scheduler)
internal/pricing         pricing helpers for providers that expose token usage
internal/scheduler       the watch engine (sleep-until-reset, weekly-respect, backoff)
internal/notify          macOS osascript notifications
internal/cli             cobra commands: status, ping, watch, schedule, continue, background, config, hooks, upgrade, uninstall, version

Contributing

Issues and PRs are welcome. See CONTRIBUTING.md and CODE_OF_CONDUCT.md. Before submitting:

gofmt -l .        # should print nothing
go build ./...
go vet ./...
go test ./...

Providers are isolated in internal/provider behind a small Provider interface (ReadUsage + Trigger), so adding a new provider is mostly self-contained provider code plus wiring in internal/cli and internal/config.

Releasing is one command — push a tag, and GitHub Actions runs GoReleaser to build the cross-platform binaries and publish a Release:

git tag v0.10.0 && git push origin v0.10.0

Nothing else needs editing. The tag is the only place a version is written down: the release build stamps it in via -ldflags, and any other build derives its version from the module's build info, so there is no constant to bump and nothing that can drift from the tag. A local go build reports dev+<revision> and never offers to upgrade itself.

Release notes are generated from the commit log, so commit subjects are the release notes — write them as a line a user would want to read. There is no hand-maintained changelog to keep in sync; published notes live on the Releases page.

License

MIT © wavever

About

Keep your Claude Code / Codex 5h rate-limit windows back-to-back — auto-pings each provider the moment its window resets. | 在 5 小时限额窗口重置的瞬间自动触发计时,让 Claude Code / Codex 的窗口背靠背、不留空档。

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

40 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages