Narendra Meshram
- Gmail API: Fetches unread messages from the inbox.
- Logic Layer: Parses email headers (From, Subject, Date) and sanitizes the body.
- State Manager: Compares Message IDs against
state.jsonto prevent duplicates. - Sheets API: Appends new unique email data as new rows.

- Python 3.10+ installed.
- A Google Cloud Project with Gmail and Sheets APIs enabled.
git clone <your-repository-url>
cd gmail-to-sheets-
Set Up Virtual Environment:
python3 -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate
-
Install Dependencies:
pip install -r requirements.txt
-
Google Cloud & OAuth Configuration:
- Create a Google Cloud project
- Enable the following APIs:
- Gmail API
- Google Sheets API
- Configure OAuth Consent Screen (External)
- Create OAuth Client ID (Desktop Application)
- Download credentials.json
- Place it in:
credentials/credentials.json
-
Configure Google Sheet:
- Create a sheet with headers:
From,Subject,Date,Content. - Copy the Spreadsheet ID into
config.py. - Update config.py (located in the project root)
- Create a sheet with headers:
-
Run the Script:
python src/main.py
- OAuth 2.0 Desktop Application flow is used
- On the first run, the user is redirected to Google’s consent screen
- Access and refresh tokens are stored locally in
token.json - Tokens are reused on subsequent runs to avoid repeated authentication
- Each Gmail email has a unique message ID
- Processed message IDs are stored in
state.json - Emails already present in the state file are skipped
- This ensures idempotent execution across multiple runs
- State is persisted using a local JSON file (
state.json) - The file stores processed Gmail message IDs
- This approach is simple, transparent, and sufficient for lightweight automation
- Initial OAuth tokens lacked Google Sheets permissions, causing authorization errors
- This was resolved by unifying Gmail and Google Sheets scopes and regenerating the OAuth token
- Emails often contained HTML tags and embedded images, cluttering the Google Sheet
- HTML cleaning was implemented to strip all tags and remove images, storing only plain text
- Google Sheets enforces a 50,000-character limit per cell
- Long email bodies caused API failures and were handled by safely truncating the content before insertion
- Large email bodies are truncated to meet Google Sheets character limits
- Email fetching is limited per run and can be extended using pagination
- Attachments are not processed
- State persistence is local and not shared across systems
- Rate Limits: The script is subject to Google API quota limits; processing thousands of emails at once may trigger a 429 error.
- No Attachments: The current version only parses the text body and does not download or link email attachments.
Screenshots and a demo video are available in the /proof folder, including:
- Gmail inbox with unread emails
- Google Sheet populated by the script
- OAuth consent screen
Note: The OAuth consent screen may display “No data available” because the application only requests Gmail and Google Sheets API scopes and does not access personal profile information.