Python: Attaching files to emails via Mailgun API using httplib2

I’m trying to send an email with an attachment using the Mailgun API and httplib2 in Python. The file I want to attach is downloaded from Google Drive. My code successfully sends the email, but the attachment is missing. Here’s what I’ve done so far:

drive_service = build('drive', 'v3', http=authorized_http)

file_request = drive_service.files().export_media(fileId=doc_id, mimeType='application/pdf')

buffer = io.BytesIO()
downloader = MediaIoBaseDownload(buffer, file_request)

complete = False
while not complete:
    status, complete = downloader.next_chunk()
    print(f'Download progress: {int(status.progress() * 100)}%')

email_data = {
    'from': sender_address,
    'to': recipient_address,
    'subject': 'Test Email with Attachment',
    'text': 'This is a test message',
    'attachment': buffer.getvalue()
}

api_url = 'https://api.mailgun.net/v3/your-domain.com/messages'

encoded_data = urllib.parse.urlencode(email_data)

http_client = httplib2.Http()
http_client.add_credentials('api', 'your-api-key')

response, content = http_client.request(
    api_url, 'POST', encoded_data,
    headers={'Content-Type': 'application/x-www-form-urlencoded'}
)

What am I doing wrong? How can I make sure the attachment is included in the email?

I’ve encountered similar issues when working with Mailgun’s API for attachments. The problem in your code is likely due to the way the attachment data is being handled. When you use urllib.parse.urlencode(), it doesn’t properly encode file data. Instead, you need to use multipart/form-data encoding. I solved this by switching to the requests library, which supports multipart encoding out of the box. For example, include the file in a files dictionary with the appropriate tuple format, and then send your email by passing both data and files to requests.post. This should correctly attach your file. Also, verify that your Mailgun credentials and domain are correct to avoid any additional issues.

hey, i’ve had similar probs with mailgun. the key is using multipart/form-data for attachments. httplib2 doesn’t handle that well. try using the requests library instead. it’s way easier:

import requests

files = {'attachment': ('file.pdf', buffer.getvalue(), 'application/pdf')}
data = {'from': sender, 'to': recipient, 'subject': 'Test', 'text': 'Hello'}

r = requests.post('https://api.mailgun.net/v3/yourdomain/messages',
                  auth=('api', 'your-key'),
                  files=files,
                  data=data)

this should work better for ya. good luck!

Your approach is close, but the issue lies in how you’re sending the attachment data. Mailgun requires multipart/form-data encoding for attachments, which httplib2 doesn’t handle natively. Here’s a solution using the requests library instead:

import requests

files = {'attachment': ('document.pdf', buffer.getvalue(), 'application/pdf')}
data = {
    'from': sender_address,
    'to': recipient_address,
    'subject': 'Test Email with Attachment',
    'text': 'This is a test message'
}

response = requests.post(
    'https://api.mailgun.net/v3/your-domain.com/messages',
    auth=('api', 'your-api-key'),
    files=files,
    data=data
)

print(response.status_code)
print(response.text)

This should correctly attach your file to the email. Remember to replace ‘your-domain.com’ and ‘your-api-key’ with your actual Mailgun domain and API key.