Skip to content
Last updated

M2M Token Limits

Access tokens obtained through the Client Credentials flow are subject to per-application limits in addition to your tenant's monthly allotment.

These limits exist to protect your tenant. The monthly allotment is shared by every integration running against your Rhythm tenant, so a single misbehaving application can exhaust it and leave every other integration unable to authenticate. Per-application limits contain that failure to the application causing it.

The limits

LimitDefaultWindow
Per application, per hour2 tokensUTC clock hour
Per application, per day4 tokensUTC calendar day
Per tenant, per month1,000 tokensUTC calendar month

Windows are calendar-aligned in UTC, not rolling. The hourly window resets at the top of each UTC hour, and the daily window resets at 00:00 UTC — not 24 hours after your first request.

A correctly designed integration requests one token per day and reuses it, so the daily limit is roughly four times what normal operation requires.

What a rejected request looks like

When a limit is exceeded, the token request is rejected and no token is issued:

{
  "error": "invalid_request",
  "error_description": "M2M access token quota exceeded (max 2 per hour). Access tokens are valid for 24 hours - cache and reuse them."
}

The description states which limit was reached.

How rejections affect each limit

A rejected request affects the two limits differently, and the difference matters:

Rejected request counts?
Per-application hourly and daily limitsYes — these count requests, not tokens issued
Your tenant's monthly allotmentNo — only tokens actually issued are counted
Rejections do not consume your monthly allotment

If an integration requests tokens in a loop and is rejected, those rejections cost you nothing from your monthly allotment. Only the tokens actually issued count against it.

This is deliberate: the per-application limits exist so that one misbehaving integration cannot drain the allotment shared by every other integration on your tenant.

Do not retry after a quota rejection

Retrying still works against you. Because the hourly and daily counters record requests, each retry pushes your application further past its limit and delays the point at which it can obtain a token again.

An integration retrying in a loop typically receives only 2 tokens in a day — fewer than the 4 it would have received by not retrying at all.

Treat a quota rejection as non-retryable. It is not transient and it will not succeed on retry.

This is the opposite of a 429 Too Many Requests response, where retrying with exponential backoff is the correct behavior. A 429 means slow down; a quota rejection means you already hold a token you should be reusing.

Staying within the limits

  1. Request an access token
  2. Cache it securely in memory or a server-side store
  3. Reuse it for every API call until it is close to expiring
  4. Request a new one only then — tokens are valid for 24 hours
  5. If a token request is rejected for quota, stop requesting and surface the error

Common causes of exceeding the limits:

  • Requesting a new token on every API call
  • Requesting a new token each time a scheduled job runs
  • Requesting a new token per end user
  • Running several instances or workers that each request their own token without a shared cache
  • Retrying automatically after a rejection
Multiple instances

If your integration runs as several instances, containers or workers, they all share one application's limits — the limits apply to the application, not to the process.

Either share a cached token between them, or request a higher limit for that application.

Requesting a higher limit

Limits are configured per application and can be raised where there is a legitimate need — for example an integration that genuinely requires more than four tokens a day across coordinated workers.

Contact Rhythm support with the application name and your expected token usage.

Summary checklist

  • Tokens are cached and reused for their full 24-hour lifetime
  • A single token is shared across instances, or a higher limit has been requested
  • Quota rejections are surfaced, not retried
  • 429 responses are retried with backoff — quota rejections are not