Rate Limiting for Stellar Horizon

Learn how not to burst over the available dynamic limit in a period of time for Stellar Horizon.

As a user of Stellar Horizon, you may run into rate limits from time to time. Stellar Horizon rate limits are intelligently designed to allow for burstable access. They do not naively enforce a rate limit via a raw quota mechanism or an average distribution over unit time.

Stellar Horizon includes a default rate limit of 3600 req/hour for dedicated node customers. We are, of course, happy to raise this limit if you encounter limiting as outlined in this document. Contact Support for help.

The rate limit for our shared node customers is 2000 req/hour. This limit is the current Blockdaemon policy and cannot be adjusted per user. Additionally, a rate limit set in nginx enforces a hard cap of 20 requests at any second.

It may be helpful to understand that Horizon’s rate limiting algorithm is the GCRA rate limit – while this may feel like unnecessarily technical details, it will aid in understanding if you want to understand the limit more completely.

Maintain Visibility with Headers

You can maintain visibility on your available quota for the current time period. If you adjust your request velocity based on the returned header values, you can ensure you never exhaust your rate limit.

Blockdaemon passes through headers without alteration as defined in the official Stellar documentation on rate limiting.

  • X-RateLimit-Limit – The Stellar documentation refers to this as “The maximum number of requests that the current client can make in one hour.”, but observation indicates this is the limit of calls in a given “window”. We can observe this in practice by paying attention to the header return values.
  • X-RateLimit-Remaining – The Stellar documentation refers to this as “The number of remaining requests for the current window.” This is accurate for observed use. This is the most important number to watch, indicating how many calls you may make until you encounter a rate limit.
  • X-RateLimit-Reset – The Stellar documentation refers to this as “Seconds until a new window starts.” This is also accurate for observed use. If you wanted to wait for a full call quota, you would like to delay by the window this indicates. However, the quota is dynamic and more easily understood as a cooldown rather than a hard lockout.

If you exceed your rate limit — X-RateLimit-Remaining reaches 0 — you will receive an HTTP 429 response code and the additional header retry-after, specifying a value in seconds that you should delay further requests.

Understanding Limits in Practice

The rate limit, in practice, effectively acts like a leaky bucket that slowly drains when water isn’t being added to it. Every request you make adds water to the bucket. If you add water faster than the bucket drains, you will overfill the bucket and hit your rate limit.

If you make 50 requests and wait three seconds before your next request, you will see the X-RateLimit-Remaining counter increase and the X-RateLimit-Reset decrease. Therefore, if you use enough requests in a time period to reach 0 remaining, you don’t need to wait for the full duration until a Reset.

This is easily understood from a terminal window.

In a terminal window, enter the following command:

curl -I MY_BLOCKDAEMON_NODE_ADDRESS/fee_stats?auth=MY_AUTH_TOKEN

Where MY_BLOCKDAEMON_NODE_ADDRESS is the node address given to you on the node card, and MY_AUTH_TOKEN is everything after “auth=” in your node address.

You will get a response that looks something like this:

server: nginx/1.18.0
date: Thu, 20 Aug 2020 20:53:01 GMT
cache-control: no-cache, no-store, max-age=0
vary: Origin
x-ratelimit-limit: 101
x-ratelimit-remaining: 100
x-ratelimit-reset: 2
access-control-allow-origin: *

If you run the command several times in rapid succession, you will see the remaining count decrease and the reset period increase, as below:

server: nginx/1.18.0
date: Thu, 20 Aug 2020 20:18:31 GMT
cache-control: no-cache, no-store, max-age=0
vary: Origin
x-ratelimit-limit: 101
x-ratelimit-remaining: 99
x-ratelimit-reset: 4
access-control-allow-origin: *
server: nginx/1.18.0
date: Thu, 20 Aug 2020 20:18:31 GMT
cache-control: no-cache, no-store, max-age=0
vary: Origin
x-ratelimit-limit: 101
x-ratelimit-remaining: 98
x-ratelimit-reset: 5
access-control-allow-origin: *

Similarly, if you wait a few seconds, you will see the reset period refresh to 0 and have a full quota again.

If you want to understand it interactively with a shell script, you can run this on his local machine:

#!/bin/bashcounter=1
while [ $counter -le 150 ]
do
curl -I <a class="c-link" href="https://my_blockdaemon_node_address/fee_stats?auth=MY_AUTH_TOKEN" target="_blank" rel="noopener noreferrer">https://MY_BLOCKDAEMON_NODE_ADDRESS/fee_stats?auth=MY_AUTH_TOKEN</a>
((counter++))
done

Conclusion

Stellar Horizon rate limits are dynamic and are not a simple quota or a naive division of available calls across the time period. By reading and implementing response headers into your code, you can ensure that you don’t burst over the available dynamic limit in a period of time.


Useful Resources

👋 Need Help?

Contact us through email or our support page for any issues, bugs, or assistance you may need.