Skip to content

Instantly share code, notes, and snippets.

@withakay
Last active January 26, 2026 14:22
Show Gist options
  • Select an option

  • Save withakay/cd0a5972657fbb234f9cf04a1c1b8a29 to your computer and use it in GitHub Desktop.

Select an option

Save withakay/cd0a5972657fbb234f9cf04a1c1b8a29 to your computer and use it in GitHub Desktop.
Github Copilot Usage CLI

ghcp-usage

View your Github Copilot usage from the CLI

Requirements

  • Python3 - should work with just about any Python sunce 3.6
  • gh cli installed and authed - we use the gh cli auth token to fetch the usage data

Install

copy to somewhere in your $PATH and make it executable

#!/usr/bin/env python3
import argparse
import json
import subprocess
import sys
from datetime import datetime
from typing import Optional
API_PATH = "/copilot_internal/user"
def gh_api_json(hostname: Optional[str], timeout: int) -> dict:
cmd = ["gh", "api", API_PATH, "-H", "Accept: application/json"]
if hostname:
cmd += ["--hostname", hostname]
try:
p = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=timeout,
)
except FileNotFoundError as e:
raise RuntimeError("gh is not installed (or not on PATH).") from e
except subprocess.TimeoutExpired as e:
raise RuntimeError(f"Timed out after {timeout}s.") from e
if p.returncode != 0:
msg = (p.stderr or p.stdout or "").strip()
raise RuntimeError(msg or f"`gh api` failed (exit {p.returncode}).")
try:
return json.loads(p.stdout)
except json.JSONDecodeError as e:
raise RuntimeError("Failed to parse JSON returned by `gh api`.") from e
def parse_quota(data: dict):
"""Returns (used, total, remaining) or None if unlimited/unknown."""
snapshots = data.get("quota_snapshots", {})
premium = snapshots.get("premium_interactions", {})
if premium.get("unlimited"):
return None
total = premium.get("entitlement")
remaining = premium.get("remaining") or premium.get("quota_remaining")
if total is None or remaining is None:
return None
used = max(total - remaining, 0)
return used, total, remaining
def format_reset_date_ddmmyy(data: dict) -> Optional[str]:
s = data.get("quota_reset_date")
if isinstance(s, str) and s:
try:
return datetime.strptime(s, "%Y-%m-%d").strftime("%d/%m/%y")
except ValueError:
pass
s = data.get("quota_reset_date_utc")
if isinstance(s, str) and s:
try:
return datetime.fromisoformat(s.replace("Z", "+00:00")).strftime("%d/%m/%y")
except ValueError:
pass
return None
def parse_snapshot(snapshot: dict):
"""Returns (used, total, remaining), 'unlimited', or None if unknown."""
if not isinstance(snapshot, dict) or not snapshot:
return None
if snapshot.get("unlimited"):
return "unlimited"
total = snapshot.get("entitlement")
remaining = snapshot.get("remaining")
if remaining is None:
remaining = snapshot.get("quota_remaining")
if total is None or remaining is None:
return None
try:
total_f = float(total)
remaining_f = float(remaining)
except (TypeError, ValueError):
return None
used_f = max(total_f - remaining_f, 0.0)
def as_int_if_close(v: float):
if abs(v - round(v)) < 1e-9:
return int(round(v))
return v
return as_int_if_close(used_f), as_int_if_close(total_f), as_int_if_close(remaining_f)
def format_quota_value(parsed) -> str:
if parsed == "unlimited":
return "unlimited"
if not parsed:
return "unknown"
used, total, _remaining = parsed
try:
pct = int((float(used) / float(total)) * 100) if float(total) else 0
except (TypeError, ValueError, ZeroDivisionError):
pct = 0
return f"{used}/{total} - {pct}%"
def metrics_from_parsed_snapshot(parsed):
if parsed == "unlimited":
return {"status": "unlimited"}
if not parsed:
return {"status": "unknown"}
used, total, remaining = parsed
try:
pct = int((float(used) / float(total)) * 100) if float(total) else 0
except (TypeError, ValueError, ZeroDivisionError):
pct = 0
return {
"used": used,
"total": total,
"remaining": remaining,
"pct": pct,
}
def format_metrics_value(metrics: dict) -> str:
status = metrics.get("status")
if status == "unlimited":
return "unlimited"
if status == "unknown":
return "unknown"
used = metrics.get("used")
total = metrics.get("total")
pct = metrics.get("pct")
return f"{used}/{total} - {pct}%"
def collect_snapshot_metrics(data: dict) -> dict:
snapshots = data.get("quota_snapshots")
if not isinstance(snapshots, dict):
return {}
out = {}
for k, v in snapshots.items():
out[k] = metrics_from_parsed_snapshot(parse_snapshot(v))
return out
def main() -> int:
ap = argparse.ArgumentParser(
prog="ghcp-usage",
description="Show Copilot premium interaction quota usage.",
)
ap.add_argument(
"--json",
action="store_true",
help="Print JSON for the selected output mode.",
)
mode = ap.add_mutually_exclusive_group()
mode.add_argument(
"--detailed",
action="store_true",
help="Show premium/chat/completions plus reset date.",
)
mode.add_argument(
"--all",
action="store_true",
help="Show all quota snapshot values plus reset date.",
)
ap.add_argument(
"--hostname",
help="GitHub hostname (for GHES); defaults to github.com via gh config.",
)
ap.add_argument("--timeout", type=int, default=15, help="Timeout seconds (default: 15).")
args = ap.parse_args()
try:
data = gh_api_json(args.hostname, args.timeout)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
return 1
reset = format_reset_date_ddmmyy(data)
reset_suffix = f" - {reset}" if reset else ""
snapshots = collect_snapshot_metrics(data)
def snap(key: str) -> dict:
return snapshots.get(key) or {"status": "unknown"}
selected_mode = "default"
if args.all:
selected_mode = "all"
elif args.detailed:
selected_mode = "detailed"
if args.json:
if selected_mode == "default":
payload = {
"mode": "default",
"reset_date": reset,
"premium": snap("premium_interactions"),
}
elif selected_mode == "detailed":
payload = {
"mode": "detailed",
"reset_date": reset,
"premium": snap("premium_interactions"),
"chat": snap("chat"),
"completions": snap("completions"),
}
else:
payload = {
"mode": "all",
"reset_date": reset,
"snapshots": snapshots,
}
print(json.dumps(payload, indent=2, sort_keys=True))
return 0
if args.detailed:
print(f"premium: {format_metrics_value(snap('premium_interactions'))}")
print(f"chat: {format_metrics_value(snap('chat'))}")
print(f"completions: {format_metrics_value(snap('completions'))}")
print(f"reset date: {reset or 'unknown'}")
return 0
if args.all:
for k in sorted(snapshots.keys()):
print(f"{k}: {format_metrics_value(snapshots[k])}")
print(f"reset date: {reset or 'unknown'}")
return 0
premium_value = format_metrics_value(snap("premium_interactions"))
print(f"{premium_value}{reset_suffix}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment