Spend · CI spend

CI spend

Which CI job is eating your money? CI spend prices your GitHub Actions pipeline the way the other spend tabs price your models: by workflow, by job, and per merged PR. It is the column an LLM cost tool leaves out, and on a busy repo it is often the larger bill.

What it is

Every other billing surface in RepoOps reads a provider's cost API and shows you the bill. This one cannot, and it says so on the page rather than hiding it. GitHub retired the billing endpoint for user-owned accounts, and the per-run endpoint that is meant to report billable milliseconds returns zeros. So CI spend measures instead: it reads every job's start and finish time, rounds each job up to the whole minute the way GitHub bills, and prices the result from a published rate table.

The number is therefore modeled, not billed, and the tab prints that sentence above the figure. Treat it as an attribution tool. It tells you which job is expensive and by how much relative to the others, which is the question you actually have when a bill jumps.

What it answers

  • Which job costs the most. Ranked by modeled cost, with a matrix collapsed into one row.
  • What a merged PR costs to ship. PR-attached spend divided over the PRs that actually merged.
  • How much is not attached to any PR. Pushes to the default branch and scheduled runs, in their own bucket.
  • Which jobs fail often. A job that fails half the time pays twice: once to fail, once on the re-run.

Why per-job rounding matters

GitHub bills each job rounded up to a whole minute, and that single rule changes where the money looks like it goes. A pipeline of sixteen jobs where ten finish in a few seconds still bills ten minutes for those ten. A tool that averaged seconds across the run would under-report the pipeline and point at the wrong job.

The cheapest fix is usually structural
When ten jobs each bill a minimum minute, merging a few of them into one saves more than making any of them faster. CI spend surfaces the job count next to the cost so that trade is visible.

Sampling

Reading a run's cost means one extra API request for its jobs, so a month of a busy repo would be thousands of requests against an hourly rate limit. CI spend samples the most recent runs and scales to the window, and it always shows you the sample size, the number of runs in the window, and the multiplier between them. An estimate is labelled as an estimate.

Setup

Set GITHUB_TOKEN in the aggregator's environment with repo and actions:read scope, then restart. Without it the tab says so and shows nothing. The token is used to read workflow runs and their jobs, and it is never returned by the endpoint.

Honest limits

  • The dollars are modeled, not billed. There is no GitHub cost API to read for a user-owned account, so a rate table stands in for one. Use it to compare jobs, not to reconcile an invoice.
  • Plan-included minutes are not deducted. They are applied to your invoice. A small account inside its free allowance will see a modeled cost against a $0 bill.
  • Larger and self-hosted runners are priced at the standard rate for their operating system. That under-reports them, so when one appears the tab names the label and marks the total a floor.
  • Public repositories read $0 throughout. Standard runners are free there, and the tab does not pretend otherwise.
  • Jobs with no usable timestamps are excluded. Still running, or cancelled before they started. They are counted as uncounted, never as free.
  • The window caps at 90 days, because GitHub ages older runs out of the API.

Read more

Looking at the wrong bill?
If the question is what your models cost rather than what your builds cost, start at Metered spend. Open it with K, then type “Metered spend.”

Last updated