Select Page

To automate sending of emails, we can write a Python program that will access any mail server. For this tutorial, we will use Google Mail server. But why Google Mail?

Google Mail (also known as GMail) is a highly secured server. Most examples on the Internet on how to send an email with GMail server has cut corners by advising developers to set “Less secure app access” on! This will only make one’s account vulnerable, exposing it to potential risks! Thankfully, on May 30, 2022 Google will disable this feature.

It is a little bit complex to implement Python scripts calling GMail functions, but I was able to have it working. Note that the application I created was tested to work only run on desktop client on a test environment and not yet in Production. I will update this post once I launch the application in Production.

PART 1 – Enabling Gmail API

PART 2 – Setup Python project workspace

PART 3 – Writing the Python Code

PART 4 – Running the Python program for sending an email

PART 5 – Troubleshooting

PART 1 – Enabling Gmail API

Step 1. Follow Steps 1 through 18 on How to Create Google Mail API Credentials JSON file in Google Cloud https://techiejackieblogs.com/how-to-create-google-mail-api-credentials-json/

You should have a JSON file downloaded to your local file system by the end of the above tutorial.

Step 2. Open the client_secret JSON file. This will be the credentials JSON file that will later be used by your Python program. The formatted file should look like this:

{
  "installed": {
    "client_id": "1000000000000-________________________________.apps.googleusercontent.com",
    "project_id": "________",
    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
    "token_uri": "https://oauth2.googleapis.com/token",
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_secret": "______-____________________________",
    "redirect_uris": [
      "http://localhost"
    ]
  }
}

Step 3. Go to your project’s API dashboard https://console.cloud.google.com/apis/dashboard. Click the “+ ENABLE APIS AND SERVICES” link.

Step 4. Search for Gmail API. Click the “ENABLE” button.

PART 2 – Setup Python project workspace

Step 1. Install Pycharm Community Edition in https://www.jetbrains.com/pycharm/download/
This tutorial uses the latest stable release pycharm-community-2021.1.2.exe

Step 2. Open Pycharm. Create New Project with a Virtual environment. The latest Pycharm installer will always have new virtual environment installed.

Step 3. Create new file with filename requirements.txt. Enter the following library names

google-api-python-client
google-auth-httplib2
google-auth-oauthlib

Step 4. Select “Terminal” on the bottom panel. This will launch a command line. Execute the command to install the libraries listed in requirements.txt

pip3 install -r requirements.txt

Step 5. Copy the JSON file created from PART 1 – Enabling Gmail API. Rename the file to credentials.json. You should now have 3 files in your project.

PART 3 – Writing the Python script

Write these lines of codes in main.py

import os
import pickle
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from base64 import urlsafe_b64encode
from email.mime.text import MIMEText

# Request all access (permission to read/send/receive emails, manage the inbox, and more)
SCOPES = ['https://mail.google.com/']
DESTINATION_EMAIL_ADDRESS = 'youremail@gmail.com'
EMAIL_SUBJECT = 'Testing from Python'
EMAIL_BODY = 'This is a test'


def gmail_authenticate():
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first time
    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 build('gmail', 'v1', credentials=creds)


def build_message(destination, obj, body):
    message = MIMEText(body, "html")
    message['to'] = destination
    message['subject'] = obj
    return {'raw': urlsafe_b64encode(message.as_bytes()).decode()}


def send_message(service, destination, obj, body):
    return service.users().messages().send(
        userId="me",
        body=build_message(destination, obj, body)
    ).execute()


if __name__ == "__main__":
    service = gmail_authenticate()
    send_message(service, DESTINATION_EMAIL_ADDRESS, EMAIL_SUBJECT, EMAIL_BODY)

PART 4 – Running the Python Code

Step 1. From Pycharm Terminal, run

python main.py

Step 2. A browser window will open so you can choose which account will be accessing the API. Note that “Gmail TJB” is the project name I created in PART 1.

Step 3. A warning message will be displayed. Google hasn’t verified this app. Verification will be in the final step prior to Production launching. For now, click the “Continue” button.

Step 4. An information on what the API wants to access will be displayed. If you are granting the API access to your email account, click the “Continue” button.

Step 5. This message will be displayed “The authentication flow has completed. You may close this window.” Return to Pycharm project and you will find that file token.pickle has been created after completing the authentication flow. File token.pickle stores the user’s access and refresh tokens

Step 6. Check your mailbox. You should receive the email sent by your Python program.

Note that next time you run the Python program, Steps 2 to 5 will be skipped since you already have token.pickle created in your local file system.

PART 5 – Troubleshooting

Error MessageCauseSolution
#1Authorization Error
Error 403: access_denied

The developer hasn’t given you access to this app. It’s currently being tested and it hasn’t been verified by Google. If you think you should have access, contact the developer (developers_email_address@gmail.com)
The SENDER_EMAIL_ADDRESS is not included in the Test Users listGo to APIs & Services -> OAuth consent screen . Add the SENDER_EMAIL_ADDRESS into the Test Users list
#2 raise HttpError(resp, content, uri=self.uri) googleapiclient.errors.HttpError: HttpError 403 when requesting https://gmail.googleapis.com/gmail/v1/users/me/messages/send?alt=json returned “Gmail API has not been used in project 1000000000000 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/gmail.googleapis.com/overview?project=1000000000000 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.”. Details: “[{‘message’: ‘Gmail API has not been used in project 1000000000000 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/gmail.googleapis.com/overview?project=1000000000000 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.’, ‘domain’: ‘usageLimits’, ‘reason’: ‘accessNotConfigured’, ‘extendedHelp’: ‘https://console.developers.google.com’}]”Access to Google API has not been enabled yet for the project.From a web browser, enable the Google API by entering the URL https://console.developers.google.com/apis/api/gmail.googleapis.com/overview?project=1000000000000 The URL is on the Details of the error message. Click the ENABLE button.
#3creds.refresh(Request())
TypeError: init() missing 1 required positional argument: ‘url’
This happens after running the application for a certain period, the token expires. In my application, token was expiring after an hour.Delete token.pickle from your file system and re-run the application.

References:

https://developers.google.com/gmail/api/quickstart/python

https://realpython.com/python-send-email/

https://www.thepythoncode.com/article/use-gmail-api-in-python

https://github.com/x4nth055/pythoncode-tutorials/tree/master/general/gmail-api