Gmail API Limits in 2026: Quotas, Rate Limits, and How to Handle Them

Gmail API - May 2026

Gmail API Limits in 2026: Quotas, Rate Limits, and How to Handle Them

Complete Gmail API quota reference covering per-minute, per-user, and daily limits, per-method unit costs, 429 error handling, and a production-ready exponential backoff pattern for authenticated users.

retry-handler.js
async function withBackoff(fn, maxRetries = 5) { let delay = 1000; for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (err) { if (err.code !== 429) throw err; const jitter = Math.random() * 500; await sleep(delay + jitter); delay *= 2; } }}
Handles 429 rateLimitExceeded and userRateLimitExceeded
Data Handling Note

Scoped to the authenticated user session

Unipile does not maintain a parallel archive of Gmail mailbox data. All email access is scoped to the session of the authenticated user who has explicitly granted OAuth consent. No data is stored independently beyond what is required to serve that specific request.

How Unipile Operates

Independent technical intermediary

Unipile acts as an independent technical intermediary, making Gmail API calls on behalf of each individual authenticated user. Unipile is not a Google partner or affiliate. No credentials are shared across users. Each OAuth token belongs exclusively to the user who granted it.

Platform Limits

Cadence is a customer-side decision

Unipile relays Gmail API rate limits and quota constraints as defined by Google. The frequency and volume of API calls made on behalf of each user is a customer-side decision. Unipile surfaces quota errors and applies backoff logic, but the calling cadence remains under the developer's control.

May 2026 Update

Gmail API limits at a glance

Before writing a single line of retry logic, you need a clear picture of all three quota axes Google enforces. The table below is the reference you will bookmark: per-project throughput, per-user caps, and the daily ceiling that resets at midnight Pacific time.

1.2M Quota units / min / project
6K Quota units / min / user
80M Quota units / day / project
500 Emails / day send limit (free)
Per message
Limit Value Dimension Notes
Gmail API rate limit (quota units) 1,200,000 / min Per GCP project Shared across all users in the project
Gmail API per user rate limit 6,000 / min Per user mailbox Most common trigger for 429 in production
Daily quota 80,000,000 / day Per GCP project Resets at midnight Pacific time
Gmail send quota (free Gmail) 500 emails / day Per sender address 2,000 / day for Google Workspace
Concurrent requests per mailbox 50 Per mailbox Hidden limit - triggers 429 regardless of quota
Batch requests per call 100 requests Per batch call Use batch to reduce quota unit consumption
Max message size (attachments) 25 MB Including headers and encoded body
Project rate limit 1,200,000 / min Quota units, per GCP project
Per-user rate limit 6,000 / min Per mailbox - most common 429 trigger
Daily quota 80,000,000 / day Per GCP project, resets midnight PT
Gmail send quota 500 / day (free) 2,000 / day for Google Workspace
Concurrent requests 50 per mailbox Hidden limit - fires 429 even under quota

Source: Google Developers documentation, Gmail API - Usage Limits (last verified May 2026). Limits are subject to change; always check your GCP console quotas page for the current values on your project.

Reference

The three quota dimensions

Gmail API usage limits work across three independent axes simultaneously. A request can be blocked even if two of the three are fine. Understanding the separation between per-project, per-user, and daily limits is the first step to writing resilient code for any authenticated user.

Per-project (GCP)

1,200,000 units/min

This is the aggregate ceiling across all users within one GCP project. If you have 500 authenticated users each making Gmail API calls simultaneously, all of their quota consumption counts against this single bucket. Hitting it triggers a rateLimitExceeded 429. The daily equivalent is 80 million units, resetting at midnight Pacific.

Per-user (per mailbox)

6,000 units/min

This cap applies independently to each authenticated user. Even if your project has quota headroom, a single user hammering their own mailbox with rapid polling will trigger a userRateLimitExceeded 429. In multi-tenant apps this is the limit that fires most often: it is per-user, not shared.

Daily quota

80,000,000 units/day

The daily limit is also per-project. Unlike the per-minute windows which recover automatically after 60 seconds, hitting the daily quota means no further API access until midnight Pacific. The error is dailyLimitExceeded. You can request a quota increase via the GCP console - we cover that in the multi-tenant section.

Important: The gmail api rate limits above are measured in "quota units", not raw request counts. A messages.send call costs 100 units, while a messages.list costs only 5. This means your practical request budget varies significantly depending on which methods you call. See the unit cost table in the next section. Also note that OAuth scope verification errors from the consent flow are completely separate from these runtime quota errors.

Technical Reference

Quota unit cost per method

Not all Gmail API calls are equal. Google counts quota usage in abstract "units", and each method has a different cost. Knowing these costs lets you calculate your real request capacity and optimise which endpoints you call. The gmail api per user rate limit of 6,000 units/min behaves very differently depending on your call mix.

Method Unit Cost Requests / min (per user) Optimization tip
messages.send 100 60 sends/min max Highest cost - use draft + send only when needed
messages.insert 25 240/min max Inserting to mailbox - lower than send
threads.get 10 600/min max Prefer threads.get over multiple messages.get
messages.get 5 1,200/min max Add fields= partial response to keep cost at 5
messages.list 5 1,200/min max Use history.list instead for sync patterns
threads.list 5 1,200/min max Paginate with maxResults to reduce calls
users.history.list 2 3,000/min max Best for incremental sync - lowest cost polling
labels.list 1 6,000/min max Cache the result - labels rarely change
messages.modify 5 1,200/min max Use batchModify for bulk label updates
messages.attachments.get 5 1,200/min max Fetch attachments only when explicitly needed
messages.send100 units
messages.insert25 units
threads.get10 units
messages.get5 units
messages.list5 units
threads.list5 units
users.history.list2 units
labels.list1 unit

Pro tip: You can use the fields= query parameter for partial responses on any method. Requesting only the fields your application needs does not reduce the raw unit cost, but it dramatically reduces response payload size and latency - a worthwhile trade when you are near gmail api quota limits. Test your queries interactively using the Gmail OAuth Playground before pushing to production.

Build your Gmail integration
Hidden Limit

The hidden 50-concurrent-requests-per-mailbox limit

There is a limit most Gmail API tutorials never mention, yet it is the cause of mysterious 429 errors in multi-threaded or async applications: Gmail enforces a maximum of 50 concurrent in-flight requests per mailbox. This limit is completely independent of your quota unit budget. You can be at 500 quota units out of 6,000 available and still receive a 429 if you have 51 requests open simultaneously against the same authenticated user's mailbox.

Why this surprises developers

Async frameworks like Node.js Promise.all() or Python asyncio.gather() make it trivially easy to fire 100+ concurrent requests. When you process a mailbox sync and fan out to fetch 200 messages simultaneously on behalf of a single authenticated user, Gmail sees 200 open TCP connections against one mailbox and immediately throttles. The quota dashboard in GCP will show plenty of remaining units - the 50-concurrent ceiling is not exposed as a quota metric.

When it triggers

The 50-concurrent limit fires during bulk mailbox sync operations: fetching large threads, downloading attachments for many messages simultaneously, or paginating through large label counts while also processing results concurrently. Any code that launches more than 50 in-flight requests to the same mailbox will hit this limit.

How to fix it

Use a concurrency limiter (semaphore) scoped per authenticated user. In Node.js, libraries like p-limit work well. In Python, an asyncio.Semaphore(40) with a safe margin below 50 prevents the burst. Keep your per-user concurrency at 40 or below for safety margin.

concurrency-limiter.js - safe per-user Gmail API calls
// p-limit keeps per-user concurrency under 40import pLimit from 'p-limit';// One limiter instance per authenticated user (keyed by userId)const userLimiters = new Map();function getLimiter(userId) { if (!userLimiters.has(userId)) { userLimiters.set(userId, pLimit(40)); } return userLimiters.get(userId);}// Wrap each Gmail API call with the user's limiterasync function fetchMessage(gmail, userId, messageId) { const limit = getLimiter(userId); return limit(() => gmail.users.messages.get({ userId: 'me', id: messageId, fields: 'id,threadId,payload.headers,snippet' }));}
Error Reference

Decoding the errors: 429 vs 403

Gmail API returns two different HTTP status codes for quota and access problems: 429 (Too Many Requests) and 403 (Forbidden). Within those two codes there are four distinct error reasons, each with a different root cause and different resolution. Conflating them wastes debugging time. Note: OAuth flow errors like invalid_grant are refresh token errors - they are completely separate from these runtime gmail api rate limits.

HTTP Code Error Reason Cause Fix
429 rateLimitExceeded Exceeded 1.2M quota units/min at the GCP project level. All authenticated users in the project collectively hit the ceiling. Exponential backoff + jitter. Wait and retry. Long-term: shard into multiple GCP projects or request a quota increase.
429 userRateLimitExceeded A single authenticated user exceeded 6,000 quota units/min, OR exceeded 50 concurrent requests to their mailbox. The most common production 429. Per-user queue with concurrency limiter. Apply the semaphore pattern (max 40 concurrent). Add exponential backoff on 429 retry.
403 dailyLimitExceeded The GCP project consumed all 80M daily quota units before midnight Pacific. Cannot be retried until reset. This is a hard stop. Wait for midnight Pacific reset or request a quota increase via GCP console. Implement daily quota budgeting per user in your app.
403 quotaExceeded A specific sub-quota was exceeded. Can also indicate the Gmail send quota (500 emails/day free, 2,000 Workspace) was hit for a specific sender address. Check which quota. For send quota: switch sending address or wait 24h. For other sub-quotas: identify the specific metric in GCP quotas dashboard.
429 rateLimitExceeded

Project-level ceiling hit: 1.2M quota units/min across all users.

Fix: Exponential backoff + jitter. Long-term: multiple GCP projects or quota increase request.

429 userRateLimitExceeded

Single user exceeded 6,000 units/min OR 50 concurrent requests. Most common 429.

Fix: Per-user queue + semaphore (max 40 concurrent). Add backoff retry.

403 dailyLimitExceeded

Project consumed all 80M daily quota units. Hard stop until midnight Pacific.

Fix: Wait for reset or request quota increase via GCP console.

403 quotaExceeded

Sub-quota or Gmail send quota (500/day free, 2,000 Workspace) hit for sender.

Fix: Check GCP dashboard for specific sub-quota. For send: switch address or wait 24h.

Do not confuse these with OAuth errors. Errors like invalid_grant, access_denied, and invalid_client come from the OAuth token exchange layer, not from Gmail API quota enforcement. They are covered separately in the Google OAuth errors guide. If you see a 401 Unauthorized, that is also an authentication issue - check your OAuth app verification status and whether the refresh token has expired.
Production Code

Production-ready handling: exponential backoff + jitter

Exponential backoff is the only correct response to a 429 from the Gmail API. Retrying immediately makes the problem worse: you pile more requests onto an already throttled quota window. Adding random jitter prevents the "thundering herd" problem where all clients in a fleet retry at exactly the same moment. Below are complete, production-tested implementations in Node.js and Python.

1s

Initial delay

Start with 1,000ms on first retry. Never retry immediately after a 429.

x2

Exponential multiplier

Double delay each attempt: 1s, 2s, 4s, 8s, 16s. Cap at 32-64 seconds maximum.

+R

Random jitter

Add 0-500ms random jitter to prevent synchronized retries across concurrent workers.

gmail-backoff
// gmail-backoff.js - production-ready exponential backoff + jitter// Handles rateLimitExceeded and userRateLimitExceeded (gmail api rate limits)const sleep = (ms) => new Promise(r => setTimeout(r, ms));async function withGmailBackoff(fn, { maxRetries = 5, initialDelay = 1000, maxDelay = 32000, jitter = 500} = {}) { let delay = initialDelay; for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await fn(); } catch (err) { const code = err?.code || err?.response?.status; const reason = err?.errors?.[0]?.reason || ''; // Only retry on quota errors - not on 403 dailyLimitExceeded const isRetryable = code === 429 || reason === 'rateLimitExceeded' || reason === 'userRateLimitExceeded'; if (!isRetryable || attempt === maxRetries - 1) throw err; const wait = Math.min(delay + Math.random() * jitter, maxDelay); await sleep(wait); delay *= 2; } }}// Usage: on behalf of each authenticated userconst message = await withGmailBackoff(() => gmail.users.messages.get({ userId: 'me', id: messageId }));
Retries on 429 only. Does not retry dailyLimitExceeded (403). Caps delay at 32s.
# gmail_backoff.py - exponential backoff + jitter for Gmail API rate limitsimport asyncio, randomfrom googleapiclient.errors import HttpErrorasync def with_gmail_backoff( fn, max_retries=5, initial_delay=1.0, max_delay=32.0, jitter=0.5): """Wrap any Gmail API call with exponential backoff + random jitter. Handles gmail api rate limits: rateLimitExceeded and userRateLimitExceeded. """ delay = initial_delay for attempt in range(max_retries): try: return fn() # fn is synchronous Gmail client call except HttpError as e: status = e.resp.status reason = e.error_details[0].get('reason', '') if e.error_details else '' # Retry on 429; do NOT retry dailyLimitExceeded retryable = status == 429 or reason in ( 'rateLimitExceeded', 'userRateLimitExceeded' ) if not retryable or attempt == max_retries - 1: raise wait = min(delay + random.uniform(0, jitter), max_delay) await asyncio.sleep(wait) delay *= 2
Compatible with Google API Python Client. Async-ready with asyncio.sleep.
Best Practices

Strategies to stay under the limit

Backoff handles the emergency. These proactive strategies prevent you from hitting gmail api quota limits in the first place. They are listed from highest to lowest impact on your quota consumption. Combining two or three of them can reduce your unit spend by 80% on typical read-heavy workloads.

Queue per authenticated user

Maintain a separate request queue per authenticated user. Never mix requests from different users into a shared pool. This isolates per-user rate limit exposure and allows fair queuing: if one user triggers a 429, only their queue pauses - others continue at full speed. Use Redis or an in-memory queue with a per-user token bucket.

Use history.list instead of messages.list for sync

Polling messages.list on a timer is the most common gmail api usage limits mistake. users.history.list costs only 2 units (vs 5 for messages.list) and returns only the delta since your last sync point. For read-heavy applications syncing large mailboxes, this switch alone can reduce quota consumption by 60%. Store the historyId from each response as your cursor. Pair with Gmail Push Notifications (Pub/Sub) for near-real-time sync.

Batch API calls

The Gmail API supports batch requests: combine up to 100 individual requests into one HTTP call. Each sub-request still counts its normal unit cost, but you reduce TCP overhead and rate limit "slots" consumed. This is particularly effective for messages.get operations where you need to fetch many messages from a sync delta. Check the Gmail Batch guide for implementation details.

Use partial responses (fields=)

Every Gmail API method supports the fields= parameter to request only the properties you need. While it does not reduce the unit cost of the request, it cuts response payload by 60-90% and improves latency. For a messages.get returning only id,threadId,payload.headers,snippet, the payload drops from 40KB+ to under 2KB. Less network = faster iteration = more headroom before hitting the gmail api daily quota ceiling.

Push vs Poll: the right sync model

Avoid messages.list every 30s

5 units per poll + N x 5 units to fetch each new message. For 100 users x 30s interval = 1M units/hour just in polling overhead.

Better history.list on timer

2 units per poll, returns only changes since last sync. 60% cheaper than messages.list polling for the same result.

Best Gmail Push Notifications (Pub/Sub)

Zero quota cost for delivery. Google pushes a notification when a mailbox changes - you fetch only the delta. Requires a public webhook endpoint. Ideal for near-real-time applications.

Build your Gmail sync integration
Advanced

Multi-tenant scaling and requesting a quota increase

Once you move beyond a handful of linked accounts, gmail api quotas become an architecture concern. This section covers GCP project sharding for large-scale deployments and the step-by-step process to request a gmail api quota increase from Google.

Note on the 100-user testing limit: Apps in development mode (not yet published through Google's OAuth verification) are capped at 100 test users. This is a completely separate limit from the API quotas. If you are hitting the 100-user ceiling, your app needs to be verified and published. See the 100-user limit guide for the full verification workflow. The gmail api rate limits discussed here apply only to verified, published apps with real authenticated users.
01

GCP project sharding

The 1.2M quota units/min limit is per GCP project. If you have 1,000 authenticated users all actively syncing, you can split them across two GCP projects to double your effective project-level capacity. Each project needs its own OAuth client credentials and consent screen. Users linked to project A cannot use project B's quota. This is particularly useful for high-volume email sync scenarios where the gmail api daily quota ceiling is a concern. Monitor your quota usage in the GCP console to know when sharding is needed.

02

Requesting a quota increase

Google grants quota increases on request, but they take time (typically 3-5 business days for initial review, up to 2 weeks for large increases). Submit via GCP Console: APIs & Services > Gmail API > Quotas > "Request higher quota". Google requires a detailed justification. Key items to include: estimated daily active users, average requests per user per day, breakdown by method (messages.send vs messages.list etc.), and the production use case. Vague requests are denied. Provide concrete numbers from your staging telemetry.

03

Monitor and budget daily quota

Implement quota budget tracking per authenticated user in your own data layer. Track quotaUnitsUsed for each user daily. When a user approaches 70-80% of their per-user daily budget, switch from active sync to push-only mode for that user for the remainder of the day. This prevents one high-volume user from burning disproportionate project-level daily quota. The cadence of how much quota you allocate per user is a customer-side decision in terms of your application design.

What to include in your quota increase request

Current usage baseline: "We currently use X million units/day across Y active users."

Growth projection: "In 6 months we expect Z users, requiring approximately W million units/day."

Use case: "Our product reads Gmail to power [CRM sync / ATS inbox / email analytics]. Users authenticate individually via OAuth."

Method breakdown: "Primary methods: messages.list (40%), messages.get (35%), history.list (20%), messages.send (5%)."

Start building at scale
Managed Solution

How Unipile manages Gmail throttling on behalf of the authenticated user

Building all the throttling infrastructure described in this guide - per-user queues, semaphores, exponential backoff, daily quota budgeting, GCP project management - takes weeks and requires ongoing maintenance. Unipile handles all of this as an independent technical intermediary, so your team focuses on product logic, not quota plumbing. The Gmail API access via Unipile is not affiliated with, endorsed by, or sponsored by Google.

Managed exponential backoff + jitter

Every Gmail API call made on behalf of each authenticated user passes through Unipile's backoff layer. 429 errors are transparent to your code: Unipile retries automatically with full exponential backoff and jitter, surfacing only the eventual success or a clean error after max retries.

Per-user queue isolation

Unipile maintains strict per-user isolation. A throttled authenticated user never impacts another user's throughput. Each linked account has its own queue with individual concurrency limits, ensuring fair resource allocation across your entire user base.

Unified model: Gmail + Outlook + IMAP

The same throttling logic applies uniformly across Gmail, Outlook (Microsoft 365 and Exchange Online), and IMAP. Your application uses one standardized API regardless of which email provider the authenticated user has linked. No provider-specific rate limit code to maintain.

GDPR compliant, SOC2 aligned

Data accessed through Unipile is scoped to the session of the authenticated user who granted OAuth consent. No parallel archive, no long-term storage of mailbox data beyond session requirements. GDPR and SOC2 alignment built in.

unipile-gmail-messages.js - no quota code needed
// With Unipile: no backoff, no semaphores, no quota tracking// Unipile handles all gmail api rate limits as an independent technical intermediaryimport UnipileClient from '@unipile/node-sdk';const client = new UnipileClient({ apiKey: process.env.UNIPILE_API_KEY, baseUrl: 'https://api3.unipile.com:13613'});// List messages for an authenticated user's linked Gmail account// Throttling, backoff, and per-user isolation handled server-side by Unipileasync function getRecentEmails(accountId) { const messages = await client.messaging.listMessages({ account_id: accountId, // authenticated user's linked account limit: 25 }); return messages;}// Works identically for Gmail, Outlook, and IMAP linked accounts// Same code, zero provider-specific rate limit handling
Unipile manages exponential backoff and per-user queuing server-side. Zero quota code in your app.
Unipile is not affiliated with, endorsed by, or sponsored by Google. Gmail is a trademark of Google LLC. All Gmail API quota limits are defined by Google and are subject to change. Unipile relays and manages these limits on behalf of authenticated users as an independent technical intermediary.

Gmail API Rate Limits - FAQ

Answers to the most common questions about gmail api rate limits, quota units, 429 errors, and scaling your integration.

The Gmail API enforces limits across three dimensions simultaneously. At the project level: 1,200,000 quota units per minute shared across all users, and 80,000,000 quota units per day. At the user level: 6,000 quota units per minute per mailbox. There is also a hidden limit of 50 concurrent requests per mailbox which can trigger a 429 even when you are well under the quota unit ceiling. Each method has a different unit cost - messages.send costs 100 units while messages.list costs only 5.

Implement exponential backoff with jitter: start at 1,000ms on first retry, double each attempt, add 0-500ms random jitter, cap at 32,000ms. Never retry immediately. Add a concurrency limiter scoped per authenticated user, keeping in-flight requests below 40 per mailbox. Use a per-user queue so one throttled user does not impact others. Longer term, switch polling from messages.list (5 units) to history.list (2 units) and consider Gmail Push Notifications to eliminate polling entirely.

The Gmail API daily quota is 80,000,000 quota units per GCP project, resetting at midnight Pacific time. This limit returns a 403 dailyLimitExceeded error - unlike per-minute 429 errors, this cannot be retried until the daily reset. To increase it: GCP Console > APIs & Services > Gmail API > Quotas > Request higher quota. Include current usage, growth projections, and your use case. Approval typically takes 3-5 business days.

The Gmail send quota is 500 emails per day for free Gmail accounts and 2,000 emails per day for Google Workspace accounts. This limit is per sender address and is separate from the API quota unit budget. Each messages.send call also costs 100 quota units, so at 6,000 units/min per-user limit you can send at most 60 emails per minute before hitting the per-user gmail api rate limit - but the daily 500/2,000 cap will likely be reached first for typical use cases.

rateLimitExceeded means the entire GCP project crossed 1,200,000 quota units/min - all authenticated users together. userRateLimitExceeded means a single user exceeded 6,000 quota units/min or sent more than 50 concurrent requests to their mailbox. In production multi-tenant apps, userRateLimitExceeded is far more common because individual users can independently hit their caps regardless of overall project-level headroom. Both return HTTP 429 and both respond to exponential backoff.

Navigate to GCP Console > APIs & Services > Gmail API > Quotas and click "Request higher quota". Your request must include a concrete justification: current daily usage baseline, 6-month user growth projection, description of how users individually authenticate via OAuth, and a breakdown of API methods by proportion. Vague requests are denied. Also ensure your app is OAuth verified - unverified apps are limited to 100 test users regardless of quota. Review typically takes 3-5 business days for initial response.

messages.send costs 100 quota units per call - the most expensive Gmail API method. With the per-user limit of 6,000 units/min, you can send at most 60 emails per minute per authenticated user. For comparison: messages.get costs 5 units (1,200 calls/min), history.list costs 2 units (3,000 calls/min), and labels.list costs 1 unit (6,000 calls/min). For high-volume outreach, consider whether your use case truly requires calling messages.send or whether creating drafts and sending in batches is more efficient.

Unipile accesses only the data explicitly requested by your application, scoped to the session of the authenticated user who has granted OAuth consent. There is no parallel archive, no independent long-term storage of mailbox data beyond what is required to serve the current API request. Each authenticated user's data is isolated: Unipile acts as an independent technical intermediary on behalf of that specific user only.

The frequency and volume of API calls made on behalf of each authenticated user is a customer-side decision. Unipile manages the backoff and queuing infrastructure, but your application determines how often to request data. Unipile surfaces quota errors transparently and applies retry logic, but the overall calling cadence - including how many users sync simultaneously and how frequently - remains under your control as the developer building on top of Unipile.

No. Unipile is not affiliated with, endorsed by, or sponsored by Google. Unipile is an independent technical intermediary that makes Gmail API calls on behalf of authenticated users who have individually granted OAuth consent through their own Google accounts. Gmail is a trademark of Google LLC. All Gmail API quotas, rate limits, and policies are defined by Google and subject to change independently of Unipile.

Questions about Gmail API limits and Unipile integration? Our team is here to help.

Talk to an expert
Pillar guide

The Complete Email API Guide for Developers

Gmail limits are one piece of the email API picture. The pillar guide covers Gmail, Outlook, and IMAP integration patterns end-to-end.

Read the Email API guide
en_USEN