API Throttling on Requests Per Minute

In my previous blog post I have discussed API designing and versioning. Now I am going to build a simple algorithm to restrict API access based on requests per minute using redis

Very often, as an API provider we need to control request traffic based on certain criteria, like account subscription, time interval or requests per day or per month. Redis provides a key expiry functionality based on TTL (time to live) and using this we can implement the requests per minute feature.

# ruby redis client expire method.
redis_client.expire(key, time_to_expire_in_secs)
# i.e
redis_client.expire("1", 60)

Here is the implementation of API request counting store.

  • incr method increments the key and sets the expiry for the key if it’s set for the first time.
  • threshold? method checks the value after incrementing it.
class ApiRpmStore

  TIME_TO_EXPIRE = 60 # 1 min

  class << self
    attr_accessor :redis_client

    def init(config = {})
       self.redis_client = Redis.new(:url => "redis://#{config['host']}:#{config['port']}/#{config['database']}")
    end

    def incr(key)
      val = redis_client.incr(key)
      redis_client.expire(key, TIME_TO_EXPIRE) if val == 1
      val
    end

    def threshold?(key, threshold_value = 0)
      self.incr(key) < threshold_value
    end

  end

end

Test this using the console.

irb> ApiRpmStore.init({'host' => 'localhost', port: 6379, database: 0})

# threshold value is 1 for key 'user-1'
irb> ApiRpmStore.threshold?('user-1', 1) # return true

irb> ApiRpmStore.threshold?('user-1', 1) # return false

Now implement the before action methods in the controller in which we are going to validate for requests per minute. If the requests per minute validation fails, then we return a response ‘422: too many requests’ with some helper url, like the subscription or license page.

class Api::ApiController < ActionController::Base

  private

  def authenticate
    authenticate_or_request_with_http_token do |token, options|
      @user = User.where(api_key: token).first
    end
  end

  def validate_rpm
    if ApiRpmStore.threshold?(@user.id.to_s, @user.request_per_min) # request_per_min is  threshold for
      render json: {help: 'http://mysite.com/plans'}, status: :too_many_requests
      return false
    end
  end
end

This is the events controller on which we are going to throttle the API requests. If you want to apply rate limit to all api then add before_action :validate_rpm to your base api controller (in my case it is the ‘Api::ApiController’ ).

class Api::V1::EventsController < Api::ApiController
  before_action :authenticate
  before_action :validate_rpm

  respond_to :json

  def index
    @events = Event.all
    respond_with @events
  end
end

You can find Rails application code sample on github

Advertisements
This entry was posted in Ruby on Rails and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s