A repository can generate costs through GitHub Actions, storage, and other metered products. In a large organization, those costs are not always owned by the team that manages the repository. Finance may need to attribute them to a product group, department, or engineering business unit.
GitHub's billing APIs now make this easier to automate. An enterprise can retrieve usage summaries, filter usage by cost center, and look up which cost center contains a particular repository. This makes it possible to build a recurring cost report without manually downloading billing data and matching repositories to departments.
There is one important distinction: a repository's cost center membership and its actual billed usage are related, but they are not the same piece of information. A reliable report needs both.
Understand How GitHub Cost Centers Work
A cost center is a way to assign GitHub spending to a business unit. An enterprise can add repositories, organizations, users, and enterprise teams to a cost center.
For example, an engineering organization might use the following structure:
| Cost center | Assigned resources | Reporting purpose |
|---|
| Platform Engineering | Platform repositories and team | Infrastructure and shared services |
| Mobile Development | Mobile repositories and team | Mobile application delivery |
| Data Engineering | Data repositories and team | Data platform spending |
| Internal Tools | Internal tooling repositories | Developer productivity |
A resource can belong to only one cost center at a time. If it is added to another cost center, it is reassigned. This matters when building historical reports because a repository's current membership might not represent the cost center that was responsible for its usage in an earlier billing period.
There is also a difference between usage-based and license-based products. For products such as GitHub Actions, usage is attributed through repositories or organizations. For license-based products such as Copilot, allocation is based on users. The reporting model should account for the product's allocation rules rather than assuming every charge belongs directly to a repository.
Cost centers apply to metered usage, not every subscription or volume-based charge.
Choose the Right Billing API
GitHub provides several REST API endpoints for billing information. For repository cost reporting, three are particularly useful.
| API endpoint | Purpose |
|---|
| GET /enterprises/{enterprise}/settings/billing/usage/summary | Retrieves aggregated usage and cost information |
| GET /enterprises/{enterprise}/settings/billing/cost-centers | Lists enterprise cost centers and their resources |
| GET /enterprises/{enterprise}/settings/billing/cost-centers/memberships | Finds the cost center assigned to a specific resource |
The usage summary endpoint is the main source for cost data. It supports filters for the reporting period, product, SKU, and cost center.
The membership endpoint is useful when a report starts with a repository name and needs to discover its assigned cost center. GitHub introduced this lookup endpoint on November 3, 2026. Before its availability, an integration generally had to list cost centers and inspect their resource collections to find a match.
For a centralized enterprise report, use enterprise-level billing endpoints rather than combining organization-level usage reports. The enterprise view is designed to report across multiple organizations and cost centers.
Configure Authentication and Permissions
Billing information is sensitive, so the integration should use a dedicated identity with the minimum required access.
For enterprise cost center APIs, enterprise owners and billing managers can use a classic personal access token or an OAuth access token with the manage_billing:enterprise scope. Fine-grained personal access tokens cannot access these enterprise cost center endpoints.
GitHub App authentication is also documented for some billing operations, but enterprise billing permissions are not generally available to every enterprise-owned GitHub App. Confirm that the required permission can actually be granted in the target enterprise before designing the integration around an App token.
For the enterprise usage summary endpoint, the documented fine-grained permission is Enterprise billing read. The exact supported token types differ by endpoint, so check the authentication section for each operation.
Keep the token in a secret store or environment variable. Avoid placing it in application settings committed to source control.
For a .NET application, the configuration can look like this:
GITHUB_API_TOKEN=your_token
GITHUB_ENTERPRISE=your-enterprise-slug
The enterprise value is the slug used in the API path, not necessarily the display name shown in the billing interface.
Retrieve Cost Center Membership for a Repository
Suppose the reporting application needs to find which cost center owns the repository contoso/payment-service.
The membership endpoint accepts a resource type and a resource identifier:
GET /enterprises/{enterprise}/settings/billing/cost-centers/memberships?resource_type=repository&name=contoso/payment-service
A successful response can look like this:
{
"resource": {
"type": "repository",
"identifier": {
"name": "contoso/payment-service"
}
},
"memberships": [
{
"cost_center": {
"id": "12345678-1234-1234-1234-123456789012",
"name": "Platform Engineering"
}
}
]
}
The example values are illustrative. The actual response should be read from the API.
If the repository is not assigned to a cost center, the memberships array is empty. Do not interpret that as zero usage. It means that the repository has no cost center membership in the returned result.
Here is a C# implementation using HttpClient:
using System.Net.Http.Headers;
using System.Text.Json;
public sealed class GitHubBillingClient
{
private readonly HttpClient _httpClient;
public GitHubBillingClient(string token)
{
_httpClient = new HttpClient
{
BaseAddress = new Uri("https://api.github.com/")
};
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue(
"application/vnd.github+json"));
_httpClient.DefaultRequestHeaders.Add(
"X-GitHub-Api-Version", "2026-03-10");
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(
"RepositoryCostReporter");
}
public async Task<JsonDocument> GetRepositoryMembershipAsync(
string enterprise,
string repository)
{
var query =
$"resource_type=repository&name={Uri.EscapeDataString(repository)}";
var endpoint =
$"enterprises/{Uri.EscapeDataString(enterprise)}" +
$"/settings/billing/cost-centers/memberships?{query}";
using var response =
await _httpClient.GetAsync(endpoint);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return JsonDocument.Parse(content);
}
}
The example uses JsonDocument so the application can inspect the response without requiring a model for every field. In a larger application, strongly typed response models are usually easier to maintain.
Retrieve Usage for a Cost Center
Once the application has a cost center ID, it can request the usage summary for a reporting period.
For example, this request retrieves enterprise usage for a particular cost center during November 2026:
GET /enterprises/{enterprise}/settings/billing/usage/summary?year=2026&month=11&cost_center_id={cost_center_id}
The response contains a usageItems collection. Each item can include fields such as:
- product: the GitHub product associated with the usage.
- sku: the specific billed SKU.
- unitType: the unit used for measurement.
- pricePerUnit: the price for one unit.
- grossQuantity: the quantity before discounts.
- grossAmount: the amount before discounts.
- discountAmount: the discount applied.
- netQuantity: the quantity after applicable adjustments.
- netAmount: the resulting amount.
For financial reporting, netAmount is generally the more useful field when presenting the amount after discounts. Keep the gross and discount values as well if finance needs to reconcile the figures.
A .NET method can retrieve the summary:
public async Task<JsonDocument> GetUsageSummaryAsync(
string enterprise,
string costCenterId,
int year,
int month)
{
var query =
$"year={year}&month={month}" +
$"&cost_center_id={Uri.EscapeDataString(costCenterId)}";
var endpoint =
$"enterprises/{Uri.EscapeDataString(enterprise)}" +
$"/settings/billing/usage/summary?{query}";
using var response =
await _httpClient.GetAsync(endpoint);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return JsonDocument.Parse(content);
}
The reporting application can then read the usage items and store them in its own database.
The enterprise usage summary endpoint supports a maximum historical window of 24 months. Plan the reporting process accordingly rather than expecting the API to provide unlimited historical billing data.
Build a Repository Cost Report
A useful reporting pipeline combines repository membership with billing usage.
For example, a scheduled job can perform the following operations:
- Retrieve the active cost centers for the enterprise.
- Identify the repositories assigned to each cost center.
- Retrieve usage summaries for the required reporting period.
- Store product, SKU, quantity, and amount information.
- Generate a report grouped by cost center and product.
A simple reporting table might look like this:
| Cost center | Product | SKU | Net usage | Net amount |
|---|
| Platform Engineering | Actions | Linux minutes | 12,500 | 100.00 |
| Mobile Development | Actions | macOS minutes | 2,000 | 80.00 |
| Data Engineering | Storage | Storage usage | 500 | 25.00 |
These values are illustrative, not actual GitHub prices or usage figures.
For finance reporting, store the billing period, cost center ID, repository identifier where available, product, SKU, and synchronization timestamp. Use the cost center ID as the stable relationship key instead of relying only on its display name, which administrators can change.
One limitation needs careful handling: the enterprise usage summary is an aggregated billing report. It should not automatically be treated as a repository-by-repository cost breakdown. Use the repository filter where the endpoint supports it, or combine the available repository-level information with cost center membership. Do not divide a cost center's total across repositories unless the organization has agreed on an allocation method.
Keep the Numbers Reliable
Billing reports need more care than ordinary operational dashboards. A missing API response or a changed cost center assignment can make a report misleading.
A few implementation decisions help avoid common problems:
- Store the cost center ID alongside its name so that renaming does not break historical joins.
- Record the year and month used in every usage request.
- Keep gross amount, discount amount, and net amount rather than storing only one total.
- Treat an empty membership result as unassigned, not as zero cost.
- Record failed synchronization jobs instead of replacing missing data with zero.
- Reconcile periodic totals with GitHub's billing interface and invoices.
- Use bounded retries for temporary server failures, but do not repeatedly retry authentication and permission errors.
- Keep historical snapshots so that a repository's reassignment does not rewrite earlier reporting periods.
Also pay attention to resource allocation. A repository can be reassigned to another cost center, and that change affects where its usage is charged. A report should make the reporting period clear and should not use today's membership as proof of ownership for every previous month.
Common Problems
| Problem | Possible cause | What to check |
|---|
| HTTP 401 | Invalid or expired token | Check token validity and authorization header |
| HTTP 403 | Insufficient billing access | Verify the user's role and endpoint-specific permissions |
| HTTP 404 | Incorrect enterprise slug or resource identifier | Check the enterprise and repository names |
| Empty memberships array | Resource is not assigned to a cost center | Review cost center assignments |
| Usage does not match repository totals | Aggregated and repository-level data are being compared | Check the endpoint filters and allocation rules |
| Historical report is incomplete | Requested data is outside the supported retention window | Check the 24-month limit |
| Duplicate report rows | Repeated synchronization inserted the same billing period again | Use a stable key for period, cost center, product, and SKU |
Conclusion
The GitHub REST API provides the building blocks for automated repository cost reporting. The usage summary endpoint supplies billing information, the cost center endpoint provides enterprise allocation details, and the membership lookup endpoint connects a repository to its assigned cost center.
The most important part is combining these resources without confusing membership with actual usage. Preserve stable identifiers, retain the billing period, and make the allocation rules clear to anyone reading the report.
With a scheduled .NET integration and a reporting database, engineering and finance teams can track GitHub spending by business unit, investigate changes in usage, and prepare more consistent cost reports.
Join the conversation! Your thoughts help the community grow.