RingCentral API Rate Limit Explained

Phong Vu
RingCentral Developers
5 min readNov 19, 2019

What is an API Rate Limit?

To prevent an API from being overwhelmed, API owners usually enforce a limit on the number of requests, or the amount of data clients can consume per period of time. This enforcement is known as API Rate Limit or API Throttle Limit. It ensures relative stability when unexpected things happen. For example, when a client app causes a spike in traffic.

If you receive an HTTP status code 429, it’s not an error, it is the server kindly asking you to please stop sending requests. Apparently, your rate of requests has been too high, and the server is not willing to accept this.

Knowing the RingCentral API Rate Limits at design time

After you created a RingCentral application from the developer portal, you can find the API rate limits that applied to your app as shown below:

RingCentral platform divides the API rate limits into API groups and each group defines the maximum number of requests per period of time which is 60 seconds.

To find out the rate limit of a certain API, you can browse the API Reference where you can see the “Usage Plan Group” of that API.

With the rate limit of the SMS API shown above, my “SmartSMS” app can send a maximum of 40 SMS messages per minute (60 seconds).

Detecting the API Rate Limits at run-time

Every RingCentral API’s response contains the following rate limit information in the response headers:

  • X-Rate-Limit-Group — API group of the request (Light, Medium, Heavy, Auth)
  • X-Rate-Limit-Window — the period (in seconds) over which rates are measured and averaged for the request rate limit.
  • X-Rate-Limit-Limit — the maximum number of requests can be sent per rate limit window.
  • X-Rate-Limit-Remaining — the remaining number of requests left for the rate limit window.

The example below shows how to detect the rate limit information every time you login your RingCentral account using the RingCentral JavaScript SDK.

var rcsdk = new SDK({
server: RINGCENTRAL_SERVER,
appKey: RINGCENTRAL_CLIENTID,
appSecret: RINGCENTRAL_CLIENTSECRET
});
var platform = rcsdk.platform();
platform.login({
username: RINGCENTRAL_USERNAME,
password: RINGCENTRAL_PASSWORD,
extension: RINGCENTRAL_EXTENSION
})
.then(function(resp) {
var headers = resp.response().headers['_headers']
console.log("Group: " + headers['x-rate-limit-group'][0])
console.log("Limit: " + headers ['x-rate-limit-limit'][0])
console.log("Remaining: " + headers ['x-rate-limit-remaining'][0])
console.log("Window: " + headers ['x-rate-limit-window'][0])
});

Handling the Rate Limits at run-time

In a situation where you need to call an API multiple times within a minute, for example, when reading the company call log which has thousands of records, you have to make sure that your app will not exceed the API call rate limit. Otherwise, your reading process will be interrupted, and your app will be suspended for a penalty period (60 seconds) every time it exceeds the rate limit.

To comply with the API call rate limit, you can implement a mechanism to handle the rate limit properly.

The example below shows you how to handle the API call rate limit when reading a company call log, which has hundreds of thousands of records without being suspended by the rate limit violation penalty.

You can use one of the following methods to control your API call sequences.

  1. Call as fast as possible and wait for the next limit window
var startTime = 0
read_as_fast_as_possible()
function read_as_fast_as_possible(){
startTime = Date.now()
platform.get('/account/~/call-log', {
dateFrom: "2019-08-01T00:00:00.000Z",
dateTo: "2019-08-31T23:59:59.999Z",
perPage: 1000
})
.then(function (resp) {
save_calllog_data(resp.json().records)
var navigationObj = resp.json().navigation
if (navigationObj.hasOwnProperty("nextPage")){
read_calllog_nextpage(navigationObj.nextPage.uri)
}
});
}
function read_calllog_nextpage(url){
platform.get(url)
.then(function (resp) {
save_calllog_data(resp.json().records)
var headers = resp.response().headers['_headers']
var remaining = parseInt(headers['x-rate-limit-remaining'][0])
var window = parseInt(headers['x-rate-limit-window'][0])
var navigationObj = resp.json().navigation
if (navigationObj.hasOwnProperty("nextPage")){
var delayInterval = 1
if (remaining == 0){
var now = Date.now()
var diff = now - startTime
delayInterval = (window * 1000) - diff
startTime = now + delayInterval
}
setTimeout(function(){
read_calllog_nextpage(navigationObj.nextPage.uri)
}, delayInterval)
}
});
}
function save_calllog_data(records){
// save call log data to your local database
...
}

2. Call and wait based on the average rate limit

read_as_per_average_rate_limit()function read_as_per_average_rate_limit(){
platform.get('/account/~/call-log', {
dateFrom: "2019-08-01T00:00:00.000Z",
dateTo: "2019-08-31T23:59:59.999Z",
perPage: 1000
})
.then(function (resp) {
save_calllog_data(resp.json().records)
var headers = resp.response().headers['_headers']
var remaining = parseInt(headers['x-rate-limit-remaining'][0])
var window = parseInt(headers['x-rate-limit-window'][0])
var navigationObj = resp.json().navigation
if (navigationObj.hasOwnProperty("nextPage")){
var delayInterval = (window / remaining) * 1000
setTimeout(function(){
read_calllog_nextpage(navigationObj.nextPage.uri)
}, delayInterval)
}
});
}
function read_calllog_nextpage(url){
platform.get(url).then(function (resp) {
save_calllog_data(resp.json().records)
var headers = resp.response().headers['_headers']
var limit = parseInt(headers['x-rate-limit-limit'][0])
var remaining = parseInt(headers['x-rate-limit-remaining'][0])
var window = parseInt(headers['x-rate-limit-window'][0])
var navigationObj = resp.json().navigation
if (navigationObj.hasOwnProperty("nextPage")){
var delayInterval = 1
if ( remaining == 0 ){
delayInterval = (window / limit) * 1000
}else{
delayInterval = (window / remaining) * 1000
}
setTimeout(function(){
read_calllog_nextpage(navigationObj.nextPage.uri)
}, delayInterval)
}
});
}
function save_calllog_data(records){
// save call log data to your local database
...
}

With the same delay mechanism, you can apply it to any RingCentral API call where you need to send requests repeatedly.

To detect API rate limits using the RingCentral Python SDK

headers = resp.response().headers
limit = int(headers[‘X-Rate-Limit-Limit’])
remaining = int(headers[‘X-Rate-Limit-Remaining’])
window = int(headers[‘X-Rate-Limit-Window’])

To detect API rate limits using the RingCentral PHP SDK

$headers= $resp->response()->getHeaders();
$limit = strval($headers[‘X-Rate-Limit-Limit’][0]);
$remaining = strval($headers[‘X-Rate-Limit-Remaining’][0]);
$window = strval($headers[‘X-Rate-Limit-Window’][0]);

If you want to have the example code above in other programming languages, please post your comment here or post your request on RingCentral Developers Forum site. To learn even more about other features we have make sure to visit our developer site.

Want to stay up to date and in the know about new APIs and features? Join our Game Changer Program and earn great rewards for building your skills and learning more about RingCentral!

--

--