---
title: "Postmark to Resend"
slug: postmark
description: "A guide on how to quickly switch from Postmark to Resend."
image: /static/migrate/postmark-og.jpg
hero_image: /static/migrate/postmark-resend.png
updated_at: "2024-11-28"
---

## Introduction

If you're considering migrating from Postmark to Resend, you're in the right place.

This guide will help you understand the key differences between the two services and provide you with the necessary steps to make the transition as smooth as possible.

**Jump ahead**

- **[History](#history)**
- **[Concepts](#concepts)**
- **[Official SDKs](#official-sdks)**
- **[Send email via API](#send-email-via-api)**
- **[Send email via SMTP](#send-email-via-smtp)**
- **[Webhooks](#webhooks)**
- **[Security & Privacy](#security-privacy)**
- **[Idempotency Keys](#idempotency-keys)**
- **[Additional Features](#additional-features)**
- **[Pricing](#pricing)**
- **[Conclusion](#conclusion)**

## History

Postmark and Resend are both email delivery services, but they have different histories and focuses.

**Key differences**

- Postmark was founded in 2009. In May 2020, [ActiveCampaign acquired Postmark](https://www.activecampaign.com/about/newsroom/press-releases/activecampaign-acquires-postmark).
- Resend was founded in 2023. Resend has a focus on providing a modern developer experience.

## Concepts

Both Postmark and Resend provide user-friendly dashboards for managing your email sending.

**Sender Signatures**

In Postmark you need a confirmed _Sender Signature_ or _Verified Domain_ for each email address you want to send from.

Resend has the concept of _Domains_ which allows you to send from any address under that domain.

<img
  src="/static/product-pages/screenshot-domain.png"
  alt="Resend Domains page"
  className="extraWidth"
/>

Both Postmark and Resend offer similar authentication features.

<Table
  data={{
    headers: ["Name", "Postmark", "Resend"],
    rows: [
      ["DKIM", "DKIM enforced", "DKIM enforced"],
      ["SPF", "SPF optional", "SPF enforced"],
      ["DMARC", "DMARC recommended", "DMARC recommended"],
    ]
  }}
/>

**Servers**

When you log into Postmark you are placed in the _Servers_ page. These _Servers_ let you separate your transactional, broadcasts, and inbound message streams.

When you log in to Resend, you are taken straight to the _Emails_ page. This page allows you to see all emails sent without needing to set up a server first.

<img
  src="/static/product-pages/screenshot-emails.png"
  alt="Resend Emails page"
  className="extraWidth"
/>

**Message Streams**

Postmark separates email traffic through _Message Streams_, meaning transactional and broadcast traffic never intersect.

Resend does not have this concept, and all emails are sent through the same stream.

Once you send emails, you can view their aggregated statuses in the _Metrics_ page.

<img
  src="/static/product-pages/screenshot-metrics.png"
  alt="Resend Metrics page"
  className="extraWidth"
/>

## Official SDKs

Both Postmark and Resend provide official SDKs for various programming languages, making it easy to integrate into your application.

**Key differences**

- Postmark does not have official SDKs for Python, Go, or Rust ([see docs](https://postmarkapp.com/developer/integration/official-libraries)).

<Table
  data={{
    headers: ["Platform", "Postmark", "Resend"],
    rows: [
      [
        "Node.js",
        {
          href: "https://github.com/activecampaign/postmark.js",
          text: "postmark.js"
        },
        { href: "https://github.com/resend/resend-node", text: "resend-node" }
      ],
      [
        "PHP",
        {
          href: "https://github.com/activecampaign/postmark-php",
          text: "postmark-php"
        },
        { href: "https://github.com/resend/resend-php", text: "resend-php" }
      ],
      [
        "Python",
        "-",
        {
          href: "https://github.com/resend/resend-python",
          text: "resend-python"
        }
      ],
      [
        "Ruby",
        {
          href: "https://github.com/activecampaign/postmark-gem",
          text: "postmark-gem"
        },
        { href: "https://github.com/resend/resend-ruby", text: "resend-ruby" }
      ],
      [
        "Go",
        "-",
        { href: "https://github.com/resend/resend-go", text: "resend-go" }
      ],
      [
        "Rust",
        "-",
        { href: "https://github.com/resend/resend-rust", text: "resend-rust" }
      ],
      [
        "Java",
        {
          href: "https://github.com/activecampaign/postmark-java",
          text: "postmark-java"
        },
        { href: "https://github.com/resend/resend-java", text: "resend-java" }
      ],
      [
        ".NET",
        {
          href: "https://github.com/activecampaign/postmark-dotnet",
          text: "postmark-dotnet"
        },
        { href: "https://github.com/resend/resend-dotnet", text: "resend-dotnet" }
      ]
    ]
  }}
/>

## Send email via API

Both Postmark and Resend provide a REST API and SDKs for sending emails programmatically.

**Key differences**

_Authentication_

- Postmark uses a custom header (`X-Postmark-Server-Token`) to authenticate ([see docs](https://postmarkapp.com/developer/api/overview#authentication)).
- Resend uses a standard header (`Authorization: Bearer`) to authenticate requests ([see docs](https://resend.com/docs/api-reference/introduction#authentication)).

_Rate limiting_

- Postmark throws an error when rate limit is exceeded, but doesn't provide more details ([see docs](https://postmarkapp.com/developer/api/overview#response-codes)).
- Resend has response headers describing your current rate limit following every request in conformance with the IETF standard ([see docs](https://resend.com/docs/api-reference/introduction#rate-limit)).

_Logging_

- Postmark's API only returns an HTTP response. There are no logs of API requests.
- Resend stores a history of API request logs, allowing for easy debugging of API responses and error codes ([see docs](https://resend.com/docs/api-reference/introduction#response-codes)).

_Postmark_

<CodeTabs>
```nodejs
const postmark = require('postmark');

var serverToken = "server-token";
var client = new postmark.ServerClient(serverToken);

client.sendEmail({
    "From": "sender@example.com",
    "To": "receiver@example.com",
    "Subject": "hello world",
    "TextBody": "<p>it works!</p>"
});
```

```ruby
require "postmark"

client = Postmark::ApiClient.new('server-token')

client.deliver(
  from: 'sender@example.com',
  to: 'receiver@example.com',
  subject: 'hello world',
  html_body: '<p>it works!</p>',
  track_opens: true
)
```

```php
$client = new PostmarkClient("server-token");

$sendResult = $client->sendEmail(
  "sender@example.com",
  "receiver@example.com",
  "hello world",
  "<p>it works!</p>");
```

```python
# No official Postmark SDK for Python
```

```go
// No official Postmark SDK for Go
```

```rust
// No official Postmark SDK for Rust
```

```java
Message message = new Message("sender@example.com", "receiver@example.com", "hello world", "<p>it works!</p>");

ApiClient client = Postmark.getApiClient("server-token");
MessageResponse response = client.deliverMessage(message);
```

```dotnet
using PostmarkDotNet;

var message = new PostmarkMessage()
  {
    To = "sender@example.com",
    From = "recipient@example.com",
    TrackOpens = true,
    Subject = "A complex email",
    TextBody = "Plain Text Body",
    HtmlBody = "HTML goes here",
    Tag = "New Year's Email Campaign",
    Headers = new HeaderCollection{
      {"X-CUSTOM-HEADER", "Header content"}
    }
  };

var client = new PostmarkClient("POSTMARK-SERVER-API-TOKEN-HERE");
var sendResult = await client.SendMessageAsync(message);
```
</CodeTabs>

_Resend_

<CodeTabs>
```nodejs
import { Resend } from 'resend';

const resend = new Resend('re_xxxxxxxxx');

await resend.emails.send({
  from: 'Acme <onboarding@resend.dev>',
  to: ['delivered@resend.dev'],
  subject: 'hello world',
  html: '<p>it works!</p>',
});
```

```ruby
require "resend"

Resend.api_key = "re_xxxxxxxxx"

params = {
  "from": "Acme <onboarding@resend.dev>",
  "to": ["delivered@resend.dev"],
  "subject": "hello world",
  "html": "<p>it works!</p>"
}

sent = Resend::Emails.send(params)
puts sent
```

```php
$resend = Resend::client('re_xxxxxxxxx');

$resend->emails->send([
  'from' => 'Acme <onboarding@resend.dev>',
  'to' => ['delivered@resend.dev'],
  'subject' => 'hello world',
  'html' => '<p>it works!</p>'
]);
```

```python
import resend

resend.api_key = "re_xxxxxxxxx"

params: resend.Emails.SendParams = {
  "from": "Acme <onboarding@resend.dev>",
  "to": ["delivered@resend.dev"],
  "subject": "hello world",
  "html": "<p>it works!</p>"
}

email = resend.Emails.send(params)
print(email)
```

```go
import (
	"fmt"

	"github.com/resend/resend-go/v2"
)

func main() {
  ctx := context.TODO()
  client := resend.NewClient("re_xxxxxxxxx")

  params := &resend.SendEmailRequest{
      From:        "Acme <onboarding@resend.dev>",
      To:          []string{"delivered@resend.dev"},
      Subject:     "hello world",
      Html:        "<p>it works!</p>"
  }

  sent, err := client.Emails.SendWithContext(ctx, params)

  if err != nil {
    panic(err)
  }
  fmt.Println(sent.Id)
}
```

```rust
use resend_rs::types::{CreateEmailBaseOptions};
use resend_rs::{Resend, Result};

#[tokio::main]
async fn main() -> Result<()> {
  let resend = Resend::new("re_xxxxxxxxx");

  let from = "Acme <onboarding@resend.dev>";
  let to = ["delivered@resend.dev"];
  let subject = "hello world";
  let html = "<p>it works!</p>";

  let email = CreateEmailBaseOptions::new(from, to, subject)
    .with_html(html);

  let _email = resend.emails.send(email).await?;

  Ok(())
}
```

```java
import com.resend.*;

public class Main {
    public static void main(String[] args) {
        Resend resend = new Resend("re_xxxxxxxxx");

        CreateEmailOptions params = CreateEmailOptions.builder()
                .from("Acme <onboarding@resend.dev>")
                .to("delivered@resend.dev")
                .subject("hello world")
                .html("<p>it works!</p>")
                .build();

        CreateEmailResponse data = resend.emails().send(params);
    }
}
```

```dotnet
using Resend;

IResend resend = ResendClient.Create( "re_xxxxxxxxx" );

var resp = await resend.EmailSendAsync( new EmailMessage()
{
    From = "Acme <onboarding@resend.dev>",
    To = "delivered@resend.dev",
    Subject = "hello world",
    HtmlBody = "<p>it works!</p>",
} );
Console.WriteLine( "Email Id={0}", resp.Content );
```

```curl
curl -X POST 'https://api.resend.com/emails' \\
     -H 'Authorization: Bearer re_xxxxxxxxx' \\
     -H 'Content-Type: application/json' \\
     -d $'{
    "from": "Acme <onboarding@resend.dev>",
    "to": ["delivered@resend.dev"],
    "subject": "hello world",
    "html": "<p>it works!</p>"
}'
```
</CodeTabs>

## Send email via SMTP

Both Postmark and Resend support sending emails via SMTP.

SMTP stands for Simple Mail Transfer Protocol. It is a text-based protocol in which one server communicates with another to send an email.

**Key differences**

_Authentication_

- Postmark uses the API Token for both Username and Password ([see docs](https://postmarkapp.com/developer/user-guide/send-email-with-smtp)).
- Resend uses a standard value for the Username and the API Key as the Password ([see docs](https://resend.com/docs/send-with-smtp)).

_Configurations_

- Postmark does not support port 465 ([see docs](https://postmarkapp.com/developer/user-guide/send-email-with-smtp)).
- Postmark supports adding custom metadata via headers ([see docs](https://postmarkapp.com/developer/user-guide/send-email-with-smtp)).

<Table
  data={{
    headers: ["Configuration", "Postmark", "Resend"],
    rows: [
      [
        "Host",
        "`smtp-broadcasts.postmarkapp.com` or `smtp.postmarkapp.com`",
        "`smtp.resend.com`"
      ],
      ["Port", "25, 587, or 2525", "25, 465, 587, 2465, or 2587"],
      [
        "Username",
        "Postmark Server API Token or Access Key",
        "The '`resend`' string"
      ],
      ["Password", "Postmark Server API Token or Secret Key", "Resend API key"],
      [
        "Authentication",
        "Plain text (unencrypted), CRAM-MD5, TLS",
        "SMTPS or STARTTLS"
      ]
    ]
  }}
/>

## Webhooks

Both Postmark and Resend provide webhooks to notify your application of email events.

**Key differences**

- Postmark does not have support for email sent and delivery delay events ([see docs](https://postmarkapp.com/developer/webhooks/webhooks-overview)).
- Resend has logs of webhook requests, allowing you to see the status of your webhook responses ([see docs](https://resend.com/docs/dashboard/webhooks/introduction#3-test-that-your-webhook-endpoint-is-working-properl)).

<Table
  data={{
    headers: ["Event", "Postmark", "Resend"],
    rows: [
      [
        "Inbound",
        {
          href: "https://postmarkapp.com/developer/webhooks/inbound-webhook",
          text: "Inbound"
        },
        {
          href: "https://resend.com/docs/dashboard/receiving/introduction",
          text: "Inbound"
        }
      ],
      [
        "Send",
        "-",
        {
          href: "https://resend.com/docs/dashboard/webhooks/event-types#email-sent",
          text: "email.sent"
        }
      ],
      [
        "Delivery",
        {
          href: "https://postmarkapp.com/developer/webhooks/delivery-webhook",
          text: "Delivery"
        },
        {
          href: "https://resend.com/docs/dashboard/webhooks/event-types#email-delivered",
          text: "email.delivered"
        }
      ],
      [
        "Delivery Delayed",
        "-",
        {
          href: "https://resend.com/docs/dashboard/webhooks/event-types#email-delivery-delayed",
          text: "email.delivery_delayed"
        }
      ],
      [
        "Bounces",
        {
          href: "https://postmarkapp.com/developer/webhooks/bounce-webhook",
          text: "Bounce"
        },
        {
          href: "https://resend.com/docs/dashboard/webhooks/event-types#email-bounced",
          text: "email.bounced"
        }
      ],
      [
        "Complaints",
        {
          href: "https://postmarkapp.com/developer/webhooks/spam-complaint-webhook",
          text: "Spam Complaint"
        },
        {
          href: "https://resend.com/docs/dashboard/webhooks/event-types#email-complained",
          text: "email.complained"
        }
      ],
      [
        "Open Tracking",
        {
          href: "https://postmarkapp.com/developer/webhooks/open-tracking-webhook",
          text: "Open"
        },
        {
          href: "https://resend.com/docs/dashboard/webhooks/event-types#email-opened",
          text: "email.opened"
        }
      ],
      [
        "Click Tracking",
        {
          href: "https://postmarkapp.com/developer/webhooks/click-webhook",
          text: "Click"
        },
        {
          href: "https://resend.com/docs/dashboard/webhooks/event-types#email-clicked",
          text: "email.clicked"
        }
      ],
      [
        "Unsubscribe",
        {
          href: "https://postmarkapp.com/developer/webhooks/subscription-change-webhook",
          text: "Subscription Change"
        },
        {
          href: "https://resend.com/docs/dashboard/webhooks/event-types#contact-updated",
          text: "contact.updated"
        }
      ]
    ]
  }}
/>

## Security & Privacy

Postmark and Resend have robust security features, but some major differences exist.

**Key differences**

- Postmark is not SOC 2 compliant, while Resend is ([see more](https://resend.com/security/soc-2)).
- Postmark does not enforce SPF, while Resend does ([see docs](https://resend.com/docs/dashboard/domains/introduction#what-are-spf-records)).

<Table
  data={{
    headers: ["Name", "Postmark", "Resend"],
    rows: [
      ["Authentication", "Email/Password", "Email/Password, Google, GitHub"],
      ["Multi-Factor Auth", "MFA available", "MFA available"],
      [
        "GDPR",
        {
          href: "https://postmarkapp.com/eu-privacy#gdpr",
          text: "GDPR compliant"
        },
        {
          href: "https://resend.com/security/gdpr",
          text: "GDPR compliant"
        }
      ],
      [
        "SOC 2",
        "-",
        {
          href: "https://resend.com/security/soc-2",
          text: "SOC 2 compliant"
        }
      ]
    ]
  }}
/>

## Idempotency Keys

Resend supports idempotency keys on the `POST /emails` and `POST /emails/batch` endpoints.

> Postmark does not currently support idempotency keys.

An idempotent operation is an action you can perform more than once, with the same input, and it always produces the same outcome and avoids repeating side effects.

By adding an `Idempotency-Key` header, or using the equivalent field on our SDKs, you can tell Resend that this specific email should only be sent once, even if we get more than one request from you about it.

`POST /emails`

<CodeTabs>

```nodejs
await resend.emails.send(
  {
    from: 'Acme <onboarding@resend.dev>',
    to: ['delivered@resend.dev'],
    subject: 'hello world',
    html: '<p>it works!</p>',
  },
  {
    idempotencyKey: 'welcome-user/123456789',
  },
);
```

```php
$resend = Resend::client('re_xxxxxxxxx');

$resend->emails->send([
  'from' => 'Acme <onboarding@resend.dev>',
  'to' => ['delivered@resend.dev'],
  'subject' => 'hello world',
  'html' => '<p>it works!</p>',
], [
  'idempotency_key' => 'welcome-user/123456789',
]);
```

```python
params: resend.Emails.SendParams = {
  "from": "Acme <onboarding@resend.dev>",
  "to": ["delivered@resend.dev"],
  "subject": "hello world",
  "html": "<p>it works!</p>"
}

options: resend.Emails.SendOptions = {
  "idempotency_key": "welcome-user/123456789",
}

resend.Emails.send(params, options)
```

```ruby
params = {
  "from": "Acme <onboarding@resend.dev>",
  "to": ["delivered@resend.dev"],
  "subject": "hello world",
  "html": "<p>it works!</p>"
}
Resend::Emails.send(
  params,
  options: { idempotency_key: "welcome-user/123456789" }
)
```

```go
ctx := context.TODO()
params := &resend.SendEmailRequest{
  From:    "onboarding@resend.dev",
  To:      []string{"delivered@resend.dev"},
  Subject: "hello world",
  Text:    "it works!",
}
options := &resend.SendEmailOptions{
  IdempotencyKey: "welcome-user/123456789",
}
_, err := client.Emails.SendWithOptions(ctx, params, options)
if err != nil {
  panic(err)
}
```

```rust
use resend_rs::types::CreateEmailBaseOptions;
use resend_rs::{Resend, Result};

#[tokio::main]
async fn main() -> Result<()> {
  let resend = Resend::new("re_xxxxxxxxx");

  let from = "Acme <onboarding@resend.dev>";
  let to = ["delivered@resend.dev"];
  let subject = "Hello World";

  let email = CreateEmailBaseOptions::new(from, to, subject)
    .with_html("<p>it works!</p>")
    .with_idempotency_key("welcome-user/123456789");

  let _email = resend.emails.send(email).await?;

  Ok(())
}
```

```java
CreateEmailOptions params = CreateEmailOptions.builder()
  .from("Acme <onboarding@resend.dev>")
  .to("delivered@resend.dev")
  .subject("hello world")
  .html("<p>it works!</p>")
  .build();


RequestOptions options = RequestOptions.builder()
  .setIdempotencyKey("welcome-user/123456789").build();

CreateEmailResponse data = resend.emails().send(params, options);
```

```dotnet
using Resend;

IResend resend = ResendClient.Create( "re_xxxxxxxxx" );

var key = IdempotencyKey.New<int>( "welcome-user", 123456789 );
var resp = await resend.EmailSendAsync(key, new EmailMessage()
{
    From = "Acme <onboarding@resend.dev>",
    To = "delivered@resend.dev",
    Subject = "hello world",
    HtmlBody = "<p>it works!</p>",
} );
Console.WriteLine( "Email Id={0}", resp.Content );
```

```curl
curl -X POST 'https://api.resend.com/emails' \
     -H 'Authorization: Bearer re_xxxxxxxxx' \
     -H 'Content-Type: application/json' \
     -H 'Idempotency-Key: welcome-user/123456789' \
     -d $'{
  "from": "Acme <onboarding@resend.dev>",
  "to": ["delivered@resend.dev"],
  "subject": "hello world",
  "html": "<p>it works!</p>"
}'
```

</CodeTabs>

`POST /emails/batch`

<CodeTabs codeHeight={450}>

```nodejs
import { Resend } from 'resend';

const resend = new Resend('re_xxxxxxxxx');

await resend.batch.send(
  [
    {
      from: 'Acme <onboarding@resend.dev>',
      to: ['foo@gmail.com'],
      subject: 'hello world',
      html: '<h1>it works!</h1>',
    },
    {
      from: 'Acme <onboarding@resend.dev>',
      to: ['bar@outlook.com'],
      subject: 'world hello',
      html: '<p>it works!</p>',
    },
  ],
  {
    idempotencyKey: 'team-quota/123456789',
  },
);
```

```php
$resend = Resend::client('re_xxxxxxxxx');

$resend->batch->send(
  [
    [
      'from' => 'Acme <onboarding@resend.dev>',
      'to' => ['foo@gmail.com'],
      'subject' => 'hello world',
      'html' => '<h1>it works!</h1>',
    ],
    [
      'from' => 'Acme <onboarding@resend.dev>',
      'to' => ['bar@outlook.com'],
      'subject' => 'world hello',
      'html' => '<p>it works!</p>',
    ]
  ],
  [
    'idempotency_key' => 'team-quota/123456789',
  ]
);
```

```python
import resend
from typing import List

resend.api_key = "re_xxxxxxxxx"

params: List[resend.Emails.SendParams] = [
  {
    "from": "Acme <onboarding@resend.dev>",
    "to": ["foo@gmail.com"],
    "subject": "hello world",
    "html": "<h1>it works!</h1>",
  },
  {
    "from": "Acme <onboarding@resend.dev>",
    "to": ["bar@outlook.com"],
    "subject": "world hello",
    "html": "<p>it works!</p>",
  }
]

options: resend.Batch.SendOptions = {
  "idempotency_key": "team-quota/123456789",
}

resend.Batch.send(params, options)
```

```ruby
require "resend"

Resend.api_key = 're_xxxxxxxxx'

params = [
  {
    "from": "Acme <onboarding@resend.dev>",
    "to": ["foo@gmail.com"],
    "subject": "hello world",
    "html": "<h1>it works!</h1>",
  },
  {
    "from": "Acme <onboarding@resend.dev>",
    "to": ["bar@outlook.com"],
    "subject": "world hello",
    "html": "<p>it works!</p>",
  }
]

Resend::Batch.send(
  params,
  options: { idempotency_key: "team-quota/123456789" }
)
```

```go
package examples

import (
	"fmt"
	"os"

	"github.com/resend/resend-go/v2"
)

func main() {

  ctx := context.TODO()

  client := resend.NewClient("re_xxxxxxxxx")

  var batchEmails = []*resend.SendEmailRequest{
    {
      From:    "Acme <onboarding@resend.dev>",
      To:      []string{"foo@gmail.com"},
      Subject: "hello world",
      Html:    "<h1>it works!</h1>",
    },
    {
      From:    "Acme <onboarding@resend.dev>",
      To:      []string{"bar@outlook.com"},
      Subject: "world hello",
      Html:    "<p>it works!</p>",
    },
  }

  options := &resend.BatchSendEmailOptions{
    IdempotencyKey: "team-quota/123456789",
  }

  sent, err := client.Batch.SendWithOptions(ctx, batchEmails, options)

  if err != nil {
    panic(err)
  }
  fmt.Println(sent.Data)
}
```

```rust
use resend_rs::types::CreateEmailBaseOptions;
use resend_rs::{Resend, Result};

#[tokio::main]
async fn main() -> Result<()> {
  let resend = Resend::new("re_xxxxxxxxx");

  let emails = vec![
    CreateEmailBaseOptions::new(
      "Acme <onboarding@resend.dev>",
      vec!["foo@gmail.com"],
      "hello world",
    )
    .with_html("<h1>it works!</h1>"),
    CreateEmailBaseOptions::new(
      "Acme <onboarding@resend.dev>",
      vec!["bar@outlook.com"],
      "world hello",
    )
    .with_html("<p>it works!</p>"),
  ];

  let _emails = resend.batch.send_with_idempotency_key(emails, "team-quota/123456789").await?;

  Ok(())
}
```

```java
import com.resend.*;

public class Main {
    public static void main(String[] args) {
        Resend resend = new Resend("re_xxxxxxxxx");

        CreateEmailOptions firstEmail = CreateEmailOptions.builder()
            .from("Acme <onboarding@resend.dev>")
            .to("foo@gmail.com")
            .subject("hello world")
            .html("<h1>it works!</h1>")
            .build();

        CreateEmailOptions secondEmail = CreateEmailOptions.builder()
            .from("Acme <onboarding@resend.dev>")
            .to("bar@outlook.com")
            .subject("world hello")
            .html("<p>it works!</p>")
            .build();

        CreateBatchEmailsResponse data = resend.batch().send(
            Arrays.asList(firstEmail, secondEmail),
            Map.of("idempotency_key", "team-quota/123456789")
        );
    }
}
```

```dotnet
using Resend;

IResend resend = ResendClient.Create( "re_xxxxxxxxx" ); // Or from DI

var key = IdempotencyKey.New<int>( "team-quota", 123456789 );

var mail1 = new EmailMessage()
{
    From = "Acme <onboarding@resend.dev>",
    To = "foo@gmail.com",
    Subject = "hello world",
    HtmlBody = "<p>it works!</p>",
};

var mail2 = new EmailMessage()
{
    From = "Acme <onboarding@resend.dev>",
    To = "bar@outlook.com",
    Subject = "hello world",
    HtmlBody = "<p>it works!</p>",
};

var resp = await resend.EmailBatchAsync(key, [ mail1, mail2 ] );
Console.WriteLine( "Nr Emails={0}", resp.Content.Count );
```

```curl
curl -X POST 'https://api.resend.com/emails/batch' \
     -H 'Authorization: Bearer re_xxxxxxxxx' \
     -H 'Content-Type: application/json' \
     -H 'Idempotency-Key: team-quota/123456789' \
     -d $'[
  {
    "from": "Acme <onboarding@resend.dev>",
    "to": ["foo@gmail.com"],
    "subject": "hello world",
    "html": "<h1>it works!</h1>"
  },
  {
    "from": "Acme <onboarding@resend.dev>",
    "to": ["bar@outlook.com"],
    "subject": "world hello",
    "html": "<p>it works!</p>"
  }
]'
```

</CodeTabs>

Adding idempotency keys not only helps you avoid sending duplicate emails and using up your quota, but also enables building more robust and scalable systems.

<LinkCard
  title="Idempotency Keys"
  description="Learn how to send an idempotent email using Resend."
  url="https://resend.com/docs/dashboard/emails/idempotency-keys"
  image="https://cdn.resend.com/posts/idempotency-keys.jpg"
/>

## Additional Features

Outside of core email sending, Resend offers additional features that Postmark does not have that may be helpful for you.

**No-code editor**

The no-code editor makes it easy for anyone to write, format, and send broadcast emails.

<LinkCard
  title="Send Marketing Emails with Resend Broadcasts"
  description="Enabling anyone to send email campaigns without code."
  url="/blog/send-marketing-emails-with-resend-broadcasts"
  image="https://cdn.resend.com/posts/send-marketing-emails-with-resend-broadcasts.jpg"
/>

**Schedule email API**

Use the new Resend API to schedule emails without using external services.

<LinkCard
  title="Schedule email API"
  description="Send emails at a specific time without additional complexity."
  url="/blog/introducing-the-schedule-email-api"
  image="https://cdn.resend.com/posts/introducing-the-schedule-email-api.jpg"
/>

**Deliverability Insights**

Improve your chances of landing in the inbox instead of the spam folder with detailed recommendations.

<LinkCard
  title="Deliverability Insights"
  description="Improve email deliverability by identifying issues and applying best practices."
  url="/blog/deliverability-insights"
  image="https://cdn.resend.com/posts/deliverability-insights.jpg"
/>

**Multi-Region**

Improve your email deliverability speed by using a region nearest to your users.

<LinkCard
  title="Faster Email Delivery with Multi-Region"
  description="Faster deliverability with reduced latency."
  url="/blog/multi-region"
  image="https://cdn.resend.com/posts/multi-region.jpg"
/>

## Pricing

Postmark and Resend offer competitive pricing based on the number of emails sent.

**Key differences**

- Postmark charges $50 for each Dedicated IP, while Resend charges $30 ([see more](https://resend.com/pricing)).
- Postmark provides 45 days of data retention, while Resend provides 30 days ([see more](https://resend.com/pricing)).

<Table
  data={{
    headers: ["Emails", "Postmark", "Resend"],
    rows: [
      ["3,000", "$15", "$0"],
      ["50,000", "$55", "$20"],
      ["100,000", "$115", "$35"],
      ["200,000", "$245", "$160"],
      ["500,000", "$455", "$350"],
      ["1,000,000", "$775", "$650"],
    ]
  }}
/>

## Conclusion

Ready to migrate to Resend? Press `S` to get started. If there's anything else we can help with, contact our team, and we'll answer any questions you have.