Unity Services rate limits requests per caller identity — per player, or per service account. Routing all your players' requests through your own game server makes them share one identity and one quota, which returns 429. Fix it by letting player devices call Unity Services directly, and by using a Service Account for any server-to-server calls.
Unity Services rate limits requests based on who is calling, not how many requests your game makes overall. When your own server proxies requests on behalf of many players, those requests stop being counted per player and start being counted against a single shared identity, which exhausts the quota quickly as your player base grows. The fix is to let player devices talk to Unity Services directly, and to authenticate any genuine server-to-server calls with a Service Account.
What the error looks like
The service returns HTTP status 429 with a Retry-After header and a JSON body in application/problem+json format:
{ "type": "problems/basic", "title": "Too Many Requests", "status": 429, "detail": "...", "requestId": "..."}The title field may also read Quota Exceeded or Ratelimit Exceeded.
Always honour the Retry-After header. It gives the number of seconds to wait before retrying. Retrying immediately, or without backoff, will keep returning 429.
Why does this happen?
Unity Services apply rate limits per caller identity. The gateway keys each limit on one of:
-
playerId— for requests authenticated with a player access token. -
serviceAccountId— for requests authenticated with a Service Account. - Source IP address — as a fallback, for unauthenticated requests such as anonymous sign-up.
This normally works well, because each player has their own quota. It breaks when you put your own server in the middle. Two common patterns cause it:
- You route all player authentication requests through your own centralised game servers before they reach Unity. Sign-in requests are unauthenticated by definition — there is no player token yet — so they are keyed on your server's public IP address. Every player draws on one bucket instead of their own, and a growing player base exhausts it quickly.
- An external application, such as a Discord bot, acts as an intermediary and rapidly polls Cloud Save to check player statuses. Every one of those reads is attributed to the application's own identity, not to the player whose data it is reading, so they all draw on one quota.
How do I fix it?
1. Let player devices call Unity Services directly
Design your architecture so player devices reach Unity Services themselves, rather than routing everything through your central server. Each device then authenticates as its own player and gets its own quota.
- Example 1. A player's console contacts Unity Authentication directly to sign in. The client then relays whatever parts of the response you need to your game servers.
- Example 2. Instead of a Discord bot polling Cloud Save to check a player's status, add an in-game button for the player to sync their permissions. The player's device invokes Cloud Code, which reads that player's Cloud Save data and notifies your Discord bot to update the permissions. The read is now attributed to the player who triggered it, and it happens once on demand rather than on a polling loop.
2. Use a Service Account for server-to-server calls
If your game servers genuinely must act on behalf of players, do not reuse the player's access token to call services from your server. Instead, once you have validated that the player is signed in to Unity Authentication, authenticate with a Service Account.
This works for two reasons. The gateway re-keys the rate limit to the serviceAccountId from the token, decoupling it from your server's IP address; and Service Accounts are allocated substantially higher quotas than individual players. Limits differ per service — see the API reference for the service you are calling.
A Service Account raises the ceiling — it does not remove it
This is the part that catches people out. A Service Account's quota is a single shared limit, not one per player, exactly like the IP-keyed bucket you are escaping. It is simply much larger. It does not grow as your player base grows.
So if you route every player's traffic through one Service Account, you will hit 429 again as you scale; you have moved the wall further back rather than removed it.
Step 1 is the change that scales, because it gives every player their own quota. Treat a Service Account as headroom for genuine server-to-server work, not as a way to keep proxying player traffic.
Where is this documented?
Each service publishes its own rate limits, and they differ — including what the limit is keyed on. Check the reference for the service you are calling:
- Player Authentication Client API — see the Rate Limits section. Most endpoints are limited per IP address.
- Cloud Code Client API — see the Limits section. Limited per player.
- Cloud Save API — see the Rate Limits section. Limited per player.
-
Response status and error codes — what
429and the other Unity Services Web API status codes mean.