Intermediate

Have you ever wonder how to read and send an email, manage drafts and attachments, search threads and messages, work with labels, set up push notifications, and manage Gmail settings… Well you have come to the right place because now we are going to learn how to access Gmail through API with Python 3

What is Gmail API?

The Gmail API is a web service. It uses a RESTful API with JSON data which contains the data for the email (or sometimes referred to as “payload”). Gmail API can be used in a variety of different applications, some use cases are:

  • Read-only mail extraction, indexing, and backup
  • Label management (add/remove labels)
  • Automated or programmatic message sending
  • Migrating email accounts from other providers
  • Set standardized email signatures for users in a domain

Setting pre-requisites for Gmail API

First of all, we need to be sure that we accomplish the prerequisites so we can use the API, but don’t be scare, these requisites are very simple.

  • Python 2.6 or greater
    • Navigate to the Python downloads page: Python downloads.
    • Click on the link/button to download Python
    • Follow the installation instructions (leave all defaults as-is).
  • The pip package management tool
    • Follow this official guide: https://pip.pypa.io/en/stable/installing/
  • A Google account with Gmail API enabled
    • Log into your project at the Google Cloud Platform console
    • Go to the navigation bar, at the top left corner
    • Select API & services -> Library
    • At the search bar type “Gmail API”, then open the API
    • Click the “Enable” button if it is disabled

      How to access gmail through API with Python 3 - Gmail API enabled
  • Download OAuth 2.0 Client IDs credentials
    • Go to your project at the Google Cloud Platform console
    • Go to the navigation bar, at the top left corner, select API & services -> credentials
    • Click at “Create credentials” -> OAuth Client IDs
    • Download the document created at OAuth 2.0 Client IDs section
      How to access Gmail through API with Python 3 Credentials-min
    • Rename the file to credentials.json
  • Install the Google Client Library
    • pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

Once you have done all the requisites, you are able to create a new python file, just be sure that the JSON file downloaded from the OAuth 2.0 Client IDs section (what we renamed to be credentials.json) at the Google Cloud Platform console is at the same folder that your python script.

Importing libraries

The following imports are needed to handle the Google API, as you can see the imports comes from google, but two of them, which are used to handle the permissions file. But let’s get some more information about each

  • pickle
    The pickle module implements binary protocols for serializing (converting an object into a string) and de-serializing (converting the string back to an object) to a Python object structure so you can save and restore
  • os.path
    This module implements some useful functions on pathnames, not files.
    The os module is used for accessing the filesystem.
  • The other 3 modules imported are used by Google API for Request handling and building and installation of the API
# Import all needed libraries to use Gmail API
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

Defining scopes for Gmail API access

Scopes are intended to provide the right level access that our application, so based on the scope chosen, different permission levels will be prompted to the end-user and those permissions will be granted to our application after the end-user accept them.

The Gmail API scopes are the permissions granted by the end-user to the application itself, this means that the user will be asked to grant some permissions based on the scope. e.g: Scope: labels means that the application can create, update and delete labels only. By requesting the minimum level of access required, users feel more confident granting access.
The Gmail API supports the following scopes:

Scope CodeDescriptionUsage
https://www.googleapis.com/auth/gmail.labelsCreate, read, update, and delete labels only.Recommended
https://www.googleapis.com/auth/gmail.sendSend messages only. No read or modify privileges on the mailbox.Sensitive
https://www.googleapis.com/auth/gmail.readonlyRead all resources and their metadata—no write operations.Restricted
https://www.googleapis.com/auth/gmail.composeCreate, read, update, and delete drafts. Send messages and drafts.Restricted
https://www.googleapis.com/auth/gmail.insertInsert and import messages only.Restricted
https://www.googleapis.com/auth/gmail.modifyAll read/write operations except immediate, permanent deletion of threads and messages, bypassing Trash.Restricted
https://www.googleapis.com/auth/gmail.metadataRead resources metadata including labels, history records, and email message headers, but not the message body or attachments.Restricted
https://www.googleapis.com/auth/gmail.settings.basicManage basic mail settings.Restricted
https://www.googleapis.com/auth/gmail.settings.sharing Manage sensitive mail settings, including forwarding rules and aliases.

Note: Operations guarded by this scope are restricted to administrative use only. They are only available to Google Workspace customers using a service account with domain-wide delegation.
Restricted
https://mail.google.com/Full access to the account’s mailboxes, including permanent deletion of threads and messages This scope, should only be requested if your application needs to immediately and permanently delete threads and messages, bypassing Trash; all other actions can be performed with less permissive scopes.Restricted

Once the end-user accepts the scope requested, it will be stored on the pickle file, if any change is made to the scope, it will trigger a new request to the end-user and the token.pickle will be replaced with the new one

# Scope provide different levels of privileges, this example is with readonly privileges
# If modifying these scopes, delete the file token.pickle.
# token.pickle files are auto generated after first API usage
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']

Getting credentials for Gmail

The following code will trigger what we just discussed on the last paragraph, so in the previous code, we just declare the scope, now we are going to create a method to get the result of executing the request of permissions. This is intended to handle our authentication with the API. This method will trigger a new web browser windows/tab that will as the end-user for permissions within his Gmail account for our application, it will be triggered just the first time and then it will be stored at a new document called token.pickle which is automatically created.

The method itself go through the following steps:

  • Create ‘creds’ variable to store the result
  • Look for a file called ‘token.pickle’, if the file exists it is assigned to ‘creds’
  • If not exists it triggers a process to requests the permissions to the end-user through the API
  • If exists but it is expired, it will refresh the token
def get_permissions():
    creds = None
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)
    return creds

Getting messages from Gmail

Now we define a method which returns a list of emails which are called ‘messages’, the service variable is going to be declared at the end of the blog, within the main function, for now, let’s have in mind that we are calling the API and get the list of messages of the user ‘me’. The ‘me’ user means the end-user which is currently using our application

def get_messages(service):
    # Call the Gmail API, in order to get all messages
    return service.users().messages().list(userId='me').execute()['messages'] 

Getting email subjects

Once we have the list of emails from the API, we are able to request for the subjects of each email, so we are going to iterate into that list looking for some specific fields.

  • Define a list in which will be stored all the subjects found
  • We are looking for the id of the email, in order to identify each unique email.
  • After that, we call the API again in order to get all the information regarding that specific email and store the information on the ‘message’ variable
  • Once we have each email Id and its data, we are looking for the payload field due to it gathers the header’s value, both values are stored in individual variables but it can be collected in just one step.
  • Now we have the header of each email, we are going to iterate through it due we are looking for the Subject field, which is stored into a new variable and then appended to defined ‘subjects’ list
def get_subjects(emails, service):     
  subjects = []     
  for email in emails:         
    _id = email['id'] # get id of individual message         
    message = service.users().messages().get(userId='me', id=_id).execute() # fetch the message using API         
    payld = message['payload'] # get payload of the message          
    header = payld['headers'] # get header of the payload         

    for e_header in header:             
      if e_header['name'] == 'Subject':                 
        msg_subject = e_header['value']                 
        subjects.append(msg_subject)     
  return subjects 

Defining and calling main

It has been a lot of code and a lot of time to get into our final step. Here is where we are going to call all our predefined methods and finally print all the subjects collected.
In order to do that to following steps are performed

  • Declare a ‘creds’ variables to store our credentials (it include the scope)
  • Declare a ‘service’ variable which stored the service of the API, so the build method returns the service of Gmail with the respective credentials that were defined previously
  • Now call our predefined method to collect all the emails and store them into ’emails’ variable
  • Same procedure but now calling ‘get_subjects’ which returns a list with all the subjects of the emails provided as a parameter
  • And finally, just iterate through the list of subjects printing each subject.
def main():
    # Checking credentials
    creds = get_permissions()
    
    #Declare Gmail API service
    service = build('gmail', 'v1', credentials=creds)
    
    #Get all messages
    emails = get_messages(service)

    subjects = get_subjects(emails,service)
    
    for subject in subjects:
        print (subject)

if __name__ == '__main__':
    main() 
Output:
#Subjects:
10 destinos para visitar este verano
Developer .net en Praxis IT Costa Rica , nueva vacante de Developer en Heredia
Acabá el 2020 con COMPRAS INTELIGENTES

As can be seen on the following image, the subjects listed on the output can be found at the Gmail client as well

How-to-access-Gmail-through-API-with-Python-3-Gmail-client

Complete code

Here is all the code that we have developed through all the blog in just one block so you can analyze it in a complete file or maybe copy and paste if you prefer

# Import all needed libraries to use Gmail API
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# Scope provide different levels of privileges, this example is with readonly privileges
# If modifying these scopes, delete the file token.pickle.
# token.pickle files are auto generated after first API usage
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']

def main():
    # Checking credentials
    creds = get_permissions()
    
    #Declare Gmail API service
    service = build('gmail', 'v1', credentials=creds)
    
    #Get all messages
    emails = get_messages(service)

    #Get all subjects
    subjects = get_subjects(emails,service)
    
    #Printing all subjects
    for subject in subjects:
        print (subject)

def get_permissions():
    creds = None
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file('credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)
    return creds

def get_messages(service):
    # Call the Gmail API, in order to get all messages
    return service.users().messages().list(userId='me').execute()['messages']

def get_subjects(emails,service):
    subjects = []
    for email in emails:
        _id = email['id'] # get id of individual message
        message = service.users().messages().get(userId='me', id=_id).execute() # fetch the message using API
        payld = message['payload'] # get payload of the message 
        header = payld['headers'] # get header of the payload
        for e_header in header:
            if e_header['name'] == 'Subject':
                msg_subject = e_header['value']
                subjects.append(msg_subject)
    return subjects

if __name__ == '__main__':
    main() 

Resource types

The Gmail API provides several resource types, as they can be used in such different ways we will let you the following links so you can investigate deeper in the ones that you prefer

Sending emails

If you are interested in this API feature we invite you to read our upcoming blog: How to send emails using gmail using python 3 blog in which we will learn much more about this handy API

Get Notified Automatically Of New Articles

Error SendFox Connection: 403 Forbidden

403 Forbidden