Why Migrate?
SendGrid is a powerful platform, but many developers only use a fraction of it. If your use case is sending password resets, order confirmations, and notifications, SMTPfast gets the job done with less complexity.
Step 1: Create Your SMTPfast Account
Sign up at smtpfa.st and verify your email address. No credit card required for the free tier (3,000 emails/month).
Step 2: Add Your Sending Domain
In the SMTPfast dashboard, go to Domains and add your sending domain. You will get:
- 3 CNAME records for DKIM
- An SPF include directive
- A recommended DMARC record
Add these DNS records alongside your existing SendGrid records. Both can coexist during the migration.
# Your SPF record can include both during migration
v=spf1 include:sendgrid.net include:amazonses.com ~all
Step 3: Generate an API Key
Go to API Keys in the dashboard and create a new key with email:send scope. Copy it immediately - you will only see it once.
Store it as an environment variable:
export SMTPFAST_API_KEY=sf_live_your_new_key
Step 4: Update Your Code
Here is how the code changes for each language.
Node.js
Before (SendGrid):
const sgMail = require("@sendgrid/mail");
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
await sgMail.send({
to: "[email protected]",
from: "[email protected]",
subject: "Your order has shipped",
html: "<h1>Order shipped!</h1><p>Tracking: XYZ123</p>",
});
After (SMTPfast):
await fetch("https://smtpfa.st/api/v1/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMTPFAST_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
from: "[email protected]",
to: "[email protected]",
subject: "Your order has shipped",
html: "<h1>Order shipped!</h1><p>Tracking: XYZ123</p>",
}),
});
You can remove the @sendgrid/mail dependency entirely. No SDK needed.
Python
Before (SendGrid):
from sendgrid import SendGridAPIClient
from sendgrid.helpers.mail import Mail
message = Mail(
from_email="[email protected]",
to_emails="[email protected]",
subject="Your order has shipped",
html_content="<h1>Order shipped!</h1><p>Tracking: XYZ123</p>",
)
sg = SendGridAPIClient(os.environ.get("SENDGRID_API_KEY"))
response = sg.send(message)
After (SMTPfast):
import requests
import os
response = requests.post(
"https://smtpfa.st/api/v1/emails",
headers={
"Authorization": f"Bearer {os.environ['SMTPFAST_API_KEY']}",
"Content-Type": "application/json",
},
json={
"from": "[email protected]",
"to": "[email protected]",
"subject": "Your order has shipped",
"html": "<h1>Order shipped!</h1><p>Tracking: XYZ123</p>",
},
)
You can remove the sendgrid package. The requests library handles everything.
cURL
Before (SendGrid):
curl -X POST https://api.sendgrid.com/v3/mail/send \
-H "Authorization: Bearer $SENDGRID_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"personalizations": [{"to": [{"email": "[email protected]"}]}],
"from": {"email": "[email protected]"},
"subject": "Your order has shipped",
"content": [{"type": "text/html", "value": "<h1>Order shipped!</h1>"}]
}'
After (SMTPfast):
curl -X POST https://smtpfa.st/api/v1/emails \
-H "Authorization: Bearer $SMTPFAST_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"from": "[email protected]",
"to": "[email protected]",
"subject": "Your order has shipped",
"html": "<h1>Order shipped!</h1>"
}'
Notice how much simpler the payload is. No nested personalizations array, no content array with type fields. Just from, to, subject, and html.
Step 5: Map SendGrid Concepts to SMTPfast
| SendGrid | SMTPfast | Notes |
|---|---|---|
personalizations[].to |
to |
String or array of strings |
personalizations[].cc |
cc |
Same format |
personalizations[].bcc |
bcc |
Same format |
from.email |
from |
String, supports "Name |
reply_to.email |
reply_to |
String or array of strings |
subject |
subject |
Same |
content[].value (text/html) |
html |
Direct HTML string |
content[].value (text/plain) |
text |
Direct text string |
custom_args |
tags |
Array of {name, value} objects |
headers |
headers |
Record of key-value pairs |
| Dynamic templates | HTML with variables | No template engine - use your own |
Step 6: Update Webhooks
If you use SendGrid's Event Webhook, you will need to set up equivalent webhooks in SMTPfast.
SendGrid event types mapped to SMTPfast:
| SendGrid Event | SMTPfast Event |
|---|---|
delivered |
delivered |
bounce |
bounced |
spam_report |
complained |
open |
opened |
click |
clicked |
Update your webhook handler to accept SMTPfast's payload format:
// Before: SendGrid webhook
app.post("/webhooks/sendgrid", (req, res) => {
// SendGrid sends an array of events
const events = req.body;
events.forEach((event) => {
switch (event.event) {
case "delivered": // ...
case "bounce": // ...
}
});
res.sendStatus(200);
});
// After: SMTPfast webhook
app.post("/webhooks/email", (req, res) => {
// SMTPfast sends one event per request
const event = req.body;
switch (event.type) {
case "delivered": // ...
case "bounced": // ...
}
res.sendStatus(200);
});
Step 7: Test in Parallel
Before cutting over completely, run both providers in parallel for a few days:
async function sendEmail(to, subject, html) {
// Send via SMTPfast
const smtpfastResult = await fetch("https://smtpfa.st/api/v1/emails", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SMTPFAST_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ from: "[email protected]", to, subject, html }),
});
if (!smtpfastResult.ok) {
// Fall back to SendGrid during migration
console.error("SMTPfast failed, falling back to SendGrid");
await sgMail.send({ to, from: "[email protected]", subject, html });
}
}
Once you are confident SMTPfast is delivering reliably, remove the SendGrid fallback.
Step 8: Clean Up
After the migration is complete:
- Remove the SendGrid SDK from your dependencies
- Delete the
SENDGRID_API_KEYenvironment variable - Remove SendGrid's SPF include from your DNS
- Revoke your SendGrid API keys
- Cancel your SendGrid plan
Migration Checklist
- SMTPfast account created
- Sending domain added and verified
- DNS records configured (DKIM, SPF, DMARC)
- API key generated and stored securely
- Code updated to use SMTPfast API
- Webhooks reconfigured
- Parallel testing completed
- SendGrid fallback removed
- SendGrid dependencies removed
- SendGrid API keys revoked
- SendGrid SPF include removed from DNS
Ready to get started?
Start sending transactional email today. Free to start, no credit card required.
Get Started for FreeRelated Posts
One approval instead of five DNS records: Domain Connect on SMTPfast
If your domain is on Cloudflare, SMTPfast can publish its sending records, and separately the inbound MX, through Domain Connect. You review each set on Cloudflare's screen, approve, and the records are written for you. Here is how it works, what gets written, and what to do when your DNS lives elsewhere.
Logs API and webhook deliveries: answer 'did it send?' without opening the dashboard
Two additions for the debugging side of email: the event log behind the Logs page is now an API, and every webhook has a delivery log with attempts, responses, automatic retries and a retry button.