SendGrid Attachments — Sending Files and Inline Images with Emails
In this tutorial, you will learn about SendGrid Attachments. We cover key concepts, practical examples, and best practices to help you master this topic.
SendGrid supports file attachments and inline images in emails through base64-encoded content attachments, supporting PDFs, images, ZIP files, and more with proper MIME type specification.
What You'll Learn
- How to add file attachments to emails
- How to embed inline images in email HTML
- How to handle attachment limits and best practices
Why It Matters
Transactional emails often need attachments: invoices as PDFs, reports as CSV files, or embedded product images. SendGrid's attachment API supports all of these, but requires proper encoding and size management to ensure deliverability.
Real-World Use
DodaTech sends monthly invoices as PDF attachments via SendGrid. The invoice PDF is generated server-side, base64-encoded, and attached with proper Content-Type and filename. The email HTML also includes the company logo as an inline image using Content-ID.
import base64
import requests
def send_invoice_with_attachment(to_email, invoice_id, pdf_bytes):
pdf_base64 = base64.b64encode(pdf_bytes).decode()
response = requests.post(
"https://api.sendgrid.com/v3/mail/send",
headers={
"Authorization": f"Bearer {SENDGRID_API_KEY}",
"Content-Type": "application/json"
},
json={
"personalizations": [{"to": [{"email": to_email}]}],
"from": {"email": "billing@dodatech.com"},
"subject": f"Invoice #{invoice_id}",
"content": [{"type": "text/html", "value": "<p>Your invoice is attached.</p>"}],
"attachments": [{
"content": pdf_base64,
"filename": f"invoice-{invoice_id}.pdf",
"type": "application/pdf",
"disposition": "attachment"
}]
}
)
return response.status_code == 202
Inline Images
import base64
def send_with_inline_images(to_email, image_paths):
attachments = []
for i, image_path in enumerate(image_paths):
with open(image_path, 'rb') as f:
image_data = base64.b64encode(f.read()).decode()
attachments.append({
"content": image_data,
"filename": f"image_{i}.png",
"type": "image/png",
"disposition": "inline",
"content_id": f"image_{i}"
})
# Reference the inline images in HTML
html = f"""
<html>
<body>
<h1>Product Showcase</h1>
<img src="cid:image_0" alt="Product 1" style="width:300px;">
<img src="cid:image_1" alt="Product 2" style="width:300px;">
<p>Check out our latest products above!</p>
</body>
</html>
"""
response = requests.post(
"https://api.sendgrid.com/v3/mail/send",
headers={
"Authorization": f"Bearer {SENDGRID_API_KEY}",
"Content-Type": "application/json"
},
json={
"personalizations": [{"to": [{"email": to_email}]}],
"from": {"email": "marketing@dodatech.com"},
"subject": "New Products Available!",
"content": [{"type": "text/html", "value": html}],
"attachments": attachments
}
)
return response.status_code == 202
Multiple Attachments with Python SDK
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail, Attachment, FileContent, FileName, FileType, Disposition, ContentId
import base64
def send_report_email(to_email, pdf_report, csv_data, logo_path):
message = Mail(
from_email='reports@dodatech.com',
to_emails=to_email,
subject='Monthly Report',
html_content='<h1>Monthly Report</h1><p>See attached files.</p><img src="cid:logo">'
)
# PDF attachment
pdf_encoded = base64.b64encode(pdf_report).decode()
attachment_pdf = Attachment(
FileContent(pdf_encoded),
FileName('monthly-report.pdf'),
FileType('application/pdf'),
Disposition('attachment')
)
message.add_attachment(attachment_pdf)
# CSV attachment
csv_encoded = base64.b64encode(csv_data.encode()).decode()
attachment_csv = Attachment(
FileContent(csv_encoded),
FileName('data-export.csv'),
FileType('text/csv'),
Disposition('attachment')
)
message.add_attachment(attachment_csv)
# Inline logo image
with open(logo_path, 'rb') as f:
logo_encoded = base64.b64encode(f.read()).decode()
attachment_logo = Attachment(
FileContent(logo_encoded),
FileName('logo.png'),
FileType('image/png'),
Disposition('inline'),
ContentId('logo')
)
message.add_attachment(attachment_logo)
sg = SendGridAPIClient(SENDGRID_API_KEY)
response = sg.send(message)
return response.status_code == 202
Common Mistakes
1. Exceeding Attachment Size Limits
SendGrid limits total email size to 30MB including headers and encoding. Base64 encoding increases size by ~37%. Keep total attachment size under 20MB.
2. Forgetting Content-ID for Inline Images
Inline images must have a content_id matching the cid: reference in the HTML. Without this, images appear as regular attachments.
3. Using Wrong Disposition
Use disposition: "attachment" for files and disposition: "inline" for images shown in the email body.
4. Not Setting Correct MIME Type
Incorrect MIME types cause some email clients to block or mishandle attachments. Always set the proper type (application/pdf, image/png, text/csv).
5. Encoding Large Files Inefficiently
Base64 encoding large files uses significant memory. Stream and encode in chunks if sending large attachments.
Practice Questions
- How are attachments encoded in SendGrid API requests?
- What is the total email size limit?
- How do you embed an image in the email body?
- What is the difference between inline and attachment disposition?
- How do you reference an inline image in HTML?
Answers
- Base64-encoded content in the attachments array. 2. 30MB total. 3. Add it as an attachment with disposition inline and content_id, then reference with cid:content_id in HTML. 4. Inline shows in the email body; attachment appears as a downloadable file. 5.
<img src="cid:content_id">.
Challenge
Build an email attachment service that accepts file uploads, automatically determines MIME type, encodes files within size limits, supports both inline images and file attachments, and sends the email with all attachments properly formatted.
FAQ
Mini Project
Build a report delivery system that generates monthly reports as PDFs, exports data as CSV, includes the company logo as an inline image, and sends everything as a single email through SendGrid with proper encoding, MIME types, and size validation.
What's Next
- Learn about categories and unique arguments for email tracking
- Explore substitution tags for legacy template personalization
- Continue to scheduling emails for delayed delivery
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro