Reputation: 540
Trying to get message from the Gmail using RESTFul API and I'm getting this error : User Rate Limit Exceeded
While, I'm implemented throttling (no more 5 gets in 1 sec, when google says that no more 25 calls in 1 sec) and also exponential backoff when I'm getting any exception and even with all this I'm still getting this exception.
So, what could be the issue here ?
Thanks
Upvotes: 1
Views: 2270
Reputation: 7290
There is a well defined quota for Google APIs, here is the one for Gmail: https://developers.google.com/gmail/api/v1/reference/quota#per-method_quota_usage
Here is a small utility class to handle the quota (for single threads, the thread safe implementation is a bit more complex):
public class ApilRateLimiter {
private long timeSliceEnd;
private final int quotaPerSecond;
private int quotaRemaining;
public ApilRateLimiter(final int quotaPerSecond) {
this.quotaPerSecond = quotaPerSecond;
this.quotaRemaining = quotaPerSecond;
this.timeSliceEnd = System.currentTimeMillis() + 1_000L;
}
public void reserve(final int quotaReserved) throws InterruptedException {
if (quotaReserved > quotaPerSecond) {
throw new IllegalArgumentException(
"reservation would never be successful as quota requested is greater than quota per second");
}
final long currentTime = System.currentTimeMillis();
if (currentTime >= timeSliceEnd) {
this.timeSliceEnd = currentTime + 1_000L;
this.quotaRemaining = quotaPerSecond - quotaReserved;
} else if (quotaReserved <= quotaRemaining) {
quotaRemaining -= quotaReserved;
} else {
Thread.sleep(timeSliceEnd - currentTime);
reserve(quotaReserved);
}
}
}
and the definition for the Gmail quotas:
public interface GmailApiLimits {
int QUOTA_PER_SECOND = 250;
int DRAFTS_CREATE = 10;
int DRAFTS_DELETE = 10;
int DRAFTS_GET = 5;
int DRAFTS_LIST = 5;
int DRAFTS_SEND = 100;
int DRAFTS_UPDATE = 15;
int GETPROFILE = 1;
int HISTORY_LIST = 2;
int LABELS_CREATE = 5;
int LABELS_DELETE = 5;
int LABELS_GET = 1;
int LABELS_LIST = 1;
int LABELS_UPDATE = 5;
int MESSAGES_ATTACHMENTS_GET = 5;
int MESSAGES_BATCHDELETE = 50;
int MESSAGES_DELETE = 10;
int MESSAGES_GET = 5;
int MESSAGES_IMPORT = 100;
int MESSAGES_INSERT = 25;
int MESSAGES_LIST = 5;
int MESSAGES_MODIFY = 5;
int MESSAGES_SEND = 100;
int MESSAGES_TRASH = 5;
int MESSAGES_UNTRASH = 5;
int STOP = 50;
int THREADS_DELETE = 20;
int THREADS_GET = 10;
int THREADS_LIST = 10;
int THREADS_MODIFY = 10;
int THREADS_TRASH = 10;
int THREADS_UNTRASH = 10;
int WATCH = 100;
}
You use it like so:
this.apiRateLimiter = new ApilRateLimiter(GmailApiLimits.QUOTA_PER_SECOND);
...
apiRateLimiter.reserve(GmailApiLimits.MESSAGES_LIST);
gmailApi.users().messages().list("me")...execute();
...
apiRateLimiter.reserve(GmailApiLimits.MESSAGES_GET);
gmailApi.users().messages().get("me"...execute();
...
Basically, you call reserve()
before you make a Gmail API call. If there is quota left for the second, reserve returns right away, otherwise it sleeps until the second is over.
Upvotes: 1