Resolving HTTP 429 Errors from the Dynamics 365 Customer Engagement REST API

In Dynamics 365 CE (a.k.a. CRM), one of the most powerful GUI customization options available to developers are Web Resources. These are custom HTML documents which can be uploaded to the server and rendered in forms. These let you have any custom user control you can imagine. Custom buttons, special tables, you could completely redesign the entire interface as a completely custom page in one giant Web Resource if you truly desired.

Custom JavaScript inside of these HTML documents can leverage the Client API, as well as access the REST API to perform queries of data in the system, but not on the page. One useful tool for building plain XMLHttpRequests for the REST API is Jason Lattimer’s CRM Rest Builder.

An example of a query for all active Contacts may look something like this:

Dynamics 365 CE Query

However, the REST API has a cap on the number of open calls that can be processed at once. The above example is fine, since this is merely a single API call. However, imagine you have a scenario where after getting the list of Contacts, you need to perform another API call for each one. The problem is that only 10 open requests are allowed at a time. Any more than that, and they get rejected with a HTTP Status code of 429 and “No Reason Phrase”.

If using the default “alertDialog” call, this will result in errors that look like this:

The simple solution is to not send all the requests at once, but to stagger the req.send() calls.

The following snippet establishes two global arrays. One to hold all the requests that we want to send, and one to hold all the requests that have been sent and are being processed by the server.


window.bufferedAPICalls = [];
window.sendingAPICalls = [];
window.setInterval(function() {
// Remove any completed calls
window.sendingAPICalls.filter(xhr => xhr.readyState === 4).forEach(function(xhr) {
if (xhr.status !== 200) {
console.log("XHR Status: " + xhr.status);
console.log(xhr);
}
});
window.sendingAPICalls = window.sendingAPICalls.filter(xhr => xhr.readyState !== 4);

// Move any buffered calls into the pending queue
while (window.bufferedAPICalls.length > 0 && window.sendingAPICalls.length < 10) {
window.sendingAPICalls.push(window.bufferedAPICalls.shift());
}

// Ensure all XHR objects in this array are sending
window.sendingAPICalls.map(function(xhr) {
if (!xhr.__sent && xhr.readyState === 1) {
xhr.__data ? xhr.send(xhr.__data) : xhr.send()
xhr.__sent = true;
}
return xhr;
});
}, 100);

Then replace all the instances of “req.send()” with “window.bufferedAPICalls.push(req);”.

Here’s an example of the XHR snippet with our replacement:

For Create or Update operations, simple attach the request body to the XHR object before pushing it into the array, like so:

Note that you will likely want to add in more robust error handling. A complete solution would check for HTTP 429 errors and attempt to rebuild and resend the request. The snippet provided above will merely print the XHR object to the console in the event of a non-200 response code.

Your Partners for Dynamics 365

If you would like assistance with the setup or configuration of your Dynamics 365 CE or BC solutions, contact CRGroup.

crgroup logo


 

Matthew Foy CRM & Dynamics 365 Specialist CRGroupAbout the Author:

Matthew Foy is a .NET developer working on CRGroup’s SharePoint and Dynamics 365 (CRM) consultancy team. He has a passion for robust solutions and a keen interest in solving problems. Connect with Matt