Automate Gmail inbox sorting using the Gmail API's users.settings.filters.create endpoint to apply labels to future emails, and users.messages.batchModify to re-sort existing messages. Key auth requires OAuth 2.0 with gmail.settings.basic + gmail.modify scopes (both Restricted, requiring OAuth verification). Watch the per-user quota: batchModify costs 50 units per call against a 15,000 units/minute limit.
API Quick Reference
OAuth 2.0
15,000 quota units/minute per user
JSON
Available
Understanding the Gmail API
The Gmail API is a RESTful API that gives you full programmatic access to Gmail mailboxes. It operates on a resource model where users, messages, threads, labels, drafts, and filters are all first-class resources, each with their own endpoints under the base URL https://gmail.googleapis.com/gmail/v1/users/{userId}.
For inbox sorting automation, you'll interact with two distinct subsystems: the filters API (users.settings.filters) for creating persistent rules on future mail, and the messages API (users.messages) for bulk-modifying existing messages. These are completely separate code paths — filters do not retroactively apply to messages already in your inbox.
The API uses OAuth 2.0 for all authentication. The gmail.modify scope — required for label application — is classified as Restricted by Google, meaning your app must complete OAuth verification and a CASA (Cloud Application Security Assessment) if you store message content server-side. For internal Workspace automation using Service Account + Domain-Wide Delegation, verification requirements differ. Official docs: https://developers.google.com/workspace/gmail/api
https://gmail.googleapis.com/gmail/v1Setting Up Gmail API Authentication
Gmail uses OAuth 2.0 (3-legged flow) for user-delegated access, meaning each user must authorize your application. Access tokens expire after 3,600 seconds (1 hour), but refresh tokens let you obtain new ones automatically. Note: in Testing mode on the consent screen, refresh tokens expire after 7 days — you must publish your app to get long-lived tokens.
- 1Go to https://console.cloud.google.com and create a new project or select an existing one
- 2Navigate to APIs & Services → Library, search for 'Gmail API', and click Enable
- 3Go to APIs & Services → OAuth consent screen. Set User Type to 'External' (or 'Internal' for Workspace-only apps). Fill in app name, support email, and developer contact
- 4Under Scopes, add: https://www.googleapis.com/auth/gmail.settings.basic and https://www.googleapis.com/auth/gmail.modify
- 5Go to APIs & Services → Credentials → Create Credentials → OAuth Client ID. Choose 'Desktop app' for scripts or 'Web application' for server apps
- 6Download the credentials JSON file and save as credentials.json in your project directory
- 7Run your script once interactively to complete the OAuth consent flow — this saves a token.json file with refresh token
- 8For server automation, use Service Account + Domain-Wide Delegation: create a Service Account, download its JSON key, enable DWD in Google Workspace Admin Console → Security → API Controls → Manage Domain Wide Delegation
1import os2import json3from google.oauth2.credentials import Credentials4from google_auth_oauthlib.flow import InstalledAppFlow5from google.auth.transport.requests import Request67SCOPES = [8 'https://www.googleapis.com/auth/gmail.settings.basic',9 'https://www.googleapis.com/auth/gmail.modify',10 'https://www.googleapis.com/auth/gmail.labels'11]1213def get_credentials():14 creds = None15 if os.path.exists('token.json'):16 creds = Credentials.from_authorized_user_file('token.json', SCOPES)17 if not creds or not creds.valid:18 if creds and creds.expired and creds.refresh_token:19 creds.refresh(Request())20 else:21 flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)22 creds = flow.run_local_server(port=0)23 with open('token.json', 'w') as token:24 token.write(creds.to_json())25 return credsSecurity notes
- •Store credentials.json and token.json outside your repository — add them to .gitignore immediately
- •Never hardcode OAuth client secrets in source code — use environment variables or secret managers
- •The gmail.modify scope is Restricted — your app requires OAuth verification before publishing to external users
- •Refresh tokens in Testing mode expire after 7 days — publish your consent screen for production use
- •For server-to-server automation, use Service Account + DWD instead of storing user refresh tokens
Key endpoints
/gmail/v1/users/{userId}/settings/filtersCreates a Gmail filter that automatically applies actions (add/remove labels, archive, forward) to future incoming messages matching specified criteria. Filters only apply to email received after creation.
| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | required | The user's email address or 'me' for the authenticated user |
criteria.from | string | optional | Sender email address pattern to match |
criteria.subject | string | optional | Subject line text to match |
criteria.query | string | optional | Full Gmail search syntax query (e.g., 'has:attachment is:unread') |
action.addLabelIds | array | optional | Array of label IDs to apply to matched messages |
action.removeLabelIds | array | optional | Array of label IDs to remove (use INBOX to archive) |
Request
1{"criteria":{"from":"newsletter@company.com","subject":"Weekly Update"},"action":{"addLabelIds":["Label_123"],"removeLabelIds":["INBOX"]}}Response
1{"id":"ANe1BmjmPmvFJzVf","criteria":{"from":"newsletter@company.com","subject":"Weekly Update"},"action":{"addLabelIds":["Label_123"],"removeLabelIds":["INBOX"]}}/gmail/v1/users/{userId}/labelsCreates a new custom label in the user's mailbox. Labels are used as categories/folders and must be created before being referenced in filters or batchModify calls.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | required | Display name for the label (must be unique in the mailbox) |
messageListVisibility | string | optional | Whether messages with this label appear in message list: 'show' or 'hide' |
labelListVisibility | string | optional | Visibility in label list: 'labelShow', 'labelShowIfUnread', or 'labelHide' |
color.textColor | string | optional | Hex color for label text |
color.backgroundColor | string | optional | Hex color for label background |
Request
1{"name":"Newsletters","messageListVisibility":"show","labelListVisibility":"labelShow","color":{"textColor":"#ffffff","backgroundColor":"#16a765"}}Response
1{"id":"Label_789","name":"Newsletters","messageListVisibility":"show","labelListVisibility":"labelShow","type":"user"}/gmail/v1/users/{userId}/messagesLists message IDs matching a search query. Returns only {id, threadId} pairs — you need a separate messages.get call to fetch actual content. Use this to find existing messages to retroactively sort.
| Parameter | Type | Required | Description |
|---|---|---|---|
q | string | optional | Gmail search syntax query (e.g., 'from:newsletter@company.com is:inbox') |
maxResults | number | optional | Maximum messages to return, 1-500 |
pageToken | string | optional | Token from previous response for pagination |
labelIds | array | optional | Filter by label IDs (e.g., ['INBOX']) |
includeSpamTrash | boolean | optional | Whether to include Spam and Trash in results |
Response
1{"messages":[{"id":"18c3a2b1d4e5f6a7","threadId":"18c3a2b1d4e5f6a7"},{"id":"18c3a2b1d4e5f699","threadId":"18c3a2b1d4e5f699"}],"nextPageToken":"06614753730951641","resultSizeEstimate":142}/gmail/v1/users/{userId}/messages/batchModifyApplies label changes to up to 1,000 messages in a single API call. Costs 50 quota units per call regardless of message count. Use this to retroactively sort existing messages into labels.
| Parameter | Type | Required | Description |
|---|---|---|---|
ids | array | required | Array of message IDs to modify, maximum 1,000 per request |
addLabelIds | array | optional | Label IDs to add to all specified messages |
removeLabelIds | array | optional | Label IDs to remove from all specified messages |
Request
1{"ids":["18c3a2b1d4e5f6a7","18c3a2b1d4e5f699"],"addLabelIds":["Label_789"],"removeLabelIds":["INBOX"]}Response
1{}Step-by-step automation
Authenticate and Build Gmail Service Client
Why: All Gmail API calls require a valid OAuth 2.0 access token — without it every request returns 401.
Run the OAuth flow to get credentials and build the Gmail API service client. On first run this opens a browser window for user consent. Subsequent runs use the saved refresh token automatically. The userId 'me' refers to the authenticated user throughout all subsequent calls.
1# First get an access token via OAuth (use gcloud for quick testing)2ACCESS_TOKEN=$(gcloud auth print-access-token)34# Verify authentication by listing labels5curl -H "Authorization: Bearer $ACCESS_TOKEN" \6 'https://gmail.googleapis.com/gmail/v1/users/me/labels'Pro tip: Use userId='me' everywhere — it automatically resolves to the authenticated user's address. Hardcoding email addresses in userId breaks Service Account impersonation flows.
Expected result: A Gmail service object is returned. You can verify by calling service.users().labels().list(userId='me').execute() and seeing your existing labels.
Create Custom Labels for Sorting Categories
Why: Labels must exist before they can be assigned to messages — you can't reference a label ID that doesn't exist.
Create the label categories you want to sort email into. Each label needs a unique name within the mailbox. The API supports custom colors using hex codes — check Google's documentation for the list of supported color values (not all hex values are valid). The response includes the label's ID which you'll need for subsequent filter and batchModify calls.
1curl -X POST \2 -H "Authorization: Bearer $ACCESS_TOKEN" \3 -H "Content-Type: application/json" \4 -d '{5 "name": "Newsletters",6 "messageListVisibility": "show",7 "labelListVisibility": "labelShow",8 "color": {"textColor": "#ffffff", "backgroundColor": "#16a765"}9 }' \10 'https://gmail.googleapis.com/gmail/v1/users/me/labels'Pro tip: Gmail has a limit of 10,000 labels per mailbox. If you're building a multi-tenant system, be conservative — create one label per category, not per user.
Expected result: Returns the new label object including its ID (e.g., Label_123abc). The label appears immediately in Gmail's sidebar.
Create API Filters for Future Email Sorting
Why: Filters are persistent rules that Gmail applies automatically to all future incoming email — this is the 'set it and forget it' part of inbox automation.
Create filters using users.settings.filters.create. Each filter has criteria (what to match) and an action (what to do). The criteria object accepts from, to, subject, query (full Gmail search syntax), hasAttachment, excludeChats, and size/sizeComparison. The action object accepts addLabelIds, removeLabelIds, and forward. A filter with removeLabelIds: ['INBOX'] effectively archives matching messages.
1curl -X POST \2 -H "Authorization: Bearer $ACCESS_TOKEN" \3 -H "Content-Type: application/json" \4 -d '{5 "criteria": {6 "from": "newsletters@substack.com",7 "query": "is:unread"8 },9 "action": {10 "addLabelIds": ["Label_789"],11 "removeLabelIds": ["INBOX"]12 }13 }' \14 'https://gmail.googleapis.com/gmail/v1/users/me/settings/filters'Pro tip: Use Gmail's search syntax in the query field for powerful matching: 'list:' matches all mailing list emails, 'has:attachment larger:5M' matches large attachments, 'from:(site1.com OR site2.com)' matches multiple senders.
Expected result: Returns the filter object with an ID. The filter appears in Gmail Settings → Filters and Blocked Addresses. All future matching emails will be sorted automatically.
Retroactively Sort Existing Messages with batchModify
Why: Filters only affect future mail — to clean up your existing inbox you must separately find and relabel messages already there.
Use messages.list with a search query to get matching message IDs, then call messages.batchModify with up to 1,000 IDs at a time. The batchModify endpoint returns an empty 204 response on success. Be careful with quota: each batchModify call costs 50 units, and messages.list costs 5 units per page — scanning 5,000 messages costs at minimum 55 units just for listing.
1# Step 1: List matching message IDs2curl -H "Authorization: Bearer $ACCESS_TOKEN" \3 'https://gmail.googleapis.com/gmail/v1/users/me/messages?q=from:substack.com+in:inbox&maxResults=500'45# Step 2: Apply label to those messages (replace IDs)6curl -X POST \7 -H "Authorization: Bearer $ACCESS_TOKEN" \8 -H "Content-Type: application/json" \9 -d '{10 "ids": ["18c3a2b1d4e5f6a7", "18c3a2b1d4e5f699"],11 "addLabelIds": ["Label_789"],12 "removeLabelIds": ["INBOX"]13 }' \14 'https://gmail.googleapis.com/gmail/v1/users/me/messages/batchModify'Pro tip: For large mailboxes with 10,000+ messages to sort, spread the work across multiple runs to avoid hitting the 15,000 units/minute per-user limit. At 50 units per batchModify call with 1,000 messages each, you can process about 300,000 messages per minute before hitting the limit.
Expected result: All matching existing messages receive the new label and are removed from INBOX. The function returns the total count of sorted messages.
Complete working code
This script creates sorting labels, sets up filters for future email, then retroactively sorts all existing matching messages. It handles the three most common sorting categories: newsletters, notifications, and receipts. Run it once to set up your inbox, then the filters keep things organized automatically.
1#!/usr/bin/env python32"""Gmail Inbox Sorter — creates labels, filters, and retroactively sorts existing mail."""3import os4import time5import logging6from googleapiclient.discovery import build7from google.oauth2.credentials import Credentials8from google_auth_oauthlib.flow import InstalledAppFlow9from google.auth.transport.requests import Request1011logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')12log = logging.getLogger(__name__)1314SCOPES = [15 'https://www.googleapis.com/auth/gmail.settings.basic',16 'https://www.googleapis.com/auth/gmail.modify',17 'https://www.googleapis.com/auth/gmail.labels'18]1920SORTING_RULES = [21 {22 'label_name': 'Newsletters',23 'label_color': {'textColor': '#ffffff', 'backgroundColor': '#16a765'},24 'filter_criteria': {'query': 'list: OR unsubscribe'},25 'sort_query': 'list: OR unsubscribe in:inbox'26 },27 {28 'label_name': 'Notifications',29 'label_color': {'textColor': '#ffffff', 'backgroundColor': '#4986e7'},30 'filter_criteria': {'from': 'noreply@ OR no-reply@'},31 'sort_query': 'from:(noreply OR no-reply) in:inbox'32 },33 {34 'label_name': 'Receipts',35 'label_color': {'textColor': '#000000', 'backgroundColor': '#f691b2'},36 'filter_criteria': {'query': 'subject:(receipt OR order OR invoice)'},37 'sort_query': 'subject:(receipt OR order OR invoice) in:inbox'38 }39]4041def get_credentials():42 creds = None43 if os.path.exists('token.json'):44 creds = Credentials.from_authorized_user_file('token.json', SCOPES)45 if not creds or not creds.valid:46 if creds and creds.expired and creds.refresh_token:47 creds.refresh(Request())48 else:49 flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)50 creds = flow.run_local_server(port=0)51 with open('token.json', 'w') as f:52 f.write(creds.to_json())53 return creds5455def get_or_create_label(service, name, color):56 labels = service.users().labels().list(userId='me').execute().get('labels', [])57 for label in labels:58 if label['name'] == name:59 log.info(f'Label exists: {name} ({label["id"]})')60 return label['id']61 result = service.users().labels().create(62 userId='me', body={'name': name, 'messageListVisibility': 'show',63 'labelListVisibility': 'labelShow', 'color': color}64 ).execute()65 log.info(f'Created label: {name} ({result["id"]})')66 return result['id']6768def sort_existing_mail(service, query, label_id):69 ids, page_token = [], None70 while True:71 params = {'userId': 'me', 'q': query, 'maxResults': 500}72 if page_token:73 params['pageToken'] = page_token74 res = service.users().messages().list(**params).execute()75 ids.extend([m['id'] for m in res.get('messages', [])])76 page_token = res.get('nextPageToken')77 if not page_token:78 break79 for i in range(0, len(ids), 1000):80 service.users().messages().batchModify(81 userId='me',82 body={'ids': ids[i:i+1000], 'addLabelIds': [label_id], 'removeLabelIds': ['INBOX']}83 ).execute()84 time.sleep(0.5)85 return len(ids)8687def main():88 service = build('gmail', 'v1', credentials=get_credentials())89 for rule in SORTING_RULES:90 label_id = get_or_create_label(service, rule['label_name'], rule['label_color'])91 service.users().settings().filters().create(92 userId='me',93 body={'criteria': rule['filter_criteria'],94 'action': {'addLabelIds': [label_id], 'removeLabelIds': ['INBOX']}}95 ).execute()96 log.info(f'Filter created for {rule["label_name"]}')97 count = sort_existing_mail(service, rule['sort_query'], label_id)98 log.info(f'Sorted {count} existing messages into {rule["label_name"]}')99100if __name__ == '__main__':101 main()Error handling
Request had insufficient authentication scopes.Your OAuth token was issued with scopes that don't include gmail.modify or gmail.settings.basic. The token is valid but lacks permissions.
Delete token.json and re-run the OAuth flow with the correct SCOPES list. Ensure gmail.modify and gmail.settings.basic are both in your consent screen's scope list.
No retry — user must re-authorize with correct scopes.
User Rate Limit ExceededYour application exceeded the 15,000 quota units per minute for this user. batchModify at 50 units/call is the most likely culprit when processing large backlogs.
Implement exponential backoff. Add sleep(0.5) between batch calls. Spread large sorting jobs over multiple runs.
Exponential backoff: wait 2^n seconds (1s, 2s, 4s, 8s, 16s, 32s, max 64s) then retry.
Invalid label IDYou referenced a label ID that doesn't exist in the user's mailbox, or used a system label name (like 'INBOX') in the addLabelIds field where it's not allowed.
Verify label IDs exist by calling users.labels.list first. System label IDs like INBOX, SENT, STARRED must be used exactly as uppercase strings.
No retry — fix the label ID and resubmit.
Too Many RequestsHit the per-project quota limit of 1,200,000 units/minute or the sending limit. For batchModify operations, this usually means too many concurrent users hitting the API.
Implement exponential backoff. For multi-user apps, add per-user concurrency limits. Consider spreading operations with time.sleep() between batches.
Exponential backoff starting at 1s, doubling each attempt, max 64s with jitter.
Invalid CredentialsAccess token has expired (TTL is 3,600 seconds). In Testing mode, refresh tokens expire after 7 days.
The Google client libraries handle token refresh automatically if you use the Credentials class correctly. Ensure your code checks creds.valid and calls creds.refresh(Request()) when expired.
Refresh the token immediately, then retry the request once.
Rate Limits for Gmail API
| Scope | Limit | Window |
|---|---|---|
| Per user | 15,000 quota units | per minute |
| Per project | 1,200,000 quota units | per minute |
| Filter limit | 1,000 filters | per mailbox (hard limit, not rate) |
| Label limit | 10,000 labels | per mailbox (hard limit) |
1import time2import random3from googleapiclient.errors import HttpError45def execute_with_backoff(request, max_retries=6):6 for attempt in range(max_retries):7 try:8 return request.execute()9 except HttpError as e:10 if e.resp.status in [403, 429, 500, 503]:11 wait = min((2 ** attempt) + random.uniform(0, 1), 64)12 print(f'Rate limited, waiting {wait:.1f}s (attempt {attempt+1})')13 time.sleep(wait)14 else:15 raise16 raise Exception('Max retries exceeded')- Use batchModify instead of individual modify calls — 1 call for 1,000 messages vs 1,000 separate calls
- Cache label IDs after the first labels.list call — avoid re-fetching on every run
- Add 500ms sleep between batch calls when processing large existing backlogs
- Schedule inbox-sorting jobs during off-peak hours if processing thousands of messages
- Monitor quota usage in Google Cloud Console → APIs & Services → Gmail API → Quotas
Security checklist
- Store OAuth credentials (credentials.json, token.json) outside your repository — add to .gitignore
- Request only the minimum required scopes: use gmail.labels for label-only operations, only add gmail.modify when you need to change message labels
- The gmail.modify scope is Restricted — complete OAuth verification before deploying to external users
- For Workspace automation, use Service Account + Domain-Wide Delegation instead of storing user refresh tokens
- Rotate Service Account keys every 90 days via Google Cloud Console
- Audit filter creation logs — a compromised app could create filters to forward emails to external addresses
- Use HTTPS for all API calls (the Google client libraries enforce this by default)
- Implement token refresh error handling to gracefully re-authenticate rather than exposing errors to end users
Automation use cases
Newsletter Zero
beginnerAutomatically sort all newsletters and mailing lists out of inbox into a dedicated label, keeping the inbox only for direct human communication.
Sales Pipeline Sorter
intermediateRoute emails from specific lead sources or domains into CRM-linked labels, then use labels as triggers for follow-up automation.
Multi-Account Inbox Manager
advancedUse Service Account + DWD to sort inboxes across all users in a Google Workspace organization simultaneously with standardized label taxonomies.
Receipt Archiver
intermediateIdentify order confirmations and receipts by subject line patterns, label them, and optionally extract data for expense tracking.
No-code alternatives
Don't want to write code? These platforms can automate the same workflows visually.
Zapier
Free tier available (100 tasks/month); Starter from $19.99/monthZapier's Gmail integration supports triggers on new emails and actions to add labels, making it easy to build sorting rules without code.
- + No code required
- + GUI-based rule creation
- + 500+ integration triggers for routing
- - Limited to 100 tasks/month on free tier
- - Can't batch-process existing messages
- - No access to Gmail's native filter system
Make (Integromat)
Free tier (1,000 ops/month); Core from $9/monthMake offers a visual scenario builder that watches for new Gmail messages and applies labels based on conditions.
- + Visual flow builder
- + More complex logic than Zapier
- + Better value on paid tiers
- - Learning curve for complex scenarios
- - Free tier limited to 1,000 operations/month
- - No retroactive sorting
n8n
Free self-hosted; Cloud from €20/monthSelf-hosted n8n provides Gmail nodes for reading and modifying messages, with full control over sorting logic in a visual workflow editor.
- + Self-hostable (free)
- + Full Gmail API access
- + Can run on a schedule for retroactive sorting
- - Requires hosting setup
- - More technical than Zapier/Make
- - No managed infrastructure
Best practices
- Always create filters before running retroactive sorting — otherwise the filter creation quota cost (5 units) is wasted on messages already sorted
- Use Gmail's native search syntax in filter criteria: 'list:' reliably matches mailing lists, 'has:attachment larger:5M' catches large files
- Test filter criteria in Gmail's search bar before creating filters via API — what you see is what the API will match
- Handle the case where a label already exists: call labels.list and reuse existing IDs rather than failing on duplicate creation
- Use batchModify's 1,000-message limit aggressively — it's 50 quota units per call regardless of how many messages are in the batch
- For Workspace deployments, prefer Service Account + DWD over per-user OAuth to avoid managing thousands of refresh tokens
- Log all filter IDs created by your automation so you can programmatically clean them up later
Ask AI to help
Copy one of these prompts to get a personalized, working implementation.
I'm using the Gmail API (Python, google-api-python-client) to automate inbox sorting. I have a service object and a list of SORTING_RULES with label_name, filter_criteria, and sort_query fields. I'm getting a 403 insufficientPermissions error when calling users().settings().filters().create(). My OAuth scopes are gmail.labels and gmail.modify. What scope am I missing and why does gmail.modify not cover filter creation?
Build a Gmail inbox sorting dashboard using the Gmail API. The app should: 1) Show all existing labels with message counts, 2) Display active filters with their criteria and actions, 3) Allow creating new sorting rules via a form (from, subject, query fields + label selection), 4) Show a log of recently sorted messages. Use Supabase for storing rule configurations. Connect to Gmail API via a Supabase Edge Function that handles OAuth token refresh.
Frequently asked questions
Do Gmail API filters apply to existing messages in my inbox?
No. users.settings.filters.create only applies to future incoming email. To sort existing messages, you must separately use users.messages.list with a search query to get message IDs, then call users.messages.batchModify to apply labels. The complete automation script above handles both steps.
Is the Gmail API free?
The Gmail API itself is free with no per-call charges. However, using Restricted scopes (gmail.modify, gmail.readonly) requires completing Google's OAuth verification process and potentially a CASA security assessment, which can have associated costs for the audit. Compute costs for running your automation server are separate.
What happens when I hit the rate limit?
You'll receive a 403 userRateLimitExceeded or 429 Too Many Requests error. The per-user limit is 15,000 quota units per minute. batchModify costs 50 units per call, so you can run about 300 batch calls per minute. Implement exponential backoff: wait 2^n seconds (starting at 1s, max 64s) and retry.
Can I use a Service Account instead of OAuth for Gmail?
Yes, for Google Workspace (G Suite) organizations only. You need to enable Domain-Wide Delegation: create a Service Account in Google Cloud Console, enable DWD in Workspace Admin Console → Security → API Controls, and use setSubject() or with_subject() to impersonate a user. Personal Gmail accounts cannot use Service Accounts — they require 3-legged OAuth.
Why does my OAuth token expire after 7 days?
When your OAuth consent screen is in 'Testing' mode, Google issues refresh tokens that expire after 7 days. To get long-lived tokens, publish your OAuth consent screen (you don't need to submit for verification to go from Testing to Published for Internal Workspace apps). External apps need verification before publishing.
What's the difference between filter criteria 'from' vs 'query' fields?
The 'from' field is a simple sender match. The 'query' field accepts full Gmail search syntax including operators like 'list:', 'has:attachment', 'larger:5M', 'subject:', 'to:', and boolean operators. For complex matching, always use 'query' — it's the same search language you type in Gmail's search bar.
Can RapidDev help build a custom Gmail integration?
Yes. RapidDev has built 600+ apps including email automation systems. Whether you need inbox sorting, lead capture pipelines, or full support triage with CRM integration, we can scope and build it. Contact us for a free consultation at rapidevelopers.com.
Need this automated?
Our team has built 600+ apps with API automations. We can build this for you.
Book a free consultation