I was looking for this somewhere on the internet. Couldn't find it. So I spent two days building it myself.

Since June 1, 2026, GitHub Copilot switched to a usage-based billing model. No more flat Premium Request buckets, everything is now metered in AI Credits at $0.01 per credit.


For individual developers this is invisible. For enterprise IT managers responsible for hundreds of seats, it changes everything about how you think about cost governance.

Why Daily Monitoring Beats Everything Else

Sure, GitHub has built-in tools. You can set a Cost Center, configure a budget alert, assign a Billing Administrator. That works fine if you have a small team or one cost center. But in a real enterprise with 150, 200, or 300+ developers spread across multiple teams and business units, a single shared credit pool means one team’s heavy usage directly impacts availability for everyone else.

You could give Billing Administrator access to team leads so they can check the GitHub billing UI themselves. But that means more privileged accounts, more people logging into GitHub billing pages, and still no proactive signal; someone has to remember to check. A daily automated report sent to the right people costs nothing to run and surfaces problems before they become mid-month surprises.

And the surprises are real. The model multiplier table is not prominently communicated to developers:

ModelCredit multiplier
GPT-5.5 / Claude Opus 4.77.5×
Claude Opus 4.6 / 4.5
Claude Sonnet 4.6 (default)
Claude Haiku 4.5 / GPT-5.4 mini0.33×
GPT-5 mini / GPT-4.10.0 (free?)

A developer who defaults to GPT-5.5 for quick code questions spends 22× more than a developer doing the same thing with Claude Haiku 4.5. Multiply that across 50 or 100 developers who don’t know this, and the math gets uncomfortable fast.

To put this in concrete terms: at 50 seats, your included AI credit pool is roughly 130,000 credits/month (~$1,300 value). At 90 seats it’s around 235,000. At 110 seats, 286,000. At 180 seats, 468,000. At 220 seats, 572,000. These are the amounts your plan covers before any overage billing kicks in. One developer running extended agentic sessions on GPT-5.5 can burn through 15,000–20,000 credits in a single day. That’s 10–15% of the monthly pool for a 50-seat org, in one day, from one person.

Daily monitoring with per-user visibility is the only way to catch this before it becomes a billing conversation with finance.

The GitHub API : Two Separate Surfaces, One Confusing Documentation

This is where things get non-obvious. The GitHub API for Copilot billing is split across two separate documentation sections that don’t clearly reference each other: the Billing Usage API and the Copilot Usage Metrics API. They return completely different data and require different permissions.

The Billing Usage API is where the money lives. It gives you actual credit quantities, gross amounts, discount coverage, and net billable amounts – the numbers that match what you see in the GitHub billing UI. The Copilot Usage Metrics API gives you behavioral data: how many interactions a user had, which IDE they used, how many lines of code were suggested vs. accepted, which models they used. Useful context, but no dollar figures.

To get per-user credit spend, you need both. You collect the user list from the metrics API (which covers one day at a time), then query the billing API per user to get their actual credit consumption for the month.

The endpoint that does the heavy lifting for per-user billing is:

GET /enterprises/{enterprise}/settings/billing/ai_credit/usage?year={year}&month={month}&user={username}

Documentation

And the aggregate enterprise view (no user filter) that gives you the full monthly picture:

GET /enterprises/{enterprise}/settings/billing/usage/summary

Documentation

One important dead end to save you time: the org-level equivalent (GET /organizations/{org}/settings/billing/ai_credit/usage?user={username}) returns a 403 for enterprise-owned organizations with the message “Organization admins for enterprise owned organizations cannot filter usage by user.” You have to go through the enterprise endpoint.

For the user list, the daily metrics report endpoint returns a downloadable NDJSON file (one JSON object per line, one line per user):

GET /orgs/{org}/copilot/metrics/reports/users-1-day?day={yyyy-MM-dd}

Documentation

The response gives you a download URL, not the data directly. You fetch the URL separately, decode the byte array as UTF-8, split by newline, and parse each line as JSON.

Security : Two Tokens, Two Permission Levels

The permission model requires two separate tokens because the enterprise billing endpoints and org metrics endpoints have genuinely different access requirements. There is no single token type that covers both cleanly.

Token 1 : Classic Personal Access Token, enterprise owner account

This must be generated by an account that holds Enterprise Owner or Enterprise Billing Manager role at the enterprise level. Being an org owner is not sufficient – GitHub treats org-level and enterprise-level roles as separate hierarchies. If you hold both, great. If not, the enterprise owner needs to generate this token and share it securely (Key Vault, not email).

Required scopes: read:enterprise and manage_billing:copilot.

Note that some GitHub Enterprise configurations disable classic PATs by policy. The enterprise owner can verify and adjust this at github.com/enterprises/{enterprise}/settings/personal-access-tokens. Fine-grained tokens cannot currently access enterprise billing endpoints, this is a platform limitation, not a configuration issue.

Token 2 : Fine-grained Personal Access Token, org owner account

This covers the Copilot metrics endpoint and can be created by any org owner. At creation time, set Resource Owner to your organization, then add two Organization permissions: Organization Copilot metrics (Read) and Organization administration (Read).

If your organization enforces SAML SSO, which most enterprise GitHub customers do, you must authorize the token after creation. Navigate to github.com/settings/tokens, find your token, click Configure SSO, and authorize it for your organization. Without this step, every API call returns a 403 with a SAML enforcement message.

Both tokens should live in a secrets store (Azure Key Vault works well) and be retrieved at runtime by the automation account running the report. Set expirations of 90 days maximum and rotate them on schedule.

Lets test it !

$tokenEnt   = "ghp_ENTERPRISE_OWNER_TOKEN"    # Classic PAT; see TOKEN 1 above
$tokenOrg   = "github_pat_YOUR_FINEGRAINED"   # Fine-grained PAT; see TOKEN 2 above
$enterprise = "ContosoEnterprise"             # slug from github.com/enterprises/{slug}
$org        = "ContosoOrg"                    # slug from github.com/orgs/{slug}
$year       = (Get-Date).Year
$month      = (Get-Date).Month
$thisMonth  = (Get-Date).ToString("yyyy-MM")
$dayOfMonth = (Get-Date).Day - 1              # yesterday is the last complete day
 
$hEnt = @{
    Authorization          = "Bearer $tokenEnt"
    Accept                 = "application/vnd.github+json"
    "X-GitHub-Api-Version" = "2026-03-10"
}
$hOrg = @{
    Authorization          = "Bearer $tokenOrg"
    Accept                 = "application/vnd.github+json"
    "X-GitHub-Api-Version" = "2026-03-10"
}
 
# metrics reports return a byte array of newline-delimited JSON
function Parse-NdJson($bytes) {
    $text = [System.Text.Encoding]::UTF8.GetString($bytes)
    $text -split "`n" | Where-Object { $_.Trim() -ne "" } | ForEach-Object { $_ | ConvertFrom-Json }
}
 
# quick connectivity check before running the full report
Write-Host "`nVerifying token access..." -ForegroundColor DarkGray
 
$identity = Invoke-RestMethod -Uri "https://api.github.com/user" -Headers $hEnt
Write-Host ("  Enterprise token : {0}" -f $identity.login) -ForegroundColor DarkGray

try {
    Invoke-RestMethod `
        -Uri "https://api.github.com/enterprises/$enterprise/settings/billing/usage/summary" `
        -Headers $hEnt | Out-Null
    Write-Host "  Enterprise billing endpoint : OK" -ForegroundColor Green
} catch {
    Write-Host "  Enterprise billing endpoint : FAILED, check enterprise role and token scopes" -ForegroundColor Red
    exit 1
}
 
try {
    $testDay = (Get-Date).AddDays(-1).ToString("yyyy-MM-dd")
    Invoke-RestMethod `
        -Uri "https://api.github.com/orgs/$org/copilot/metrics/reports/users-1-day?day=$testDay" `
        -Headers $hOrg | Out-Null
    Write-Host "  Org metrics endpoint        : OK" -ForegroundColor Green
} catch {
    Write-Host "  Org metrics endpoint        : FAILED, check org role, token permissions, and SSO authorization" -ForegroundColor Red
    exit 1
}

The Reports

Report 1 : Monthly Billing Trend

Answers the question every IT manager gets from finance: how is our Copilot spend trending, and when did it change?

$billing = Invoke-RestMethod `
    -Uri "https://api.github.com/enterprises/$enterprise/settings/billing/usage" `
    -Headers $hEnt

$billing.usageItems |
    Where-Object { $_.product -eq "copilot" } |
    Group-Object { $_.date.ToString("yyyy-MM") } |
    Select-Object `
        @{N="Month";     E={$_.Name}},
        @{N="AICredits"; E={
            $c = $_.Group | Where-Object { $_.sku -eq "Copilot AI Credits" }
            if ($c) { [math]::Round(($c | Measure-Object quantity -Sum).Sum, 0) } else { "-" }
        }},
        @{N="Seats";     E={
            $s = $_.Group | Where-Object { $_.sku -match "Copilot Business|Copilot Enterprise" }
            [math]::Round(($s | Measure-Object quantity -Sum).Sum, 0)
        }},
        @{N="Gross USD"; E={"$" + [math]::Round(($_.Group | Measure-Object grossAmount -Sum).Sum, 2)}},
        @{N="Net USD";   E={"$" + [math]::Round(($_.Group | Measure-Object netAmount -Sum).Sum, 2)}} |
    Sort-Object Month | Format-Table -AutoSize

Sample output for a 90-seat organization transitioning to AI Credits billing in June:

Month   AICredits  Seats  Gross USD   Net USD
-----   ---------  -----  ---------   -------
2026-01         -     52  $2,184.00   $2,184.00
2026-02         -     61  $2,457.00   $2,457.00
2026-03         -     74  $4,290.00   $4,030.00
2026-04         -     88  $5,120.00   $4,710.00
2026-05         -     92  $5,876.00   $5,240.00
2026-06   237,400     90  $3,180.00   $560.00

The June gross/net gap reflects credits covered by the included plan pool. The transition from flat-rate Premium Requests to AI Credits is clearly visible.


Report 2 : Monthly Billing Trend

Answers: are we on track, or heading for an overage conversation with finance at month-end?

$summary = Invoke-RestMethod `
    -Uri "https://api.github.com/enterprises/$enterprise/settings/billing/usage/summary" `
    -Headers $hEnt

$aiItem        = $summary.usageItems | Where-Object { $_.sku -eq "copilot_ai_unit" }
$totalConsumed = [math]::Round($aiItem.grossQuantity, 0)
$covered       = [math]::Round($aiItem.discountQuantity, 0)
$overage       = [math]::Round($aiItem.netQuantity, 0)
$projected     = if ($dayOfMonth -gt 0) { [math]::Round($totalConsumed / $dayOfMonth * 30, 0) } else { 0 }

Write-Host ("  Consumed so far  : {0,10:N0} credits  = `${1:N2}" -f $totalConsumed, ($totalConsumed * 0.01))
Write-Host ("  Covered by plan  : {0,10:N0} credits  = `${1:N2}" -f $covered, ($covered * 0.01))
Write-Host ("  Billable overage : {0,10:N0} credits  = `${1:N2}" -f $overage, ($overage * 0.01))
Write-Host ("  Projected month  : {0,10:N0} credits  = `${1:N2}" -f $projected, ($projected * 0.01))
Write-Host ("  Daily burn rate  : {0,10:N0} credits/day" -f [math]::Round($totalConsumed / [math]::Max($dayOfMonth,1), 0))

Sample output on day 3 of the month for a 90-seat org:

  Consumed so far  :     79,100 credits  = $791.00
  Covered by plan  :     79,100 credits  = $791.00
  Billable overage :          0 credits  = $0.00
  Projected month  :    790,000 credits  = $7,900.00
  Daily burn rate  :     26,300 credits/day

When projected month exceeds the included pool (235,000 credits for 90 seats in this example), the overage line starts growing. That’s your trigger for a conversation.

Report 3 : Budget & Alert Configuration

Shows what guardrails are currently configured and whether they will actually block usage or only alert.

$budgets = Invoke-RestMethod `
    -Uri "https://api.github.com/enterprises/$enterprise/settings/billing/budgets" `
    -Headers $hEnt

$budgets.budgets | ForEach-Object {
    Write-Host ("  [{0}] {1}" -f $_.budget_scope.ToUpper(), $_.budget_entity_name)
    Write-Host ("    Alert threshold : `${0}" -f $_.budget_amount)
    Write-Host ("    Block on exceed : {0}" -f $_.prevent_further_usage)
    Write-Host ("    Alert recipient : {0}" -f ($_.budget_alerting.alert_recipients -join ", "))
}

The prevent_further_usage flag is worth understanding. Set to false, GitHub sends an alert but Copilot keeps working. Set to true, GitHub cuts off access for all users when the threshold is hit. Most organizations leave it as alert-only at the enterprise level to avoid disruption, but you can set tighter hard limits at the cost center level for specific teams.

Report 4 : Model Cost Breakdown

Shows which models are actually driving spend across the organization. This is frequently surprising , Auto mode often selects more expensive models than users realize.

$entModels = Invoke-RestMethod `
    -Uri "https://api.github.com/enterprises/$enterprise/settings/billing/ai_credit/usage?year=$year&month=$month" `
    -Headers $hEnt

$entModels.usageItems |
    Select-Object `
        @{N="Model";     E={$_.model}},
        @{N="Credits";   E={[math]::Round($_.grossQuantity, 0)}},
        @{N="Gross USD"; E={"$" + [math]::Round($_.grossAmount, 2)}} |
    Sort-Object Credits -Descending | Format-Table -AutoSize

Sample output for a 90-seat org in the first week of the month:

Model                    Credits   Gross USD
-----                    -------   ---------
Claude Opus 4.6           28,400   $284.00
GPT-5.4                   26,900   $269.00
Auto: GPT-5.3-Codex       15,700   $157.00
Claude Opus 4.7           12,200   $122.00
GPT-5.5                    9,800    $98.00
Claude Sonnet 4.6          4,100    $41.00
Auto: Claude Sonnet 4.6    3,900    $39.00
Claude Haiku 4.5             340     $3.40

The “Auto:” prefix entries are model selections made by Copilot’s auto-routing logic. These often land on more expensive models than a manual selection would. Combined, the 7.5× tier models (GPT-5.5 and Claude Opus 4.7) represent a disproportionate share of spend relative to their usage count.

Report 5 : Per-User AI Credits

The most operationally useful report. Shows exactly who is consuming what, down to the primary model driving their spend.

The collection works in two steps: first gather all users who were active on any day in the current month (not just yesterday, users who were active earlier in the month but not recently would otherwise be missed), then query the enterprise billing endpoint per user.

powershell

# Step 1: collect all active users across the month
$allUsers = @{}
$current  = [DateTime]::new($year, $month, 1)
$endDay   = (Get-Date).AddDays(-1)

while ($current -le $endDay) {
    $d = $current.ToString("yyyy-MM-dd")
    try {
        $r    = Invoke-RestMethod -Uri "https://api.github.com/orgs/$org/copilot/metrics/reports/users-1-day?day=$d" -Headers $hOrg
        $raw  = Invoke-WebRequest -Uri $r.download_links[0] -UseBasicParsing
        $text = [System.Text.Encoding]::UTF8.GetString($raw.Content)
        $text -split "`n" | Where-Object { $_.Trim() -ne "" } | ForEach-Object {
            $u = ($_ | ConvertFrom-Json).user_login
            if ($u -ne "") { $allUsers[$u] = 1 }
        }
    } catch {}
    $current = $current.AddDays(1)
}

# Step 2: fetch billing per user
$userBilling = $allUsers.Keys | Sort-Object | ForEach-Object {
    $u = $_
    try {
        $r = Invoke-RestMethod `
            -Uri "https://api.github.com/enterprises/$enterprise/settings/billing/ai_credit/usage?year=$year&month=$month&user=$u" `
            -Headers $hEnt
        $totalCredits = ($r.usageItems | Measure-Object grossQuantity -Sum).Sum
        $totalUSD     = ($r.usageItems | Measure-Object grossAmount -Sum).Sum
        $topModel     = ($r.usageItems | Sort-Object grossQuantity -Descending | Select-Object -First 1).model
        if ($totalCredits -gt 0) {
            [PSCustomObject]@{
                User     = $u
                Credits  = [math]::Round($totalCredits, 0)
                GrossUSD = "$" + [math]::Round($totalUSD, 2)
                TopModel = $topModel
            }
        }
    } catch { $null }
} | Where-Object { $_ -ne $null } | Sort-Object Credits -Descending

$userBilling | Format-Table -AutoSize

Sample output for a 90-seat org, partial month:

User                   Credits  GrossUSD  TopModel
----                   -------  --------  --------
jsmith_Contoso          18,100   $181.00  GPT-5.5
tiwuse_Contoso          11,900   $119.00  Claude Opus 4.7
codex_Contoso           11,500   $115.00  Claude Opus 4.6
jtang_Contoso            9,900    $99.00  GPT-5.4
bhooz_Contoso            7,400    $74.00  Claude Opus 4.6
rkumar_Contoso           6,200    $62.00  Claude Opus 4.7
...

The gap between the sum of per-user credits and the enterprise total is typically under 1% and represents system-level operations without an associated user login. For all practical reporting purposes, this is complete attribution.

From Data to Action

Collecting this data daily and feeding it into a Power BI report means the IT team, team leads, and finance all have visibility without anyone needing Billing Administrator access in GitHub. One scheduled runbook, one report, distributed to whoever needs it.

The per-user data makes targeted conversations possible. A developer spending $120/day on Copilot is not necessarily doing anything wrong. They might be running complex multi-file refactoring sessions where Claude Opus genuinely earns its cost. But a developer spending $80/day on quick chat questions that could be handled by Haiku at $2.60/day is a very different situation. You can only tell the difference when you have the data.

Model steering policies, cost center budgets, and alert thresholds all become more precise once you can see actual consumption patterns. The included credit pool is generous enough for normal usage. The question is whether your organization’s usage patterns match the assumptions behind “normal.”


Leave a Reply

Your email address will not be published. Required fields are marked *