Handling Rate Limits with Slack APIs

Preparing your app for scale

Illustration by Kelsey Wroten

Here in San Francisco near Slack HQ, we’re supposed to be prepared for the inevitable “big one” (i.e., a huge earthquake). We hope it never happens but we still stock the extra large, deluxe earthquake emergency kit just in case. As an app developer, it can also be useful for you to take preparedness measures for major scale.

While rate limits are rarely triggered in practice, unexpected behavior can occur when using any API, Slack’s included. What if your app has unexpected peak usage? What if your app gains an influx of users overnight and you’re handling more traffic than you ever thought you would?

Well first — yay! 🎉Congrats on your app adoption. Second, hopefully you’ve developed your app with an eye toward this extra large, deluxe scale. And if you’re just now considering rate limiting while reading this, worry not — it’s not too late.


Rate Limits on the Slack Platform

The Slack API uses rate limits on a per-method and per-workspace basis. In other words, if your app is rate limited for calling emoji.list too much, your token will still be able to call chat.postMessage. And if your app is rate limited in one workspace, you can still make requests for other ones. You also only need to worry about rate limiting for the Web API (the Events API has it too, but it’s 30,000 event deliveries per hour per workspace, so it’s unlikely you’ll hit a limit there).

The platform has a tiered rate limiting system: the number of times an app can call a method depends on the method. For example, an app can call files.upload about 20 times a minute but can call chat.postEphemeral about 100 times a minute. There are four tiers of rate limits on the platform that are purposefully vague and, in most cases, apps will stay well under the limits without even trying.

The full breakdown of the different Web API tiers and which each method belongs is on the API site. When you’re developing an app, you should make sure the rate limits are what you expect so you can handle them accordingly — but how?

Handling rate limits

Rate limits are nothing to fear, but they can lead to a bad user experience if they’re not expected and handled within your app.

Your first step should be to limit the number of API calls your app makes. Some data (like user and channel information) doesn’t change often, so you may be able to cache some of it. And some of the data you’re fetching may be inside other responses that your app is already receiving.

Beyond limiting your own requests, your app needs to be able to handle rate limiting errors when they occur. Using the Node SDK makes it easy. It handles rate limiting by default so you don’t need to implement any additional logic.

But if you can’t or don’t want to use it, there are ways to handle rate limit errors yourself:

  • Determine if a Web API request you send has been rate limited. This information is in the status code of the response after you send the request to Slack. If your calls to Slack have been rate limited, you’ll see a status code of 429:
HTTP/1.1 429 Too Many Requests
Accept: application/json, text/plain
Content-Type: application/x-www-form-urlencoded
Date: Tue, 29 Jan 2019 18:41:22 GMT
retry-after: 3
{
  "ok": false,
  "error": "ratelimited"
}
  • Wait until you can retry that Web API method again. Since the time you need to wait varies, Slack sends a header with the information so you aren’t left guessing. The header is called retry-after and is the total amount of seconds (not milliseconds) you should wait before trying the request again.

In the Node SDK, we use a queue for all of the requests we make. If the client encounters a rate limit, we’ll pause the queue for the specified retry-after time and then start the queue again. Here’s pseudocode for that basic way of handling rate limit errors:

let conversationId = 'C123'
// First, try to send the request
let response = sendRequest({ channel: conversationId, text: 'Hello, friend 😄' })
if (response.status == 429) {
  // Uh oh, figure out how long you need to wait
  let retryAfter = response.headers['retry-after'];
  // Pause requests to the Web API
  queue.pause()
  // Wait to make any more requests for the amount of time in header
  // Remember that the header is in seconds, not milliseconds
  wait(retryAfter * 1000)
  // Resume requests and carry on
  queue.resume()
} else {
  // Phew, no rate limiting error
}

If such logic feels a bit, well, limiting, consider using exponential back-off to retry your requests.

Rate limiting is one of those things where a little preparation goes a long way. To avoid a frustrating end-user experience, you should cover your bases and prepare your app for the scale it might eventually achieve. Whether you use a built-in rate limit handler or code your own handling, be prepared for when the big 429 hits.

If you run into any problems, you can chat with our developer support anytime at developers@slack.com.


Curious about what’s next in the near and far future of our platform? Check out our platform roadmap for developers.