Working with the GitHub API rate limit #189255
Pinned
wilsonwong1990
started this conversation in
Discover: GitHub Best Practices
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Introduction
Building off the great discussion post around GitHub API rate limits by @devopsjester back in June 2025, I wanted to dive a bit a deeper how and methods to work within the GitHub API rate limits. @devopsjester did a great job breaking down the REST API rate limits, the GraphQL API rate limits, secondary rate limits, and overview of importance of the rate limits. To do a quick rehash:
To dive deeper into best practices of GitHub's rate limits, I wanted to provide some recommendations to consider when you build your next awesome GitHub integration.
Recommendation 1: Don't use APIs at all, use webhooks
The best advice there is when using the GitHub API is to not use if you do not have to. This may sound a bit counterintuitive in a discussion around GitHub's API, but if a webhook exists it's better to opt for that over an API call.
A lot of GitHub integrations are built on extracting data from GitHub and mainly are event driven. Imagine a GitHub integration where when a new PR opens, you want your integration to add feedback or a comment. The common thought is, let's build around the List pull requests REST API endpoint! So, you end up building an integration that looks something like this:
When you start designing, this seems to make sense: everything stays within your codebase and just naturally flows. You just feed a token into for authorization, and with a few lines of code, you will have built a working integration. Truth is, in smaller repositories where there are less frequent changes, this may work well. However, this does not scale and hence why it doesn't follow best practices: as the repository grows with more development work, there will be more PRs. And as the number of PRs grows, there are more API requests to get the specific PRs data that will consume your rate limit. Eventually, you will hit a point where you exhaust your API rate limit and your integration will break.
Webhooks on the other hand do not use the API and as it doesn't use the API, no rate limit would ever be hit. Webhooks allow you to subscribe to specific events in your organization or repository. A payload is delivered to your webhook server when the subscribed event occurs. Taking the example above, the flow would look something like this instead:
Prerequisite steps:
Steps:
While webhooks do add overhead by having to either set up a webhook proxy service like smee.io or a local webhook listener, this eliminates the need to poll the GitHub LIST API endpoints and allows you to write an app that focuses on just using the GitHub GET API endpoints to retrieve the needed information. Also, once a webhook service is set up, it can be used for other projects or integrations you are building that have webhook capabilities.
Recommendation 2: GitHub Apps
Whether your integration can use webhooks or not, the next recommendation is to utilize a GitHub App over a service account or
Personal Access Token. GitHub Apps are a first-class user on the GitHub platform, do not have any additional costs, and generally more secure as they have fine grained permissions. Not only that but in the context of rate limits, GitHub Apps that are owned by a GitHub Enterprise Cloud organization have a rate limit of 15,000 REST API requests per hour. This is three times the rate limit of a GitHub user! This alone can make supporting a large GitHub integration much easier and scalable.
For a detailed look at how to build a GitHub App and use the GitHub App's Installation Token for API access, please look at this community discussion post by @loujr.
Recommendation 3: Octokit as your library
Once you have considered webhooks and GitHub Apps for your GitHub integration, the next will be focusing on what libraries and SDKs you want to build on.
The recommendation is to use Octokit as it is the official SDK of GitHub. Not only that, but there are already two plugins built for
Octokit that handle rate limit logic for you: @octokit/plugin-throttling and @octokit/plugin-retry.
Most of the bugs and issues seen with adopting rate limit best practices comes with trying to hand build the logic itself. Whether it's adding
sleepsor monitoring theretry-afterheaders, accounting for the rate limit adds on to development overhead.Other times, developers (understandably) want to build the features for their GitHub integration first as it is more important or more exciting. This often causes rate limit logic to be push into the backlog for later work or a "nice to have".
This is where Octokit excels as it already has the two plugins that are built in to handle this logic. The @octokit/plugin-throttling tackles rate limits by queuing requests when you're approaching limits and provides callbacks for both primary and secondary rate limit events. The @octokit/plugin-retry complements @octokit/plugin-throttling by handling transient server errors. Both repositories for the plugins have excellent examples that show how easy it is to implement retries and rate limit throttling so you can focus on developing those features.
There are various third party libraries as well but do research and see if the library has rate limit logic and how it handles rate limiting.
Recommendation 4: Cache with Conditional Requests
After choosing your library and as you begin to build your code around GitHub's APIs, ETags are a great way to monitor the state of an API result if you are using the REST API. Think of ETags like a fingerprint of the resource's current state. Here's the flow:
Here's an example of this using Octokit.js:
As there are no official Octokit plugins for ETags, in this example script, we are simply monitoring for
200and304HTTP statuses when we make a REST API request to the List Pull Requests endpoint. On each200HTTP response, we record the Pull Requests data to the CACHE_FILE and the ETag value to the ETAG_FILE. On each304HTTP response, we instead make no changes to these files. As a304is aNOT MODIFIEDreturn, it does not count against our rate limits.While there is no plugin, by including one more header you can monitor for changes while not being billed for it.
Consider a case where you scale up this example script to monitor for new Pull Requests for 50 of your repositories every 5 minutes.
That means you would be doing the following:
Now let's assume that 90% of the time when you make these requests, there is no change to the Pull Requests. That means 540 requests were made returning that same data, effectively meaning you wasted 540 requests.
With ETag caching, you would still do 600 requests but only 60 requests would count against your rate limit while also getting the same data.
One other consideration for conditional requests and caching is the
Last-Modifiedheader. Some endpoints will return this as a response header.Last-Modifiedrelies on timestamps to inform of any changes since that date and time. You can then send aIf-Modified-Sinceheader for the same 304 behavior as above. Do note ETags are better still for as it monitors for actual data changes vs just a date and time, but there are situations whereLast-Modifiedcan augment the request or is a better fit due to limitations of ETags.A couple things to consider when designing caching strategies:
Last-Modifiedheader instead.Wrapping it all up
While on the surface rate limits seem to be a constraint and a hassle, when respected they can help make your integrations faster, more efficient, and more reliable. GitHub is a huge platform used by the smallest developers to largest companies in the world. The rate limits help maintain a fast and reliable platform for us to build on: whether it is your first python Hello World! to your massive Rust applications. When building your next GitHub integration, consider using webhooks, GitHub Apps, Octokit as your library, and Conditional Requests to help you build a reliable, efficient, and best practice following app!
Beta Was this translation helpful? Give feedback.
All reactions