#!/home/johan/clawd/.venv/bin/python3 """ Fetch Claude.ai usage limits using curl_cffi to bypass Cloudflare. Uses browser TLS fingerprinting to avoid bot detection. Usage: ./claude-usage-fetch.py # Human readable output ./claude-usage-fetch.py --json # JSON output ./claude-usage-fetch.py --save # Save to memory/claude-usage.json """ import json import sys import os from datetime import datetime, timezone from curl_cffi import requests # Config file for cookies (so they can be updated without editing script) CONFIG_FILE = "/home/johan/clawd/config/claude-cookies.json" USAGE_FILE = "/home/johan/clawd/memory/claude-usage.json" # Default cookies (will be overridden by config file if it exists) DEFAULT_COOKIES = { "sessionKey": "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", "cf_clearance": "00xy9IG_RYfnzJK9brttMzWZ7hBPeH_V76Kp2qfC.7c-1770013650-1.2.1.1-jA6mdGLMDMq_gfdcxo_hmt94pZmc_C6nN2Z6DDxsV2EtKnsfBWSrdI6dDeLV7wZkIvi3HbZeke.hJw1Blsd_oNN2dmYFnQukyZclGUgdJ.gkT00.rWmXWzrotSMlelMrk6YWXb32.cMEhjdUHTnPx0WBAC9vfH8ImYFf4uIqOOwI400sioTg5ILmaD22.OhsgdlexnYJiTGbqhqKENmlWZrsMZLtGQqxXoasvZOlp6o", "__cf_bm": "vDv1.m3wBtCMxGvuoUZUTugPoMMx2aey711bK86Vqdo-1770013650-1.0.1.1-Ygz_OiVEOHnTvQS1kHTw.e1iwqWD0OouyUs.RWKHXaRLUWovJ88vk94zjQNo8vMBNHtVKsDuigjntRSE8YcN5pe_Wlj_z2a5GNgPBW.dikw", "lastActiveOrg": "1ed7bd80-0068-464f-bf6c-9f3553bda3a6", } def load_cookies(): if os.path.exists(CONFIG_FILE): with open(CONFIG_FILE) as f: return json.load(f) return DEFAULT_COOKIES def fetch_usage(): cookies = load_cookies() org_id = cookies.get("lastActiveOrg", "1ed7bd80-0068-464f-bf6c-9f3553bda3a6") api_url = f"https://claude.ai/api/organizations/{org_id}/usage" try: resp = requests.get( api_url, cookies=cookies, impersonate="chrome", headers={ "Accept": "application/json", "Referer": "https://claude.ai/settings/usage", } ) if resp.status_code == 200: return resp.json() else: print(f"Error: HTTP {resp.status_code}", file=sys.stderr) if "challenge" in resp.text.lower() or "Just a moment" in resp.text: print("COOKIES_EXPIRED: Cloudflare challenge detected - cookies need refresh", file=sys.stderr) return None except Exception as e: print(f"Error: {e}", file=sys.stderr) return None def format_usage(data): """Convert API response to our standard format""" result = { "last_updated": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), "source": "api", } if data.get("five_hour"): result["session_percent"] = int(data["five_hour"]["utilization"]) result["session_resets"] = data["five_hour"]["resets_at"] if data.get("seven_day"): result["weekly_percent"] = int(data["seven_day"]["utilization"]) result["weekly_resets"] = data["seven_day"]["resets_at"] if data.get("seven_day_sonnet"): result["sonnet_percent"] = int(data["seven_day_sonnet"]["utilization"]) return result def main(): raw_data = fetch_usage() if not raw_data: print("Failed to fetch usage", file=sys.stderr) sys.exit(1) usage = format_usage(raw_data) # Always record to dashboard history try: import urllib.request record = json.dumps({ "timestamp": usage["last_updated"], "weekly_percent": usage.get("weekly_percent", 0), "session_percent": usage.get("session_percent", 0), }).encode() req = urllib.request.Request( "http://localhost:9200/api/claude-usage/record", data=record, headers={"Content-Type": "application/json"}, method="POST", ) urllib.request.urlopen(req, timeout=5) except Exception: pass # Dashboard might be down, don't fail if "--json" in sys.argv: print(json.dumps(usage, indent=2)) elif "--raw" in sys.argv: print(json.dumps(raw_data, indent=2)) elif "--save" in sys.argv: os.makedirs(os.path.dirname(USAGE_FILE), exist_ok=True) with open(USAGE_FILE, 'w') as f: json.dump(usage, f, indent=2) print(f"Saved to {USAGE_FILE}") print(f"📊 Weekly: {usage.get('weekly_percent', '?')}% used ({100 - usage.get('weekly_percent', 0)}% remaining)") else: print(f"📊 Claude.ai Usage") print(f" Session (5h): {usage.get('session_percent', '?')}%") print(f" Weekly (7d): {usage.get('weekly_percent', '?')}%") print(f" Sonnet only: {usage.get('sonnet_percent', '?')}%") print(f" ─────────────") print(f" Remaining: {100 - usage.get('weekly_percent', 0)}% weekly") if __name__ == "__main__": main()