I wrote an API rate limiter to use with Last.fm's API.
Last.fm's Tos states that I cannot make more than 5 requests per originating IP address per second, averaged over a 5 minute period.
Here is the class I wrote:
public class RateLimiter
{
private static readonly List<DateTime> _requests = new List<DateTime>();
private const double _perMillisecond = 1000.1;
private const int _rateLimit = 5;
private const int _rateLimitCooldownMs = 500;
public static void CheckLimiter()
{
_requests.Add(DateTime.Now);
var requestsDuringRateLimit = _requests.Where(w => (DateTime.Now - w).TotalMilliseconds < _perMillisecond).ToArray();
if (requestsDuringRateLimit.Count() >= _rateLimit)
{
Thread.Sleep(_rateLimitCooldownMs);
_requests.Clear();
Console.Clear();
}
}
}
The CheckLimiter
method is called before the HttpWebRequest
is initiated, is this a good way to limit API requests?
This is quite fine in my opinion. Except that, there is a bug in this code. This is because what if each request is done more than a second after one another? It will never go inside that if
block. Thus, some kind of memory leak because the _requests
will grow larger over time and possibly never be cleared if my scenario above always happens.
Example:
for (int i = 0; i < 100; i++)
{
RateLimiter.CheckLimiter();
Thread.Sleep(2000);
}
What you can do is to remove entries in your _requests
that are exceeding the 1 second rule like adding this line at the end of your method.
if (_requests.Count != 0)
{
//remove irrelevant/expired entries
_requests.RemoveAll(date => (DateTime.Now - date).TotalMilliseconds >= _perMillisecond);
}