If you don’t limit your API usage, bad users can DDOS your server.

If you don’t track API usage, how do you know if it is even used?

Apps like Twitter and Shopify have daily or monthly (30-day) API usage limits:

In this tutorial we will track API usage per user and add a 30-day max limit of API requests per user.

Example of API request being blocked by status 429 Too Many Requests:

api-request-limit-exceeded

ApiRequest model will store the usage data:

rails g model ApiRequest user:references path method

On the user model:

  • set a max limit of requests per month
  • count requests within the last 30 days
  • check if the limit is exceeded
# app/models/user.rb
class User < ApplicationRecord
  ...

  has_many :api_requests

  MAX_API_REQUESTS_PER_30_DAYS = 10_000

  def api_requests_within_last_30_days
    api_requests.where("created_at > ?", 30.days.ago).count
  end

  def api_request_limit_exceeded?
    api_requests_within_last_30_days >= MAX_API_REQUESTS_PER_30_DAYS
  end
end

In the base API controller, log API requests with log_api_request and check limit:

# app/controllers/api/v1/authenticated_controller.rb
  before_action :check_api_limit
  before_action :log_api_request

  ...

  private

  ...

  def log_api_request
    current_user.api_requests.create!(path: request.path, method: request.method)
    # in the response header, include remaining api request count
    response.headers['X-Superails-User-Api-Call-Limit'] = "#{current_user.api_requests_within_last_30_days.to_s}/#{User::MAX_API_REQUESTS_PER_30_DAYS.to_s}"
  end

  def check_api_limit
    if current_user.api_request_limit_exceeded?
      render json: { message: "API request limit exceeded" }, status: :too_many_requests
    end
  end

Now when a user tries to make an API request, we will first check if the user has exceeded the limit.

If the limit is not exceeded, the request is performed and data about it is stored in the ApiRequest model.

Shopify also includes a Rate limits header. With response.headers['X-Superails-User-Api-Call-Limit'] we are doing the same!

To display request headers in a response from a curl request, you can include the -v option: curl -v -X GET localhost:3000/api/v1/home/index.json -H "Authorization: Bearer MySecretToken".

api-curl-response-with-headers

That’s it!