Print your current-period GitHub Copilot spend and reset date.
Project description
copilot-spend
Find out what your Copilot habit actually costs.
A small Python CLI that reads your GitHub Copilot quota and prints
your current-period spend in dollars and PRUs, plus when the period resets.
Works against both github.com and GitHub Enterprise hosts.
How it works
flowchart TD
Start([copilot-spend ...]) --> Cmd{subcommand?}
Cmd -- login --> Host[prompt: github.com or GHE host]
Host --> DeviceCode[POST /login/device/code]
DeviceCode --> ShowCode[show user code + verification URL]
ShowCode --> Poll[poll /login/oauth/access_token]
Poll -- access_denied / expired --> LoginFail[/exit 2: login failed/]
Poll -- ghu_ token --> Verify[verify token works<br/>GET /copilot_internal/user]
Verify -- fail --> LoginFail
Verify -- ok --> WriteAuth[write auth.json 0o600]
WriteAuth --> LoginDone[/exit 0/]
Cmd -- logout --> DeleteFiles[delete auth.json]
DeleteFiles --> LogoutDone[/exit 0/]
Cmd -- bare --> Native{native<br/>~/.config/copilot-spend/auth.json?}
Native -- yes --> NativeBearer[bearer = ghu_ token]
Native -- no --> Opencode{opencode<br/>~/.local/share/opencode/auth.json?}
Opencode -- yes --> OpencodeBearer[bearer = gho_ token]
Opencode -- no --> NoCreds[/exit 2: run copilot-spend login/]
NativeBearer --> Quota[GET /copilot_internal/user with Bearer]
OpencodeBearer --> Quota
Quota -- 200 --> Print[/print spend + reset date<br/>exit 0/]
Quota -- 401 / 403 --> AuthErr[/exit 2: re-authenticate/]
Quota -- 404 --> NoSub[/exit 4: no Copilot quota/]
Quota -- 5xx / timeout --> ApiErr[/exit 3: API error/]
The bare copilot-spend invocation, expanded as numbered steps:
- Resolves a GitHub Copilot token from the first source that exists, in
this order:
- Native:
~/.config/copilot-spend/auth.json, created by runningcopilot-spend login. - Opencode fallback:
~/.local/share/opencode/auth.json(keysgithub-copilot.accessandgithub-copilot.enterpriseUrl), if opencode is installed.
- Native:
- Uses the resolved token directly as the
Bearerfor the next step. Both source kinds work the same way: native gives aghu_…GitHub App user-to-server token, opencode gives agho_…OAuth App token, and/copilot_internal/useraccepts either one directly. - Calls
GET /api/v3/copilot_internal/useron your GHE host, orGET https://api.github.com/copilot_internal/userif no enterprise host is configured. - Computes the billable overage:
billable_PRUs = max(0, consumed - entitlement), thendollars_owed = billable_PRUs × $0.04. The firstentitlementPRUs each period are included with your plan and cost nothing. - Prints a plain-text summary on stdout.
No background daemon. No history. No session-token cache. One HTTP request per run.
Requirements
- Python 3.10 or newer
- macOS or Linux
- A GitHub Copilot token, obtained by either:
- running
copilot-spend login, or - having opencode installed and authenticated already
- running
Install
# Option A: pipx (persistent, isolated)
pipx install copilot-spend
# Option B: pip (into your active environment or --user)
pip install copilot-spend
# Option C: uv tool (persistent, isolated)
uv tool install copilot-spend
# Option D: one-off run, no install
uvx copilot-spend
pipx run copilot-spend
Install from source
git clone https://github.com/nkootstra/copilot-spend.git
cd copilot-spend
pipx install .
# or:
uv tool install --from . copilot-spend
# or one-off:
uvx --from . copilot-spend
Usage
copilot-spend # print current-period quota
copilot-spend --json # same data as JSON (stable schema, jq-friendly)
copilot-spend whoami # print active login, host, source, and plan
copilot-spend login # authenticate via GitHub OAuth device flow
copilot-spend logout # remove copilot-spend's stored credentials
copilot-spend login prompts for github.com or a GHE host, shows a
device code with a URL to visit, then polls until you complete the
flow in your browser. The new token is verified against
/copilot_internal/user before anything gets persisted. Credentials
land in ~/.config/copilot-spend/auth.json (mode 0o600). If you
already have opencode authenticated, the bare copilot-spend command
continues to work without login.
Example output (under your allowance):
GitHub Copilot - your-login (business)
Used: 221 PRUs
Allowance: $12.00 (300 PRUs included)
Remaining: $3.16 (79 PRUs of free allowance left)
Resets: May 31, 2026 (in 15 days)
Example output (over your allowance — billable overage):
GitHub Copilot - your-login (business)
Used: 4073 PRUs
Allowance: $12.00 (300 PRUs included)
Billable: $150.92 (3773 PRUs over allowance at $0.04/PRU)
Resets: Jun 01, 2026 (in 16 days)
Flags: --help, --version, --json. Subcommands: login, logout, whoami.
JSON output (stable schema, null fields included):
{
"billable_prus": 3773,
"consumed_prus": 4073,
"dollars_entitlement": 12.0,
"dollars_free_remaining": 0.0,
"dollars_owed": 150.92,
"entitlement_prus": 300,
"free_remaining_prus": 0,
"login": "your-login",
"plan": "business",
"pru_price_usd": 0.04,
"reset": "2026-06-01T00:00:00+00:00"
}
Environment variables
| Variable | Effect |
|---|---|
COPILOT_SPEND_QUIET=1 |
Silence the permissive-perms warning when the auth file isn't 0o600. |
COPILOT_SPEND_DEBUG=1 |
Re-raise unexpected exceptions with a full traceback; also emits a one-line diagnostic if the reset-date field can't be extracted. |
COPILOT_SPEND_CONFIG_DIR |
Override the config directory (default: $XDG_CONFIG_HOME/copilot-spend or ~/.config/copilot-spend). |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Unexpected error |
| 2 | Auth error (missing/invalid auth.json or token) |
| 3 | API error (network, timeout, 4xx/5xx) |
| 4 | No Copilot quota on the account |
Switch to your own GitHub App
copilot-spend login runs the GitHub OAuth device flow against
Microsoft's well-known VS Code Copilot GitHub App
(Iv1.b507a08c87ecfe98). This is the same client ID used by every
working third-party Copilot tool — copilot.vim, avante.nvim, LiteLLM,
and others — because GitHub's session-token exchange endpoint
(/copilot_internal/v2/token) only accepts tokens issued by a GitHub
App, not by an OAuth App.
The trade-off: the GitHub consent screen during login says "GitHub for VS Code" rather than "copilot-spend", and you depend on Microsoft not rotating that app. To remove both, register your own GitHub App and swap the constant:
- Visit https://github.com/settings/apps and click New GitHub App.
This must be a GitHub App, not an OAuth App — OAuth Apps issue
gho_…tokens that the Copilot exchange endpoint rejects with 404. - Set Homepage URL and Callback URL to anything (the device flow does not use them).
- Enable Device flow under "Identifying and authorizing users".
- Account permissions: none required beyond user identification.
The
read:userOAuth scope is enough. - Note the resulting Client ID (starts with
Iv23orIv1.). - Replace the
CLIENT_IDconstant insrc/copilot_spend/login.pywith your new client ID. - Rebuild/reinstall (
pipx install --force .oruv tool install --force --from . copilot-spend).
After the swap, the consent screen shows your app's name and your
copilot-spend install no longer breaks if Microsoft rotates
Iv1.b507a08c87ecfe98.
Caveats
- The PRU price is hardcoded at $0.04 (correct as of 2026-05). Update the
constant in
src/copilot_spend/quota.pyif GitHub changes it. - v1 ships with VS Code's GitHub App ID
Iv1.b507a08c87ecfe98for the device flow. See "Switch to your own GitHub App" above to remove the dependency. - The billing model assumed: the first
entitlementPRUs each period are included with your plan, and anything beyond that is billable at $0.04 per PRU. This matches observed behavior on a business plan. Org-level caps or contracts may change what you actually pay — treat theBillablefigure as a personal estimate, not an invoice. - The reset-date field name in the Copilot API response is best-effort:
copilot-spendtries the field names observed on a real response, plus a few defensive fallbacks, and printsnext reset: unknownif none match. AdjustRESET_FIELD_CANDIDATESinquota.pyif your response uses a different name. - The
/copilot_internal/userendpoint is not a public, documented API. GitHub may change its shape at any time.
Development
python -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file copilot_spend-0.2.0.tar.gz.
File metadata
- Download URL: copilot_spend-0.2.0.tar.gz
- Upload date:
- Size: 31.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a9cb7150ea4a20410704238c1c27c132598afe15bb5a4e0efedbb9fc63d50c00
|
|
| MD5 |
201f97fe84d47eebebfdd3fdfe90b910
|
|
| BLAKE2b-256 |
4f5216b9a1e6220e37abb8db4878d52cd84418651f608cf02d5c3d752fa287dc
|
Provenance
The following attestation bundles were made for copilot_spend-0.2.0.tar.gz:
Publisher:
release.yml on nkootstra/copilot-spend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
copilot_spend-0.2.0.tar.gz -
Subject digest:
a9cb7150ea4a20410704238c1c27c132598afe15bb5a4e0efedbb9fc63d50c00 - Sigstore transparency entry: 1562718449
- Sigstore integration time:
-
Permalink:
nkootstra/copilot-spend@d6bd0c3bceeafe0355f46edda9531dad5bf04252 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/nkootstra
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d6bd0c3bceeafe0355f46edda9531dad5bf04252 -
Trigger Event:
release
-
Statement type:
File details
Details for the file copilot_spend-0.2.0-py3-none-any.whl.
File metadata
- Download URL: copilot_spend-0.2.0-py3-none-any.whl
- Upload date:
- Size: 20.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7672827beaf8eff68172941a58069dd46c43c50e659cf41bf69c00cea623a0ae
|
|
| MD5 |
9c66e30387849b00665d8b97ebc7140b
|
|
| BLAKE2b-256 |
0bf4b2affd1720f6a192c2489175acf6ab17cc8788931ae909ed2793cbee9cad
|
Provenance
The following attestation bundles were made for copilot_spend-0.2.0-py3-none-any.whl:
Publisher:
release.yml on nkootstra/copilot-spend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
copilot_spend-0.2.0-py3-none-any.whl -
Subject digest:
7672827beaf8eff68172941a58069dd46c43c50e659cf41bf69c00cea623a0ae - Sigstore transparency entry: 1562718528
- Sigstore integration time:
-
Permalink:
nkootstra/copilot-spend@d6bd0c3bceeafe0355f46edda9531dad5bf04252 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/nkootstra
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d6bd0c3bceeafe0355f46edda9531dad5bf04252 -
Trigger Event:
release
-
Statement type: