By Daniel Rohregger, Microsoft MVP Published
The Microsoft 365 Copilot Cost Management API reads and controls your tenant’s credit consumption through Microsoft Graph. You can see how many credits were purchased and used, which spending policies apply, who gets alerts, and why a particular user can’t use a service.
The cost management dashboard in the Microsoft 365 admin center shows consumption by policy, user, group, agent, service and funding source. The API gives you the raw data, which you can feed into your own systems and combine with other data.
I ran it end to end in my test tenant on October 29, 2026. Here’s what it is, what comes back, and what I’d use it for. The dashboard I built from it is on the Lookbook page (in German) and on GitHub.
What is the Copilot Cost Management API?
It’s an extension of Microsoft Graph under the path /copilot/costManagement. It belongs to usage-based billing: services such as Copilot Cowork and Work IQ bill in Copilot credits, and admins use spending policies to decide who can consume how many. The API covers exactly that control.
One thing up front: the API lives under /beta. Microsoft’s documentation says beta APIs can change and aren’t supported in production applications. For your own reports and scripts, that means checking your calls regularly.
What does the API return, and what doesn’t it?
I sorted the functions from Microsoft’s documentation into six groups and added the permission each call needs.
| Group | What you get | Permission |
|---|---|---|
| Tenant credit balance | One row per billing method: purchased, consumed, remaining | CopilotCostManagement.Read.All |
| Spending policies | Create, read, update, delete; limits for the period and per user | CopilotCostManagement-Policy.Read.All or .ReadWrite.All |
| A policy’s audience | All users or selected groups | CopilotCostManagement-Assignment.Read.All or .ReadWrite.All |
| Services per policy | Which services a policy governs | same as policies |
| Alerts | Thresholds and recipients, notification only | same as policies |
| User balances | Per user and service: whether they can consume, and under which policy | CopilotCostManagement-UserData.Read.All |
What the API deliberately leaves out is also in the documentation: it’s denominated in credits and contains no currency amounts, invoices, prices or forecasts. You work out the dollar or euro amount yourself, using the price in your contract.
I ran into two limits myself. First, it returns snapshots. The credit balance carries only a timestamp for the last update, not a history. If you want to know how consumption develops over the weeks, you have to store the calls yourself on a schedule. Second, you can’t list all user balances: calling userBalances without a user ID ends in a 404. You query user by user, so you need the user list from another source.
What did I see in the test tenant?
I called all 22 documented operations, in 62 calls over about 19 seconds. 51 went through as expected, 11 ended in an error on purpose (ten while writing, plus the 404 on userBalances), and there were no unexpected responses. The write calls ran against a test policy that I created and deleted at the end. I’ve left out names, IDs and addresses here.
The credit balance
One call returns the balance for both billing methods:
GET https://graph.microsoft.com/beta/copilot/costManagement/getTenantCreditBalance
| Billing method | Purchased | Consumed | Remaining |
|---|---|---|---|
| Prepaid credits | 50,000 | 22,265.38 | 25,029 |
| Pay-as-you-go | – | 3,152.11 | – |
Two things stand out. The data was over an hour old when I called it: the timestamp said 09:50 UTC, and the call ran after 11:05. And 50,000 minus 22,265.38 is 27,734.62, but the API reports 25,029 as the remainder. I can’t explain the difference of roughly 2,706 credits from the responses. That’s why I don’t calculate the remaining balance myself and read the remainingQuantity field instead.
For pay-as-you-go, the API returns consumption only. At $0.01 per credit, as described in the article on the Copilot pricing model, that works out to roughly $31.52. The API doesn’t do that step for you.
The policies
My tenant has one policy named “All Users Policy.” The name sounds like everyone. The type in the response is selectedGroupsSpendingPolicy, though, and the policy applies to exactly one security group. If you go by the name alone, you read it wrong. The API shows the actual type, the assigned group, the four governed services (cowork, workIQ, sharePointCopilot, oneDriveCopilot) and the limits. Both limits are set to unlimited here.
An alert is attached to the same policy: enabled, with an absolute threshold of 25,000 credits. Percentage thresholds are also possible, as a whole number from 1 to 100 according to the documentation.
Why can’t someone use a service?
The user balance answers a support question that otherwise means clicking through the admin center. For the account I queried, this came back:
"unevaluatedServices": [
{ "serviceId": "cowork", "reason": "noApplicablePolicy" },
{ "serviceId": "workIQ", "reason": "noApplicablePolicy" }
]
No policy applies to this account for Cowork and Work IQ, and the list of service balances is empty. The only policy in the tenant applies to a single group. I read this as a missing assignment rather than a limit that was reached. I saw that on one account only, so check it against your own users.
A privacy warning: the list of alert recipients returns complete user objects, in my case including a phone number and a secondary address. Treat the result as personal data and store only what you need.
What happens when you write?
Write calls check a lot before they change anything. The error responses show the rules. I triggered ten of them on purpose. The last row (409) came when I tried to change the billing method of a disabled test policy that had no audience. My test plan allowed for either a 200 or a 409:
| What I tried | Response | Meaning |
|---|---|---|
Update a policy without If-Match |
428 | The policy’s ETag is required |
| Update with a stale ETag | 412 | The ETag no longer matches, for example because the policy changed in the meantime |
Change the status field with PATCH |
400 | The server manages status |
| Add an unknown service | 400 | invalidService |
| Disable the last enabled service | 409 | atLeastOneServiceRequired |
| Alert threshold of 150% | 400 | invalidAlertThreshold |
| Enable alerting without recipients | 400 | alertRecipientsRequired |
| Recipient who isn’t a user in the tenant | 400 | alertRecipientNotFound |
| Remove the last recipient while alerting is enabled | 400 | alertRecipientsRequired |
| Remove the last assigned group | 409 | lastAssignedGroup |
| Change the billing method of the test policy | 409 | invalidTransition; the cause is open, Microsoft uses this code for a conflict during a concurrent state change |
What that means for your scripts: every change to an existing policy starts with a read to get the current ETag and ends with a check of the response. Also, you can’t delete a service. According to the documentation, you set it to disabled, and the entry stays.
What do you use the API for?
The dashboard in the admin center answers the question “What has been consumed?” The API pays off when you have a different question. The points below are ideas that follow from the responses. I built the inventory dashboard further down, and I haven’t tried the rest.
How do you get the data into your own systems?
The responses are JSON, so you can write them wherever your team already works: a list, a database, a ticketing system or a flow. A nightly run could store the credit balance, which gives you the history the API doesn’t have. A ticket could be opened when a user shows noApplicablePolicy. And because policies can be read and changed, you can describe them in a file and compare that with the actual state before anyone changes them in the admin center.
What can you analyze in more detail?
The API gives you credits, not money. You can take the price from your contract, calculate each billing method separately and run scenarios: What does it cost if the pay-as-you-go group consumes twice as much? How far do the prepaid credits go at the pace of the last few weeks? Who is the biggest consumer per service? Answers like these need your own assumptions, and the API supplies the numbers for them.
What do you combine the data with?
The biggest benefit comes when you join the responses with data Microsoft doesn’t send along:
- Group members from Microsoft Graph: Only then do you know how many people a policy actually covers. My collector script queries the members of the assigned groups for this.
- Directory data such as department or cost center: This lets you allocate consumption to business units.
- License assignments and usage reports: Who has a license but doesn’t use it, and who consumes credits without a policy?
- Your own budgets: Credits set against finance’s plan.
Is there a ready-made dashboard?
This test produced a tool: the Copilot Cost Inventory. Two collector scripts (PowerShell for Windows, zsh for macOS) are read-only and send nothing but GET requests. The result is a JSON file. You drop it onto a single HTML file that runs offline in the browser and uploads nothing. There you see the credit balance, consumption per service and per policy, per-user limit usage, the top consumers and a diagnostic of every API call. I tested the macOS script. I haven’t run the PowerShell script for Windows myself.
In the Lookbook, the dashboard runs with made-up sample data from a fictional tenant, so you can try it there. The interface is in English. You can download the package with the dashboard file, both scripts and instructions there, free and without signing in. The source code is on GitHub under the MIT license. It’s a community project by Copilot Coaching and isn’t affiliated with Microsoft.
Two notes before you use it on your own tenant:
- The generated JSON file contains names, sign-in names, consumption per person and alert recipients. Don’t share it and don’t put it in a repository.
- On the first sign-in, the script asks an admin to consent to permissions for the “Microsoft Graph Command Line Tools.” That consent stays in the tenant until an admin removes it. The script points this out itself.
What should you know before you use it?
- Permissions: Almost all operations support delegated permissions only, meaning a sign-in with a work or school account. Only the credit balance can also be read with an application permission. Creating a policy additionally requires the Billing Administrator or Global Administrator role.
- Beta: Fields and error codes can change. Re-check a script after Microsoft makes changes.
- One tenant: I tested the API in a single test tenant. I haven’t measured how it behaves in large tenants with many policies.
- Write with care: Try every change on a test policy of your own first, the way I did.
If you want to put the API to work in your tenant or join the analysis with your own data, that’s a job for coaching for IT teams and admins.
Sources
- Microsoft Learn: Microsoft 365 Copilot Cost Management API overview, costManagementRoot: getTenantCreditBalance
- Microsoft Learn: Understand usage-based billing and cost management for Copilot Credits
- My own calls on October 29, 2026 in a test tenant, 62 calls against all 22 documented operations
As of October 29, 2026. The API is in beta, and fields, permissions and error codes tend to change without notice.