As per PandaDoc documentation there are limits for the calls your application makes to PandaDoc. Official API reference states it’s 100 requests per minute per endpoint. However, this is not always true, as I have discovered there are only 50 request per minute when we query /public/v1/templates/ endpoint. On an online session I had with Scott Bilodeau he has also confirmed that some endpoints have different limits and /public/v1/templates/ endpoint has a limit of 50 requests per minute.

Unfortunately, PandaDoc has not provided us the official data on the limits for each endpoint. Hence, we decided to figure out them ourselves by running requests against PandaDoc Public API for each of the endpoints:

EntityEndpointProduction Limit, requests/minuteSandbox Limit, requests/minuteAvailable methods
Document(s)/public/v1/documents/5010List, Status, Details, Send, Share, Download, Download Protected, Delete
Template(s)/public/v1/templates/5010List, Details and Delete
Document Folder(s)/public/v1/documents/folders10010Create, List, Rename
Template Folder(s)/public/v1/documents/folders10010Create, List, Rename

As can be seen from the table different endpoints have different limits. Therefore, after an application goes over the N requests per minute limit, PandaDoc would produce an error message similar to the one below:

{"type":"request_error","detail":"Request was throttled. Expected available in 13 seconds."}

Python-pandadoc library provides the following Request Limit Managers:

  1. NoLimitRequestLimitManager – This Limit Manager is not a real limit manager, but just a stub. However, you can use this one for testing, when no limiting is required.
  2. SleepRequestLimitManager – This is the most simple implementation of a limit manager, it is suitable for a single threaded application and uses simple time.sleep() function to limit the number of requests per minute. For example setting:

SleepRequestLimitManager.__limiting_delay_seconds = 6

should produce less than 10 requests per minutes, which should be enough to run your application in the sandbox mode.

  • DjangoProductionMemcachedRequestLimitManager - When calling from a single thread we can limit number of requests by just adding a delay between them, as we did with SleepRequestLimitManager. However, when an application has multiple processes or threads, another technique should be implemented. Since I heavily use Django in my projects, I have implemented a Limit Manager with Memcached, it has 2 flavors: for production limits and for sandbox limits (which are much smaller).
  • DjangoSandboxMemcachedRequestLimitManager – This request limit manager is inherited from DjangoProductionMemcachedRequestLimitManager, so it has the same implementation, the only difference is much smaller number of requests per minute – 10 requests, as per PandaDoc API Refference

When you have large spikes in the number of requests at certain moments of time or your number of requests is pretty high and Requests Limits become a bottle neck for your application, wisely distributing applications requests over the time can greatly improve your users experience! Requests that require user interaction (e.g. creating a document upon button click, or sharing this document with a user) should get much higher priority and larger portion of the quota compared to the API request that are initiated by the background services e.g. when downloading the documents in the PDF format after these documents are complete. Therefore to differentiate these requests we use for_download=True option, so request limit manager can distinguish between these calls and apply different quotas.

Here is how request limit manager is called:

with self.__request_limit_manager(max_attempts=5, retry_delay=60, for_download=True):

response = self.get(uri)

max_attempts, retry_delay and for_download are optional parameters.

Therefore, make sure your application either implements Request Limit Manager or uses the ones provided by the python-pandadoc library.