← Back to blog

OneNote API Rate Limits: Graph Throttling Lessons

Ensky Lin8 min read

For a Microsoft Graph OneNote integration, start with the documented delegated limits: 120 requests per minute, 400 per hour, and five concurrent requests for each app-and-user pair. A 429 Too Many Requests response for the OneNote resources in that table does not include Retry-After, so the client needs its own admission control and a backoff policy.

There is an important qualification: Microsoft's limits page also lists an app-only row of 240 requests per minute, 800 per hour, and 20 concurrent requests per app, but the OneNote API overview says that the OneNote API does not support app-only authentication. The numbers are still worth understanding as published documentation; they are not a license to build an app-only OneNote integration.

Which OneNote API rate limit applies to your request?

Microsoft's service-specific throttling limits list these limits for the OneNote resource family:

Authentication context listed by Microsoft Requests per minute Requests per hour Concurrent requests
Delegated: per app per user 120 400 5
App-only: per app 240 800 20

The table is not a promise of throughput. Microsoft says Graph requests can be evaluated against global and service-specific limits, and the first limit reached triggers throttling. It also says that the service-specific values can change.

For a request made on behalf of a signed-in OneNote user, use the delegated row as the published baseline. Do not add the app-only row to the delegated row and call the result a Microsoft quota. A product can impose an extra local guardrail, but that is its own policy and must be described that way.

Does the OneNote API support app-only authentication?

No. Microsoft's OneNote API overview says that the Microsoft Graph OneNote API does not support app-only authentication; it describes access through delegated permissions.

That makes the app-only row in the limits table easy to misread. The two Microsoft pages are not evidence that a developer can choose either authentication flow for a new OneNote integration. For a practical OneNote client, delegated authentication is the supported path, and the 120/400/five row is the relevant published baseline.

We also use delegated permissions. Our additional internal safeguards should not be confused with a claim about Microsoft's authentication support or enforcement model.

What should you do after a OneNote API 429?

The OneNote section of Microsoft's limits page states that the listed resources do not return Retry-After with a 429. That is an exception to the usual Graph throttling guidance.

Microsoft's general throttling guidance recommends exponential backoff when Retry-After is absent. A resilient handler should:

  1. stop admitting new work for the affected upstream path after a 429;
  2. retry with exponential backoff and jitter, not an immediate loop;
  3. bound the retry by elapsed time or attempts, then surface or reschedule the failure; and
  4. move long waits out of a request handler into durable work scheduling.

Backoff recovers an individual request. It does not decide whether the next request should be admitted. That decision needs shared state when several workers use the same upstream budget.

Why can individually correct retries still create a retry storm?

This is implementation advice, not a Microsoft specification. Independent retries can align after a shared 429, causing several callers to re-enter the same constrained path at once. A better backoff curve alone does not coordinate those callers.

Coordinate the cooldown at the limiter, not at each call site. When sustained 429s appear, the limiter refuses local callers for a shared cooldown window. This collapses N independent retry loops into one decision. A call site cannot do this, because it only sees its own timeline.

Reserve capacity for work someone is waiting on. We send two kinds of traffic to Graph: interactive notebook scans and background migration jobs. Without a reservation, a large migration consumes the locally admitted budget and a new user's first scan waits indefinitely.

Which wait should a multi-layer limiter return?

When a limiter checks more than one local policy and both are saturated, it has to report a single wait to the caller. Returning the shortest wait is wrong: if another layer stays saturated longer, resuming at the shorter wait produces an immediate second refusal.

The request is only safe to admit when every layer has capacity, so the reported wait is Math.max. Stated generally: when several constraints gate the same action, report the longest wait, never the shortest.

Can Graph throttle headers guide a OneNote limiter?

Only as optional telemetry. It is not a OneNote response contract.

Microsoft documents x-ms-throttle-limit-percentage and x-ms-throttle-scope in the identity and access section of its service-specific limits page, not in the OneNote section. That documentation says:

  • x-ms-throttle-limit-percentage is returned only after an application has consumed more than 0.8 of a limit. Its documented range is 0.8–1.8; values above 1.0 mean that some incoming requests are already being throttled.
  • x-ms-throttle-scope is a structured value: <Scope>/<Limit>/<ApplicationId>/<TenantId|UserId|ResourceId>. Its documented scope values include Tenant_Application, Tenant, and Application; the limit component can be Read, Write, or ReadWrite.

Those statements do not establish that OneNote returns either header. In particular, do not assume x-ms-throttle-limit-percentage is present on every successful OneNote response, or parse x-ms-throttle-scope as invented User and Application enum values.

If you collect the headers, record their absence as well as their value and keep 429 handling correct when neither one appears. Do not state a production emission rate without a dated, reproducible query result.

Does Graph batching reduce OneNote rate-limit usage?

No. JSON batching reduces HTTP round trips; it does not turn many quota evaluations into one. Microsoft says each request in a JSON batch is evaluated individually against the applicable throttling limit.

The batch envelope can return HTTP 200 while one or more sub-responses are 429. Microsoft's batch throttling guidance says to retry each failed request, and notes that SDKs do not automatically retry throttled requests from a batch.

What happens when a batch envelope succeeds but sub-requests fail?

A successful $batch envelope does not establish that every sub-request succeeded. Inspect and classify every sub-response before marking the work complete.

Treat the sub-response as the retry unit:

  • Successful sub-response: retain its result.
  • 429 sub-response: reschedule that item with the shared backoff policy.
  • Other failed sub-response: classify it before deciding whether a retry is safe.

This avoids replaying completed work and makes partial failure visible.

What is our conservative limiter model?

This section describes a dated internal implementation observation, not a Microsoft specification.

As checked on August 8, 2026, our user-scoped Durable Object checks a delegated-sized local bucket (120/400/five) and a second, application-labelled bucket (240/800/20) atomically. It returns the longest required wait when both local policies prevent work, and it gives interactive scans capacity that background migrations do not consume.

The second bucket is deliberately conservative. Because the object is scoped to one user, it is not an aggregate app-wide counter across users. It must not be cited as proof that Microsoft applies the app-only row alongside the delegated row. Its role is to make our queues more predictable when upstream throttling occurs.

We also write an upstream 429 back to the shared local limiter. This makes the cooldown visible to other queue consumers for that user instead of allowing each consumer to infer that capacity is still available. The relevant implementation is reviewable in src/lib/config.ts, src/lib/migration/graph-client.ts, and src/worker/durable-objects/graph-rate-limiter-do.ts.

FAQ

Can I use higher OneNote API rate-limit numbers found in a tool or blog?

Uncited higher per-user limits and claims that OneNote 429 responses include Retry-After do not match Microsoft's current documentation for the OneNote resources in the service-specific limits table.

Treat an uncited number as an observation or a claim, not as Microsoft's contract. Use the published delegated baseline for a supported OneNote client, measure your own behavior, and revisit the configuration only with dated evidence. Microsoft explicitly says the published limits can change.

Can I use the 240/800/20 row as extra headroom for delegated OneNote calls?

No. The OneNote overview says app-only authentication is unsupported, and the limits page does not say a delegated request must or may combine both rows. An extra local limiter can be prudent, but it is a product decision rather than an official quota calculation.

Should I wait for x-ms-throttle-limit-percentage before slowing down?

No. It is not documented as a OneNote header and is only documented after use exceeds 0.8 of a limit in a different service section. A correct OneNote handler works without it.

What is the next step if I am moving notes instead of building an integration?

Compare the available routes in our guide on how to migrate from OneNote to Notion, or see how we migrate OneNote to Notion. Test a representative section before you move a full notebook.

Sources

Microsoft says published limits can change. This article distinguishes Microsoft documentation from Note Bridge's implementation observations and implementation advice.

Ready to migrate your notes?

Try Note Bridge free — migrate up to 20 pages with no credit card required.

Get started for free