Migrate to Mailtrap from SendGrid
A complete technical guide to easily switch from SendGrid to Mailtrap. Most teams complete the migration process in under an hour.
Migration Checklist
- Authenticate your domain
Add and verify your domain before sending. Follow the Domain Setup article at docs.mailtrap.io.
Need some help?
Contact our support and our developers will help you with it.
- Get your API token
Mailtrap auto-generates a token when you add a domain. Find it under Settings → API Tokens. Read more on API tokens. - Update your integration
Swap your SendGrid endpoints and credentials for Mailtrap’s (API or SMTP). See the migration sections below. - Migrate templates
Both platforms use Handlebars, so content migrates directly. See the templates sections below. - Migrate suppressions
Export SendGrid suppressions and import them into Mailtrap via CSV or manually. Click here for more information. - Migrate users
Add users from the User Management tab and review permissions during migration. - Set up webhooks
Follow the Mailtrap Webhooks step-by-step guide. - Security and compliance
Visit the Trust Center page to review Mailtrap’s security practices and compliance standards.
Mailtrap tip
You can use ActionMailer Balancer Ruby gem to proportionally distribute the email sending load between two different sending services (e.g. 70% SendGrid and 30% Mailtrap) to mitigate the sending risks.
Concepts
Before going over the technical details, it’s important to clear up some key concepts.
Sending domains
SendGrid equivalent: Domain Authentication
Before being able to send emails with Mailtrap, you first need to add and verify your sending domain. For domain verification, Mailtrap provides DNS records, which are the same standards SendGrid relies on.
DKIM records can be added alongside SendGrid’s (different selectors, no conflict), and one DMARC record covers both. For SPF, however, you must merge Mailtrap’s include into your existing SPF record rather than adding a separate one.
To learn how to verify your domain, you can read our step-by-step knowledge base article, or watch the video we prepared for you. The whole process takes ~15 minutes.
Separate sending streams
SendGrid equivalent: N/A
Mailtrap provides two separate email infrastructures, or sending streams:
- Transactional Stream – For sending user-triggered emails like welcome emails and password resets.
- Bulk Stream – For sending promotional, marketing, emails like newsletters and product updates.
By keeping the sending infrastructures separate, you are able to:
- Protect your transactional email reputation from the performance of your bulk campaigns
- Ensure each stream routes through the right IP pools
- Give mailbox providers the signals they need to categorize and deliver your emails correctly

Email categories
SendGrid equivalent: Categories
Like SendGrid, Mailtrap offers an Email Categories feature that lets you track the performance of different types of emails (e.g., welcome emails, password resets, etc.).
- Using Email Categories with Mailtrap: Insert a category name into the
X-MT-Categoryheader if you are using SMTP or pass the category name in thecategoryfield if you are sending via API. - Using Categories with SendGrid: Pass categories inside the
X-SMTPAPIJSON header if you are using SMTP, or pass an array of strings in thecategoriesfield if you are sending via API. SendGrid supports up to 10 categories per email.

Organization & sub-accounts
SendGrid equivalent: Subusers
Mailtrap offers its Organization & sub-accounts feature, which lets you manage complex setups involving multiple teams, clients, environments, or products under a single Organization. To start using the feature, you first need to enable it under the Organization tab.

Note: The feature is available to from Business plan onward.
Terminology comparison
- Sending Domains
- Email Logs
- User Management
- Email Templates
- Email Categories
- Headers
- X-MT-Custom-Variables
- Audit Logs
- Domain Authentication
- Email Activity
- Teammates
- SendGrid Templates
- Categories
- X-SMTPAPI
- Unique Arguments
- Activity
API migration
Authentication
Both platforms use Bearer token authentication in the Authorization header – same pattern, different key.
| SendGrid | Mailtrap | |
| Method | API key (Bearer token) | API token (Bearer token) |
| Header | Authorization: Bearer YOUR_SENDGRID_API_KEY | Authorization: Bearer YOUR_MAILTRAP_API_KEY |
| Key management | Settings → API Keys (full access, restricted, billing) | Settings → API Tokens (auto-generated per domain, editable permissions) |
No code change needed beyond swapping the key value. The header format is identical.
API mapping
| API type | SendGrid | Mailtrap | Notes |
| Transactional email | POST https://api.sendgrid.com/v3/mail/send | POST https://send.api.mailtrap.io/api/send | SendGrid wraps recipients in personalizations[]; Mailtrap uses flat JSON |
| Bulk email | Same endpoint (/v3/mail/send) with personalizations[] batching | POST https://bulk.api.mailtrap.io/api/send | Mailtrap isolates bulk on a separate host to protect transactional reputation |
| Template sending | Same endpoint with template_id + dynamic_template_data | Same send endpoint with template_uuid + template_variables | SendGrid dynamic template IDs start with d- |
| Suppressions | GET/DELETE https://api.sendgrid.com/v3/suppression/* (bounces, blocks, spam\_reports, invalid\_emails, unsubscribes) | https://mailtrap.io/fr/api/accounts/{account_id}/suppressions | SendGrid splits suppressions across multiple endpoints by type |
| Stats | GET https://api.sendgrid.com/v3/stats (global), /categories/stats, /mailbox_providers/stats | GET/api/accounts/{account_id}/stats, /stats/domains, /stats/categories, /stats/email_service_providers, and /stats/date | SendGrid offers more granular stat breakdowns (by category, mailbox provider, browser, etc.) |
| Email logs | GET https://api.sendgrid.com/v3/messages (Email Activity Feed) | POST /api/1.0/messages/{search,info,content}.json | SendGrid’s activity feed requires an additional add-on for extended history |
Outbound Sending API JSON Field Mapping
SendGrid uses POST https://api.sendgrid.com/v3/mail/send; Mailtrap uses POST https://send.api.mailtrap.io/api/send. Both accept application/json.
| Field | SendGrid | Mailtrap |
| Sender address | from.email | from.email |
| Sender name | from.name | from.name |
| Recipients | personalizations[].to[].email | to[].email |
| Recipient name | personalizations[].to[].name | to[].name |
| CC recipients | personalizations[].cc[].email | cc[].email |
| CC name | personalizations[].cc[].name | cc[].name |
| BCC recipients | personalizations[].bcc[].email | bcc[].email |
| BCC name | personalizations[].bcc[].name | bcc[].name |
| Subject | subject (root or personalizations[].subject) | subject |
| HTML body | content[].value (where type = text/html) | html |
| Plain-text body | content[].value (where type = text/plain) | text |
| Reply-to address | reply_to.email | reply_to.email |
| Reply-to name | reply_to.name | reply_to.name |
| Reply-to list | reply_to_list[] | — |
| Custom headers | headers (root or personalizations[].headers) | headers |
| Categories | categories[] (array, up to 10) | category (single string) |
| Custom metadata | custom_args (root or personalizations[].custom_args) | custom_variables |
| Template ID | template_id | template_uuid |
| Template data | personalizations[].dynamic_template_data | template_variables |
| Substitutions | personalizations[].substitutions | — |
| Attachment content | attachments[].content (Base64) | attachments[].content (Base64) |
| Attachment filename | attachments[].filename | attachments[].filename |
| Attachment MIME type | attachments[].type | attachments[].type |
| Attachment disposition | attachments[].disposition | attachments[].disposition |
| Attachment content ID | attachments[].content_id | attachments[].content_id |
| Scheduled send | send_at (Unix timestamp) | — |
| Batch ID | batch_id | — |
| IP pool | ip_pool_name | — |
| Open tracking | tracking_settings.open_tracking.enable | Via account settings or headers |
| Click tracking | tracking_settings.click_tracking.enable | Via account settings or headers |
| Multi-recipient batching | personalizations[] (up to 1,000 recipients per request) | — |
| Sending stream | Single endpoint for all email types | Determined by endpoint: send.api.mailtrap.io (transactional) or bulk.api.mailtrap.io (bulk) |
| Batch send | Via personalizations[] in a single request | POST /api/batch (up to 500 emails) |
Code snippets
-
cURL
-
PHP
curl --request POST \
--url https://api.sendgrid.com/v3/mail/send \
--header 'Authorization: Bearer YOUR_SENDGRID_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"personalizations": [
{
"to": [
{
"email": "recipient@example.com",
"name": "Jane Doe"
}
]
}
],
"from": {
"email": "sender@yourdomain.com",
"name": "Your App"
},
"subject": "Order Confirmation #1234",
"content": [
{
"type": "text/plain",
"value": "Your order has been confirmed."
},
{
"type": "text/html",
"value": "<h1>Order Confirmed</h1><p>Your order #1234 has been confirmed.</p>"
}
]
}'
Copy
curl --request POST \
--url https://send.api.mailtrap.io/api/send \
--header 'Authorization: Bearer YOUR_MAILTRAP_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"from": {
"email": "sender@yourdomain.com",
"name": "Your App"
},
"to": [
{
"email": "recipient@example.com",
"name": "Jane Doe"
}
],
"subject": "Order Confirmation #1234",
"text": "Your order has been confirmed.",
"html": "<h1>Order Confirmed</h1><p>Your order #1234 has been confirmed.</p>"
}'
Note:
- Flat structure vs.
personalizationswrapper. SendGrid nests recipients, subjects, and dynamic data inside apersonalizationsarray. Mailtrap keeps things flat:to,cc,bcc,subject, andtemplate_variablesall live at the root of the request body. For single-email transactional calls (the common case), this cuts the nesting and the boilerplate. - Body fields:
content[]array vs.html/text. SendGrid wraps body content in acontentarray where each element declares a MIMEtypeand avalue. Mailtrap uses dedicatedhtmlandtextfields at the root. Less indirection, easier to read in a diff. - Auth: identical pattern. Both use
Authorization: Bearer <api_key>. Swap the key, update the endpoint URL, adjust the payload structure. - Sending stream selection is structural. SendGrid routes all email through a single endpoint (
/v3/mail/send). Mailtrap uses separate endpoints –send.api.mailtrap.iofor transactional,bulk.api.mailtrap.iofor bulk – isolating the two at the infrastructure level.
SMTP migration
| Setting | SendGrid | Mailtrap (Transactional) | Mailtrap (Bulk) |
| Host | smtp.sendgrid.net | live.smtp.mailtrap.io | bulk.smtp.mailtrap.io |
| Port | 587 (recommended), 465, 25 | 587 (recommended), 25, 2525 | 587 (recommended), 25, 2525 |
| TLS | Not required | Required | Required |
| Authentication | PLAIN, LOGIN | PLAIN, LOGIN | PLAIN, LOGIN |
| Username | apikey string | api string | api string |
| Password | API key | API token | API token |
Migration notes:
- Mailtrap uses separate SMTP hosts for transactional (
live.smtp.mailtrap.io) and bulk (bulk.smtp.mailtrap.io) streams. SendGrid uses a single host (smtp.sendgrid.net) for all sending. - The SMTP username is a literal string in both cases, but they differ: Mailtrap uses
api, SendGrid usesapikey. Note that neither is a placeholder and both must be entered exactly as shown.

For more information on migrating your SMTP configuration, click this link. ⬅️
Rate limits & quotas
| Limit | SendGrid | Mailtrap |
| API rate limit (mail send endpoint) | ~600 requests/minute (General API) | 150 requests per 10 seconds per API token |
| Batch size (max emails per call) | No native batch endpoint (1,000 recipients per single send) | 500 emails per batch call |
| Message size limit (incl. attachments) | 30 MB | 10 MB default (extendable to 30 MB on request) |
| Max recipients per single send | 1,000 (across to, cc, bcc) | Single send ( /api/send ) → 1 email, up to 1,000 recipients per field ( to / cc / bcc ) Batch send ( /api/batch ) → up to 500 separate emails per API call |
Email templates
Both SendGrid and Mailtrap use Handlebars syntax for dynamic templates. Basic variable insertion ({{variable}}), conditionals ({{#if}}), iteration ({{#each}}), and unescaped HTML ({{{variable}}}) work the same way on both platforms. Your template markup will mostly port directly; the differences are in helper functions, default value syntax, and how variables are passed via the API.
Syntax comparison
| Pattern | SendGrid | Mailtrap |
| Variable insertion | {{variable_name}} | {{variable_name}} |
| Nested access | {{user.profile.firstName}} | {{user.profile.firstName}} |
| Unescaped HTML | {{{variable}}} | {{{variable}}} |
| Default/fallback value | {{insert name "default=Customer"}} (via insert helper) | {{#if name}}{{name}}{{else}}Customer{{/if}} |
| Conditionals | {{#if condition}}...{{else}}...{{/if}} | {{#if condition}}...{{else}}...{{/if}} |
| Negated conditional | {{#unless condition}}...{{/unless}} | {{#unless condition}}...{{/unless}} |
| Equality check | {{#equals status "active"}}...{{/equals}} | – |
| Comparison helpers | {{#greaterThan a b}}, {{#lessThan a b}}, {{#notEquals a b}} | – |
| Logical operators | {{#and condition1 condition2}}, {{#or condition1 condition2}} | – |
| Length check | {{#length items}} | – |
| Iteration | {{#each items}}...{{/each}} | {{#each items}}...{{/each}} |
| Date formatting | {{formatDate timestamp "MMMM DD, YYYY"}} (built-in helper, accepts epoch or ISO 8601, supports timezone offset) | Not supported – format dates before passing them to the template |
| Pass variables via API | personalizations[].dynamic_template_data (JSON object) | template_variables (JSON object) |
| Template identifier | template_id (dynamic templates prefixed with d-, e.g. d-abc123) | template_uuid |
| Legacy substitutions | personalizations[].substitutions (for non-dynamic templates) | – |
Migration notes
- Variables and loops transfer directly. Both platforms use standard Handlebars –
{{var}},{{#if}},{{#each}}, and{{{unescaped}}}work identically. No template markup changes needed for these. - Default values need rewriting. SendGrid’s
inserthelper ({{insert name "default=Customer"}}) is a SendGrid-specific extension. In Mailtrap, use a standard Handlebars{{#if}}/{{else}}block instead:{{#if name}}{{name}}{{else}}Customer{{/if}}. More verbose, but standard Handlebars – no vendor lock-in. - Date formatting moves to your application layer. SendGrid’s
{{formatDate timestamp "MM/DD/YYYY"}}helper formats dates inside the template. Mailtrap doesn’t support a date formatting helper, so format dates in your code before passing them as template variables. If you have templates withformatDatecalls, replace each with a pre-formatted string variable (e.g.,{{formatted_date}}) and compute the value server-side. - Comparison and logical helpers aren’t available. SendGrid supports
greaterThan,lessThan,notEquals,and,or, andlengthhelpers for in-template logic. Mailtrap uses standard Handlebars, which doesn’t include these. Move that logic to your application: compute the result and pass a boolean or pre-resolved value as a template variable. - Variable passing is almost identical. Both use a JSON object – rename the field from
dynamic_template_data(nested insidepersonalizations[]) totemplate_variables(at the root level). The variable structure itself stays the same. - Template IDs change format. SendGrid dynamic templates use a d- prefixed string (e.g.,
d-abc123def456). Mailtrap usestemplate_uuid. You’ll need to recreate templates in Mailtrap and update the IDs in your code. - Templates override subject and content. In SendGrid, a dynamic template with a subject line overrides any
subjectset in the API request. Mailtrap behaves the same way – if the template includes a subject, thesubjectfield in the API request is ignored.
Frequently Asked Questions
-
Do I need to re-verify my domain if I’ve already set it up in SendGrid?
Yes, you’ll need to re-verify your domain. See the Mailtrap Knowledge Base for up-to-date guidance.
-
Why does Mailtrap have two separate SMTP hosts?
Mailtrap separates transactional and bulk streams to protect your sender reputation and ensure proper delivery routing for each email type.
-
The auth header looks the same. Do I just swap the API key?
Almost. Both services use
Authorization: Bearer <api_key>, so the header format is identical. Swap the key value, update the endpoint URL fromapi.sendgrid.com/v3/mail/sendtosend.api.mailtrap.io/api/send, and adjust the JSON payload structure (see below ⬇️). -
How much of my request payload do I need to rewrite?
The core structure changes, but the data stays the same. Strip the
personalizationswrapper and moveto,cc,bcc, andsubjectto the root level. Replace thecontent[]array with flathtmlandtextfields. If you’re sending a single transactional email (the common case), this simplifies the payload significantly. -
I use multiple SendGrid ‘categories’ per email. How do I handle that in Mailtrap?
Mailtrap accepts a single
categorystring instead of SendGrid’scategories[]array (up to 10). Pick your primary category for thecategoryfield and move any secondary labels intocustom_variables, where you can store arbitrary key-value metadata. -
Does Mailtrap support SendGrid’s ‘personalizations’ for batch sending?
Not as a single-call array. SendGrid lets you bundle multiple recipient groups with different subjects and substitutions in one request via
personalizations[]. In Mailtrap, use the bulk endpoint (bulk.api.mailtrap.io/api/send) and send up to 500 emails per batch call. Each email in the batch is a separate object with its own recipients and variables. -
Does Mailtrap offer migration assistance?
Yes, Mailtrap offers migration assistance from Business plan onwards.
-
How to migrate templates?
Both platforms use Handlebars syntax, so standard template markup ports directly. The one exception: SendGrid’s
{{#equals}}helper isn’t supported in Mailtrap – replace equality checks with pre-computed boolean variables passed at send time.On the API side, replace
template_idwithtemplate_uuidandpersonalizations[].dynamic_template_datawithtemplate_variables. Store your UUIDs carefully – Mailtrap doesn’t allow recovering or reassigning them after deletion.