Migration illustration Migration guide

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

  1. 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.

Need some help?
  1. 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.
  2. Update your integration
    Swap your SendGrid endpoints and credentials for Mailtrap’s (API or SMTP). See the migration sections below.
  3. Migrate templates
    Both platforms use Handlebars, so content migrates directly. See the templates sections below.
  4. Migrate suppressions
    Export SendGrid suppressions and import them into Mailtrap via CSV or manually. Click here for more information.
  5. Migrate users
    Add users from the User Management tab and review permissions during migration.
  6. Set up webhooks
    Follow the Mailtrap Webhooks step-by-step guide.
  7. 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.

 

Mailtrap tip

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-Category header if you are using SMTP or pass the category name in the category field if you are sending via API.
  • Using Categories with SendGrid: Pass categories inside the X-SMTPAPI JSON header if you are using SMTP, or pass an array of strings in the categories field 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

API migration

Authentication

Both platforms use Bearer token authentication in the Authorization header – same pattern, different key.

SendGridMailtrap
MethodAPI key (Bearer token)API token (Bearer token)
HeaderAuthorization: Bearer YOUR_SENDGRID_API_KEYAuthorization: Bearer YOUR_MAILTRAP_API_KEY
Key managementSettings → 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 typeSendGridMailtrapNotes
Transactional emailPOST https://api.sendgrid.com/v3/mail/sendPOST https://send.api.mailtrap.io/api/sendSendGrid wraps recipients in personalizations[]; Mailtrap uses flat JSON
Bulk emailSame endpoint (/v3/mail/send) with personalizations[] batchingPOST https://bulk.api.mailtrap.io/api/sendMailtrap isolates bulk on a separate host to protect transactional reputation
Template sendingSame endpoint with template_id + dynamic_template_dataSame send endpoint with template_uuid + template_variablesSendGrid dynamic template IDs start with d-
SuppressionsGET/DELETE https://api.sendgrid.com/v3/suppression/* (bounces, blocks, spam\_reports, invalid\_emails, unsubscribes)https://mailtrap.io/fr/api/accounts/{account_id}/suppressionsSendGrid splits suppressions across multiple endpoints by type
StatsGET https://api.sendgrid.com/v3/stats (global), /categories/stats, /mailbox_providers/statsGET/api/accounts/{account_id}/stats, /stats/domains, /stats/categories, /stats/email_service_providers, and /stats/dateSendGrid offers more granular stat breakdowns (by category, mailbox provider, browser, etc.)
Email logsGET https://api.sendgrid.com/v3/messages (Email Activity Feed)POST /api/1.0/messages/{search,info,content}.jsonSendGrid’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.

FieldSendGridMailtrap
Sender addressfrom.emailfrom.email
Sender namefrom.namefrom.name
Recipientspersonalizations[].to[].emailto[].email
Recipient namepersonalizations[].to[].nameto[].name
CC recipientspersonalizations[].cc[].emailcc[].email
CC namepersonalizations[].cc[].namecc[].name
BCC recipientspersonalizations[].bcc[].emailbcc[].email
BCC namepersonalizations[].bcc[].namebcc[].name
Subjectsubject (root or personalizations[].subject)subject
HTML bodycontent[].value (where type = text/html)html
Plain-text bodycontent[].value (where type = text/plain)text
Reply-to addressreply_to.emailreply_to.email
Reply-to namereply_to.namereply_to.name
Reply-to listreply_to_list[]
Custom headersheaders (root or personalizations[].headers)headers
Categoriescategories[] (array, up to 10)category (single string)
Custom metadatacustom_args (root or personalizations[].custom_args)custom_variables
Template IDtemplate_idtemplate_uuid
Template datapersonalizations[].dynamic_template_datatemplate_variables
Substitutionspersonalizations[].substitutions
Attachment contentattachments[].content (Base64)attachments[].content (Base64)
Attachment filenameattachments[].filenameattachments[].filename
Attachment MIME typeattachments[].typeattachments[].type
Attachment dispositionattachments[].dispositionattachments[].disposition
Attachment content IDattachments[].content_idattachments[].content_id
Scheduled sendsend_at (Unix timestamp)
Batch IDbatch_id
IP poolip_pool_name
Open trackingtracking_settings.open_tracking.enableVia account settings or headers
Click trackingtracking_settings.click_tracking.enableVia account settings or headers
Multi-recipient batchingpersonalizations[] (up to 1,000 recipients per request)
Sending streamSingle endpoint for all email typesDetermined by endpoint: send.api.mailtrap.io (transactional) or bulk.api.mailtrap.io (bulk)
Batch sendVia personalizations[] in a single requestPOST /api/batch (up to 500 emails)

Code snippets

  • Mailtrap cURL SDK code snippet cURL
  • Mailtrap PHP SDK code snippet PHP
SendGrid SendGrid
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>"
    }
  ]
}'
Mailtrap Mailtrap

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. personalizations wrapper. SendGrid nests recipients, subjects, and dynamic data inside a personalizations array. Mailtrap keeps things flat: to, cc, bcc, subject, and template_variables all 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 a content array where each element declares a MIME type and a value. Mailtrap uses dedicated html and text fields 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.io for transactional, bulk.api.mailtrap.io for bulk – isolating the two at the infrastructure level.

SMTP migration

SettingSendGridMailtrap (Transactional)Mailtrap (Bulk)
Hostsmtp.sendgrid.netlive.smtp.mailtrap.iobulk.smtp.mailtrap.io
Port587 (recommended), 465, 25587 (recommended), 25, 2525587 (recommended), 25, 2525
TLSNot requiredRequiredRequired
AuthenticationPLAIN, LOGINPLAIN, LOGINPLAIN, LOGIN
Usernameapikey stringapi stringapi string
PasswordAPI keyAPI tokenAPI 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 uses apikey. 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

LimitSendGridMailtrap
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 MB10 MB default (extendable to 30 MB on request)
Max recipients per single send1,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

PatternSendGridMailtrap
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 APIpersonalizations[].dynamic_template_data (JSON object)template_variables (JSON object)
Template identifiertemplate_id (dynamic templates prefixed with d-, e.g. d-abc123)template_uuid
Legacy substitutionspersonalizations[].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 insert helper ({{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 with formatDate calls, 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, and length helpers 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 inside personalizations[]) to template_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 uses template_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 subject set in the API request. Mailtrap behaves the same way – if the template includes a subject, the subject field in the API request is ignored.

Question icon

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 from api.sendgrid.com/v3/mail/send to send.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 personalizations wrapper and move to, cc, bcc, and subject to the root level. Replace the content[] array with flat html and text fields. 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 category string instead of SendGrid’s categories[] array (up to 10). Pick your primary category for the category field and move any secondary labels into custom_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_id with template_uuid and personalizations[].dynamic_template_data with template_variables. Store your UUIDs carefully – Mailtrap doesn’t allow recovering or reassigning them after deletion.