Credit free load test · Microsoft 365 Copilot

What a Copilot Studio agent really does under load

Scope: These results cover Copilot Studio's Standard Harness only—not GitHub Harness. The measured limits and performance should not be assumed to apply to GitHub Harness.

140,405 real messages pushed through a published agent and measured end to end. The documented quota is 100 generative messages a minute. It sustained 175 a minute, and every refusal arrived looking like a success. The headline 50,000 message run was performed twice, on different days, and cost nothing either time. A community project built with GitHub Copilot, shared under the MIT licence; not a Microsoft product or supported by Microsoft.

Loading the evidence. Every conversation is embedded in this page, so it downloads once and then works offline with no server behind it. The numbers and charts below fill in when it lands.

The whole result in one chart

Every message in the corpus, grouped by how loaded the environment was during the busiest minute that message belonged to. Below about a minute the service answers almost everything. Above it, refusals climb and never recover. This one shape is the finding: the limit is arrival rate, and it has a hard knee.

What was tested

A single generative prompt, "What is the best movie right now", sent through the Microsoft 365 Agents SDK as an authenticated Microsoft 365 Copilot licensed user. That billing path is charged at no cost, so every message here was credit free, and that was confirmed in the Power Platform admin centre after each campaign rather than assumed. Each turn opened its own conversation, modelling distinct users rather than one person asking repeatedly, and recorded the send time, the first service response, the finished answer and the full answer text.

Documented limits against what actually happened

Volume is not the problem. Simultaneity is.

The single most useful conclusion here, so it is worth isolating. This environment will absorb enormous daily volume with almost no failures. What breaks it is a crowd arriving inside the same minute.

Volume over a day: no problem at all

Measured per clock hour across the sustained campaigns, against a documented cap of 2,000 messages an hour. Charted in full further down this page.

The same 1,000 people pressing send together

Every burst of exactly 1,000 simultaneous messages ever fired at this agent. In each of these, 1,000 users pressed send at the exact same instant, inside the same second, with no spacing whatsoever. This is the worst case the service can be handed.

So how many can press send at once?

One important nuance: this is a rate limit, not a concurrency limit. The same 1,000 messages fired at once held 1,000 connections open and got 194 answers; spread over one minute they held only 89 and got 193. Eleven times less concurrency, same result. The service counts messages per minute, not simultaneous users. Simultaneity only hurts because 1,000 people pressing send together is 1,000 messages in one minute. So more client capacity cannot help, and spreading arrivals over time is the only lever that works.

Is it ~182 per minute, or ~182 per longer period?

Per minute, and it replenishes continuously. The admission constant is not an allowance you exhaust. This is the most common misreading of the sizing table below, so it is measured directly: the largest number of answers ever delivered inside a sliding window of each width, across the whole corpus.

Three numbers that are easy to conflate. Burst depth is what a single instantaneous wave gets before the door shuts. Replenishment rate is what keeps arriving every minute thereafter. There is no longer-period cap: the documented 2,000 an hour is not enforced, and one run delivered .

And this is not a count of concurrent conversations. Steady-state concurrency during the sustained run was about 15 simultaneous generations, because each answer takes roughly 4.6 seconds. The service caps arrival rate, so concurrency is an outcome of latency rather than a setting. A burst only looks like a concurrency event because 1,000 people pressing send together is, by definition, 1,000 messages inside one minute.

"But 250 a minute fails, so how can 9,000 an hour work?"

The arithmetic looks contradictory only if there are two limits. There is one. Everything the environment does is the same number, roughly 180 answers a minute, multiplied or divided by time. 9,000 in an hour is 150 a minute, which is under it. 250 a minute is over it. The hour never had a budget of its own.

The column that settles it is answers a minute. Every message in the corpus is banded by the rolling 60 second load the environment was actually under when it was sent, then that band's delivered rate is measured.

Below the knee, answers a minute tracks offered a minute exactly and nothing is refused. Above it, the answers column flattens and stops responding to pressure entirely: offering 358 a minute delivers 181, and offering 1,050 a minute delivers 224. The surplus is discarded, not queued, which is the single most important operational point in this report. Pushing harder does not buy throughput, it only converts would-be answers into refusals.

How many users can get a generative answer right now?

The sizing question read the other way round. Pick the number of people you expect to press send inside the same minute, and read off the chance any one of them gets an answer rather than an error. Typical uses the median admission of ; the band is the worst and best admission ever measured, so it carries real run to run variance rather than implying false precision.

Read it as a per-person coin flip: at 500 simultaneous users each person has roughly a 36% chance of an answer and a 64% chance of the raw error dump. The number answered barely moves, because the environment admits a near constant number and refuses the rest. Everyone above the line gets refused in about 320 ms, faster than the roughly 4,000 ms a real answer takes, so a failed crowd fails quickly and loudly.

Does adding user accounts help? No.

The decisive test

One account floods the agent while a completely different account asks the same agent the same question at a trivial six messages a minute. Both accounts were proven working alone against an idle environment first, so a refusal here cannot be an access problem.

Every stage, split by account

Predicted against measured

Both predictions were written down before the run, as with E8.

Scope: this is a generative answer ceiling, not a tool ceiling

Everything measured in this report is a pure generative answer with no tool invoked. That was deliberate, and by the end it was enforced: every tool was deleted from the agent and the live endpoint was probed before those runs to prove the planner was handed nothing. So the ~175 a minute figure is the ceiling on the agent thinking and replying, and nothing else.

If your agent calls tools, this is not the only ceiling you face. Each tool call goes out through a connector to a real API, and those carry their own independent throughput limits that stack on top of the environment budget measured here. Any one of them can bite first, so a tool-using agent can sit comfortably inside the generative quota and still fail.

CeilingRoughly where it sitsScope
Generative orchestration (measured here)~175 a minuteper Dataverse environment
Connector throttlingcommonly 6,000 per 5 minutes per connection where the connector publishes nothing lower, but many publish far lowerper connection
Power Platform requests40,000 per 24 h on paid Power Platform licences, 6,000 on Microsoft 365 licences, 250,000 on the Copilot Studio base offerper user per 24 h
The downstream service itselfGraph, Exchange and SharePoint each throttle per user and per tenantper user / tenant

Three points worth carrying into a sizing conversation

1. Connector limits are per connection, not per environment. Every user of the agent sharing one connection shares that quota, so a connector can throttle at a load the generative budget would have absorbed comfortably. Each connector publishes its own figures on its Microsoft Learn reference page and they vary widely, so look up the specific ones your agent uses rather than assuming the default.

2. The per-user Power Platform entitlement has a trap in it. Copilot Studio counts API calls to Power Automate flows against the user's daily request allocation, and a user holding only a Microsoft 365 licence sits on the 6,000 per 24 hours tier, not 40,000. An agent that fires a flow on every turn burns that allocation quickly.

3. The two failure modes look completely different, and only one is visible to normal monitoring. Generative refusals, across all , arrive as HTTP 200 with no Retry-After and are invisible to anything watching status codes. Connector throttling behaves conventionally: HTTP 429 with a Retry-After, which standard retry logic and platform monitoring both understand. A tool-using agent fails in two different ways, and the one this report is about is the one your monitoring will miss.

Honesty label: the tool and connector limits in this block are documented, not measured by this project. Everything else in this report is measured. If tool throughput matters to your scenario it deserves its own test, because this harness deliberately removed tools in order to isolate the generative path.

The findings

Outcome of every message sent