How do I throttle my site's API users? How do I throttle my site's API users? apache apache

How do I throttle my site's API users?


Ok, there's no way to do what I asked without any writes to the server, but I can at least eliminate logging every single request. One way is by using the "leaky bucket" throttling method, where it only keeps track of the last request ($last_api_request) and a ratio of the number of requests/limit for the time frame ($minute_throttle). The leaky bucket never resets its counter (unlike the Twitter API's throttle which resets every hour), but if the bucket becomes full (user reached the limit), they must wait n seconds for the bucket to empty a little before they can make another request. In other words it's like a rolling limit: if there are previous requests within the time frame, they are slowly leaking out of the bucket; it only restricts you if you fill the bucket.

This code snippet will calculate a new $minute_throttle value on every request. I specified the minute in $minute_throttle because you can add throttles for any time period, such as hourly, daily, etc... although more than one will quickly start to make it confusing for the users.

$minute = 60;$minute_limit = 100; # users are limited to 100 requests/minute$last_api_request = $this->get_last_api_request(); # get from the DB; in epoch seconds$last_api_diff = time() - $last_api_request; # in seconds$minute_throttle = $this->get_throttle_minute(); # get from the DBif ( is_null( $minute_limit ) ) {    $new_minute_throttle = 0;} else {    $new_minute_throttle = $minute_throttle - $last_api_diff;    $new_minute_throttle = $new_minute_throttle < 0 ? 0 : $new_minute_throttle;    $new_minute_throttle += $minute / $minute_limit;    $minute_hits_remaining = floor( ( $minute - $new_minute_throttle ) * $minute_limit / $minute  );    # can output this value with the request if desired:    $minute_hits_remaining = $minute_hits_remaining >= 0 ? $minute_hits_remaining : 0;}if ( $new_minute_throttle > $minute ) {    $wait = ceil( $new_minute_throttle - $minute );    usleep( 250000 );    throw new My_Exception ( 'The one-minute API limit of ' . $minute_limit         . ' requests has been exceeded. Please wait ' . $wait . ' seconds before attempting again.' );}# Save the values back to the database.$this->save_last_api_request( time() );$this->save_throttle_minute( $new_minute_throttle );


You can control the rate with the token bucket algorithm, which is comparable to the leaky bucket algorithm. Note that you will have to share the state of the bucket (i.e. the amount of tokens) over processes (or whatever scope you want to control). So you might want to think about locking to avoid race conditions.

The good news: I did all of that for you: bandwidth-throttle/token-bucket

use bandwidthThrottle\tokenBucket\Rate;use bandwidthThrottle\tokenBucket\TokenBucket;use bandwidthThrottle\tokenBucket\storage\FileStorage;$storage = new FileStorage(__DIR__ . "/api.bucket");$rate    = new Rate(10, Rate::SECOND);$bucket  = new TokenBucket(10, $rate, $storage);$bucket->bootstrap(10);if (!$bucket->consume(1, $seconds)) {    http_response_code(429);    header(sprintf("Retry-After: %d", floor($seconds)));    exit();}


Simplest solution would be to just give each API key a limited number of requests per 24 hours, and reset them at some known, fixed, time.

If they exhaust their API requests (ie. the counter reaches zero, or the limit, depending on the direction you're counting), stop serving them data until you reset their counter.

This way, it will be in their best interest to not hammer you with requests.