Show your OpenAI Codex rate-limit reset credits with live expiry countdown A developer created a Python script that displays OpenAI Codex rate-limit reset credits with a live expiry countdown. The tool reads authentication from the Codex CLI's auth file and calls the OpenAI API to fetch credit data, showing available credits, next expiration, and a progress bar. | /usr/bin/env python3 | | | """Show your OpenAI Codex rate-limit reset credits. | | | Reads authentication from ~/.codex/auth.json created by the Codex CLI . | | | The auth file is never transmitted anywhere except OpenAI's own API. | | | """ | | | import argparse | | | import json | | | import urllib.request | | | from datetime import datetime, timedelta, timezone | | | from pathlib import Path | | | ANSI styles | | | BOLD = "\033 1m" | | | DIM = "\033 2m" | | | GREEN = "\033 32m" | | | YELLOW = "\033 33m" | | | RED = "\033 31m" | | | CYAN = "\033 36m" | | | RESET = "\033 0m" | | | API URL = "https://chatgpt.com/backend-api/wham/rate-limit-reset-credits" | | | def load auth auth path: Path - dict: | | | """Load and return the Codex auth JSON.""" | | | if not auth path.exists : | | | raise FileNotFoundError f"Auth file not found: {auth path}" | | | return json.loads auth path.read text | | | def fetch credits auth: dict - dict: | | | """Call the Codex rate-limit reset credits endpoint.""" | | | token = auth "tokens" "access token" | | | account = auth "tokens" "account id" | | | req = urllib.request.Request | | | API URL, | | | headers={ | | | "Authorization": f"Bearer {token}", | | | "ChatGPT-Account-ID": account, | | | "OpenAI-Beta": "codex-1", | | | "originator": "Codex Desktop", | | | }, | | | | | | with urllib.request.urlopen req as resp: | | | return json.loads resp.read | | | def parse dt iso: str | None - datetime | None: | | | if not iso: | | | return None | | | return datetime.fromisoformat iso.replace "Z", "+00:00" | | | def fmt dt dt: datetime | None - str: | | | if not dt: | | | return "n/a" | | | return dt.strftime "%b %d, %H:%M UTC" | | | def time left dt: datetime | None - str: | | | if not dt: | | | return "" | | | delta = dt - datetime.now timezone.utc | | | if delta <= timedelta 0 : | | | return f"{RED}expired{RESET}" | | | days = delta.days | | | hours, remainder = divmod delta.seconds, 3600 | | | minutes, = divmod remainder, 60 | | | if days 0: | | | return f"{GREEN}{days}d {hours}h left{RESET}" | | | if hours 0: | | | return f"{YELLOW}{hours}h {minutes}m left{RESET}" | | | return f"{RED}{minutes}m left{RESET}" | | | def progress bar available: int, total: int, width: int = 18 - str: | | | if total <= 0: | | | return f"{DIM}{'░' width}{RESET}" | | | filled = min width, max 0, int round available / total width | | | empty = width - filled | | | if empty: | | | return f"{GREEN}{'█' filled}{DIM}{'░' empty}{RESET}" | | | return f"{GREEN}{'█' filled}{RESET}" | | | def render credits data: dict - None: | | | credits = credits data.get "credits", | | | available = credits data.get "available count", 0 | | | earned = credits data.get "total earned count", 0 | | | total = len credits | | | print | | | print f" {BOLD}{CYAN}🐳 Codex Rate-Limit Reset Credits{RESET}" | | | print | | | print | | | f" {progress bar available, total }" | | | f" {BOLD}{available}{RESET}{DIM}/{total} available{RESET}" | | | f" · earned: {earned}" | | | | | | if not credits: | | | print f"\n {YELLOW}⚠️ No credits found.{RESET}\n" | | | return | | | sorted credits = sorted | | | credits, | | | key=lambda c: | | | parse dt c.get "expires at" or datetime.max.replace tzinfo=timezone.utc | | | , | | | | | | next expires = parse dt sorted credits 0 .get "expires at" | | | print | | | print | | | f" {BOLD}Next expires:{RESET} {fmt dt next expires } · {time left next expires }" | | | | | | print | | | for idx, credit in enumerate sorted credits, start=1 : | | | status = credit.get "status", "unknown" | | | expires = parse dt credit.get "expires at" | | | granted = parse dt credit.get "granted at" | | | if status == "available": | | | icon = "●" | | | status color = GREEN | | | elif status == "redeemed": | | | icon = "✓" | | | status color = DIM | | | else: | | | icon = " " | | | status color = YELLOW | | | print | | | f" {status color}{icon}{RESET} {BOLD} {idx}{RESET}" | | | f" {status color}{status}{RESET}" | | | f" · expires {fmt dt expires } · {time left expires }" | | | | | | print f" {DIM}granted {fmt dt granted }{RESET}" | | | print | | | def main - None: | | | parser = argparse.ArgumentParser | | | description="Show your OpenAI Codex rate-limit reset credits.", | | | | | | parser.add argument | | | "--auth", | | | type=Path, | | | default=Path "~/.codex/auth.json" .expanduser , | | | help="Path to your Codex auth.json file default: ~/.codex/auth.json ", | | | | | | args = parser.parse args | | | auth = load auth args.auth | | | credits data = fetch credits auth | | | render credits data | | | if name == " main ": | | | main |