Skip to content
Esc
↑↓navigate↵open⌘Jpreview
Dashboard
On this page

Rate limit policy

Understand and implement SuperTokens rate limit policies for managed and self-hosted services.

Overview

The following page describes how rate limits apply during SuperTokens API calls.

For managed service

The SuperTokens core of a managed account is rate limited on a per app and per IP address basis. This means that if you query the core for app1 using the same IP address, the rate limit kicks in, and you get a 429 status code back from the core. However, if you query the core using different IP addresses or for a different app, the rate limit of that does not interfere with the previous requests (that had another IP or was for another app).

Free tier

The free tier of the managed service has a rate limit of 50 requests per second with a burst of 50 requests per second (with no delay). This should be enough for 5-10 concurrent sign in / up (each sign in API call queries the SuperTokens core multiple times).

Paying users

If you are a paying user for SuperTokens, the rate limit and the burst limit adjust dynamically based on your usage (with a minimum of a 100rps). You should not see any 429s unless there is a significant spike in requests.

Special case

The /hello API exposed by the core is commonly used for health checks. This API does not require any API key, and has its own rate limit of 5 requests per second per app (regardless of the IP address querying it). This is independent to the rate limit described above, and cannot change.

For self hosted

The SuperTokens core has no rate limit other than for the /hello API (which is 5 requests per second per app). You can optionally cap concurrent requests per connection URI domain, as described in Concurrency cap per connection URI domain. You are free to add rate limits to the core by using a reverse proxy like Nginx.

If you want to implement rate limiting policy similar to the managed service described above, add the following to your http and server block in the nginx.conf file:

http {
    # other configs..
    
    map $request_uri $limit_req_zone_key {
        "~^/(appId|appid)-(\w+)/?" $binary_remote_addr:$2;
        default $binary_remote_addr;
    }

    limit_req_zone $limit_req_zone_key zone=mylimit:10m rate50/s;
    limit_req_status 429;

    # other configs..

    upstream supertokens {
        server localhost:3567;
    }

    server {
        limit_req zone=mylimit burst=50 nodelay;

        # other configs..

        listen 0.0.0.0:80;

        location / {
            proxy_pass http://supertokens;
        }
    }
}

In the above, the core adds a rate limit per app per IP address.

Concurrency cap per connection URI domain

The concurrency cap requires Core 12.4.0 or later and is off by default.

A connection URI domain is the host name that backends use to reach the core. A self-hosted core usually has a single one, which all its apps share.

The cap limits how many requests from one connection URI domain the core processes at the same time, so that one domain can’t take all the threads of the request pool (max_server_pool_size). The core enforces it only while the pool is close to full:

  • While more than concurrency_cap_reserved_pool_percent of the pool’s threads are free, the core rejects no request.
  • Once fewer threads are free, a request that would take its connection URI domain above max_concurrent_requests_per_cud requests in flight gets an immediate 429 response with a Retry-After: 1 header, instead of a thread.

For example, with max_server_pool_size: 10 and the default reserve of 25%, the core keeps 2 threads in reserve and enforces the cap once more than 8 requests are in flight.

Only requests that pass the API key check for an app that exists under the connection URI domain count toward its cap. Requests to routes without an API key check, such as /hello and /.well-known/jwks.json, and requests for an app that doesn’t exist count toward the pool’s usage but are never capped. Requests on the admin port don’t count at all.

Configuration Environment variable Default Description
max_concurrent_requests_per_cud MAX_CONCURRENT_REQUESTS_PER_CUD 0 Maximum concurrent requests per connection URI domain. 0 turns the cap off. Can’t exceed max_server_pool_size.
concurrency_cap_reserved_pool_percent CONCURRENCY_CAP_RESERVED_POOL_PERCENT 25 Share of max_server_pool_size, in percent, kept free before the cap applies. Must be between 1 and 100. 100 enforces the cap at all times.
docker run \
    -p 3567:3567 \
    -e MAX_SERVER_POOL_SIZE=20 \
    -e MAX_CONCURRENT_REQUESTS_PER_CUD=8 \
    -d "$SUPERTOKENS_IMAGE"
# You need to add the following to the config.yaml file.
# The file path can be found by running the "supertokens --help" command

max_server_pool_size: 20
max_concurrent_requests_per_cud: 8

The backend SDK retries requests that get a 429 response. The core’s /requests/stats API reports concurrentRequestsRejected, the number of an app’s requests that got a 429 from the cap, and concurrentRequestsInFlight, which covers the whole connection URI domain, so every app on the same domain reports the same value.

API reference

API schema and response details