Migration Guide — Switch from SendGrid, Mailgun, Postmark & SES
Step-by-step guide to migrate your email infrastructure to Ark from SendGrid, Mailgun, Postmark, or Amazon SES. API mapping, domain migration, and suppression import.
Migrate your transactional email from another provider to Ark. This guide covers API mapping, domain migration, suppression list import, and webhook reconfiguration for the four most common providers.
import sendgridfrom sendgrid.helpers.mail import Mailsg = sendgrid.SendGridAPIClient(api_key="SG.xxx")message = Mail( from_email="hello@yourdomain.com", to_emails="user@example.com", subject="Order Shipped", html_content="<p>Your order is on its way!</p>")message.custom_args = {"order_id": "ord_123"}sg.send(message)
After (Ark):
Copy
from ark import Arkclient = Ark()client.emails.send( from_="hello@yourdomain.com", to=["user@example.com"], subject="Order Shipped", html="<p>Your order is on its way!</p>", metadata={"order_id": "ord_123"})
import requestsrequests.post( "https://api.mailgun.net/v3/yourdomain.com/messages", auth=("api", "key-xxx"), data={ "from": "hello@yourdomain.com", "to": ["user@example.com"], "subject": "Order Shipped", "html": "<p>Your order is on its way!</p>", "v:order_id": "ord_123" })
After (Ark):
Copy
from ark import Arkclient = Ark()client.emails.send( from_="hello@yourdomain.com", to=["user@example.com"], subject="Order Shipped", html="<p>Your order is on its way!</p>", metadata={"order_id": "ord_123"})
from postmarker.core import PostmarkClientpostmark = PostmarkClient(server_token="xxx")postmark.emails.send( From="hello@yourdomain.com", To="user@example.com", Subject="Order Shipped", HtmlBody="<p>Your order is on its way!</p>", Tag="order_notification", Metadata={"order_id": "ord_123"}, MessageStream="outbound")
After (Ark):
Copy
from ark import Arkclient = Ark()client.emails.send( from_="hello@yourdomain.com", to=["user@example.com"], subject="Order Shipped", html="<p>Your order is on its way!</p>", tag="order_notification", metadata={"order_id": "ord_123"})
import boto3ses = boto3.client("ses", region_name="us-east-1")ses.send_email( Source="hello@yourdomain.com", Destination={"ToAddresses": ["user@example.com"]}, Message={ "Subject": {"Data": "Order Shipped"}, "Body": { "Html": {"Data": "<p>Your order is on its way!</p>"}, "Text": {"Data": "Your order is on its way!"} } }, Tags=[{"Name": "order_id", "Value": "ord_123"}])
After (Ark):
Copy
from ark import Arkclient = Ark()client.emails.send( from_="hello@yourdomain.com", to=["user@example.com"], subject="Order Shipped", html="<p>Your order is on its way!</p>", text="Your order is on its way!", metadata={"order_id": "ord_123"})
You can use the same sending domain with Ark. The key DNS changes:
Record
What Changes
Replace old provider’s include: with include:spf.arkhq.io
Add Ark’s DKIM record (different selector: ark._domainkey)
Return Path
Update CNAME to point to rp.arkhq.io
No changes needed if already configured
Update SPF — don’t duplicate. You can only have one SPF TXT record per domain. Replace your old provider’s include with Ark’s, or add both includes to the same record if running in parallel during migration.Example:v=spf1 include:spf.arkhq.io include:sendgrid.net ~all (during migration, then remove the old provider after cutover).
Export your suppression/bounce list from your old provider and import it into Ark to avoid sending to known-bad addresses:
Python
Node.js
cURL
Copy
from ark import Arkimport csvclient = Ark()def import_suppressions(csv_file: str): """Import suppression list from CSV (email,reason columns).""" suppressions = [] with open(csv_file) as f: reader = csv.DictReader(f) for row in reader: suppressions.append({ "email": row["email"], "reason": row.get("reason", "imported") }) # Bulk import in batches of 100 for i in range(0, len(suppressions), 100): batch = suppressions[i:i+100] client.suppressions.bulk_create( suppressions=batch ) print(f"Imported {min(i+100, len(suppressions))}/{len(suppressions)}")# Export from your old provider, then:import_suppressions("sendgrid_suppressions.csv")
Copy
import Ark from 'ark';import fs from 'fs';import { parse } from 'csv-parse/sync';const client = new Ark();async function importSuppressions(csvFile: string) { const content = fs.readFileSync(csvFile, 'utf-8'); const records = parse(content, { columns: true }); // Bulk import in batches of 100 for (let i = 0; i < records.length; i += 100) { const batch = records.slice(i, i + 100).map((row: any) => ({ email: row.email, reason: row.reason || 'imported', })); await client.suppressions.bulkCreate({ suppressions: batch, }); console.log(`Imported ${Math.min(i + 100, records.length)}/${records.length}`); }}await importSuppressions('sendgrid_suppressions.csv');