API Token Quota and Usage Measurements

Throttles and Quotas

OAuth2 Bearer Tokens

Each key/secret pair has the following limitations across all Keap applications they are used to access:

  • 1500 Queries per Minute
  • 150000 Queries per Day (Resets at 12:00 AM UTC)

Personal Access Tokens or Service Account Keys

Each key has the following limitations and may only access it’s single authorized Keap application:

  • 10 Queries per Second
  • 240 Queries per Minute
  • 30000 Queries per Day (Resets at 12:00 AM UTC)

Informational Headers

Each request to the Infusionsoft REST API is returned with a number of informational headers to allow you to tune your frequency of requests and determine your remaining quota for a period. You can use these header values to determine how many concurrent calls you should be making to the API, even if operating a stateless cluster with many instances making calls via the same ClientId.

We additionally have a per-second spike policy in place for which we do not return metrics; The default rate for this policy is 25 calls per second.

The quota-descriptive headers are:

Header NameTypeExample/Acct. MaxDescription
x-keap-product-quota-limitInteger“150000”The maximum bucket size of requests before they will begin to be rejected.
x-keap-product-quota-time-unitString“day”The rolling period over which the bucket applies, currently “day” for all packages.
x-keap-product-quota-intervalInteger“1”The rolling period over which the bucket applies, currently “1” for all packages.
x-keap-product-quota-availableInteger“149999”The total remaining calls in your quota for the rolling period.
x-keap-product-quota-usedInteger“1”The number of calls that have been made against the bucket during the period.
x-keap-product-quota-expiry-timeInteger“158663200”The timecode at which the quota bucket will be fully drained.

The spike-throttling descriptive headers are:

Header NameTypeExample/Acct. MaxDescription
x-keap-product-throttle-limitInteger“1500”The maximum number of calls that may be made inside the throttle period.
x-keap-product-throttle-time-unitString“minute”The time unit for the reset period of the throttle. Currently “minute” for all consumers.
x-keap-product-throttle-intervalInteger “1”The reset period for the throttle. Currently “1” for all consumers.
x-keap-product-throttle-availableInteger “1499”The number of calls remaining during the throttled period.
x-keap-product-throttle-usedInteger “1”The number of calls that have been made against the throttle during the period.

The tenant-throttling descriptive headers are:

Header NameTypeExample/Acct. MaxDescription
x-keap-tenant-idString“ab103.infusionsoft.com”Fully qualified Tenant name.
x-keap-tenant-throttle-limitInteger“500”This limit of 500 is set by Keap and is the maximum number of calls per period.
x-keap-tenant-throttle-time-unitString“minute”The time unit for the reset period of the throttle. Currently “minute” for all consumers.
x-keap-tenant-throttle-intervalInteger “1”The reset period for the throttle. Currently “1” for all consumers.
x-keap-tenant-throttle-availableInteger “499”The number of calls remaining during the tenant throttle period.
x-keap-tenant-throttle-usedInteger “1”The number of calls that have been made against the tenant throttle during the period.

By capturing the above returned Headers (specifically “x-keap-product-throttle-available” and “x-keap-product-quota-available”) on each request it is possible to slow the rate of requests based on how close you are to hitting throttle limits or to generate internal log events to warn that spikes or quota limit hits are occurring.

Many HTTP client libraries support extending their request mechanisms to patch in functions to do this. An example for Axios, a popular Javascript framework would be similar to:

const axios = require("axios");
axios.interceptors.response.use((response) => {  
  if(Number(response.headers["x-keap-product-throttle-available"]) < 100){
    // Set whatever triggers/variables are necessary
  }
  return response;
}, (error) => {
  return Promise.reject(error);
});

axios.get(url).then(function (response) {
  // Go about your normal processing
})