Sending Email With the Mailchimp API in C#



Last updated: November 6th, 2026

Updated November 2026: added how to send an individual email through the API with Mailchimp Transactional, how to create and send a campaign with the Marketing API, a dependency-free HttpClient example for adding or updating a contact, and fixed the original MailChimp.Net code (missing generic types, MoreLINQ replaced with Chunk). Plan requirements and endpoints are as of November 2026.

Quick Answer: Two Different Mailchimp APIs

  • To send one email to one person from your code (order confirmations, password resets, receipts), use Mailchimp Transactional (formerly Mandrill), a paid add-on: POST https://mandrillapp.com/api/1.4/messages/send.
  • To keep a marketing audience in sync so your marketing team can send campaigns and automations, use the Mailchimp Marketing API (https://<dc>.api.mailchimp.com/3.0/). It sends campaigns to an audience or segment (shown below), not an arbitrary one-off email to a single address.

Send an Email With the Mailchimp Transactional API in C#

As of November 2026, Mailchimp Transactional is an add-on for Mailchimp Standard or Premium plans, billed in monthly blocks of emails. New accounts get a one-time demo of 500 emails to addresses on their own verified domain, and a test API key lets you call the API without sending anything (Mailchimp Transactional fundamentals).

  1. In Mailchimp, enable Transactional from the billing page, then open the Transactional dashboard.
  2. Add and verify your sending domain (DKIM and SPF DNS records). Unverified domains are rejected.
  3. Create an API key (Settings > API Keys) and store it in an environment variable or user secrets.
  4. Send with plain HttpClient. Every Transactional call is a POST with the key in the JSON body:
using System.Net.Http.Json;
using System.Text.Json;

// Mailchimp Transactional (formerly Mandrill): send one email through the API.
string apiKey = Environment.GetEnvironmentVariable("MAILCHIMP_TRANSACTIONAL_KEY")!;

using var http = new HttpClient { BaseAddress = new Uri("https://mandrillapp.com/api/1.4/") };

var payload = new
{
    key = apiKey,
    message = new
    {
        from_email = "orders@example.com",   // must be on a domain verified in Mailchimp Transactional
        from_name = "Example Store",
        subject = "Your order #1042 has shipped",
        html = "<p>Good news: your order is on its way.</p>",
        text = "Good news: your order is on its way.",
        to = new[] { new { email = "customer@example.net", name = "Pat Customer", type = "to" } },
        tags = new[] { "order-shipped" }
    }
};

using HttpResponseMessage response = await http.PostAsJsonAsync("messages/send", payload);
string json = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();

// One result per recipient: status is "sent", "queued", "scheduled", "rejected" or "invalid".
foreach (JsonElement result in JsonDocument.Parse(json).RootElement.EnumerateArray())
{
    Console.WriteLine($"{result.GetProperty("email").GetString()}: {result.GetProperty("status").GetString()}");
}

A rejected status includes a reject_reason (for example hard-bounce or unsigned when the domain isn't verified). For templates built in Mailchimp, call messages/send-template with the template name and merge variables instead of raw HTML.

Send a Campaign With the Marketing API in C#

If "send an email" means a newsletter or announcement to your audience, that is a campaign. It takes three calls: create the campaign, set its content, then send it. The Marketing API uses HTTP Basic auth with any user name and your API key as the password; the data center (such as us21) is the part of the key after the dash:

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;

// Mailchimp Marketing API: create, fill and send a regular campaign to an audience.
string apiKey = Environment.GetEnvironmentVariable("MAILCHIMP_API_KEY")!;   // e.g. "abc123...-us21"
string listId = Environment.GetEnvironmentVariable("MAILCHIMP_LIST_ID")!;
string dataCenter = apiKey[(apiKey.LastIndexOf('-') + 1)..];

using var http = new HttpClient { BaseAddress = new Uri($"https://{dataCenter}.api.mailchimp.com/3.0/") };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"anystring:{apiKey}")));

// 1) Create the campaign
using HttpResponseMessage created = await http.PostAsJsonAsync("campaigns", new
{
    type = "regular",
    recipients = new { list_id = listId },
    settings = new
    {
        subject_line = "New this month",
        title = "Newsletter 2026-11",          // internal name in the Mailchimp UI
        from_name = "Example Store",
        reply_to = "hello@example.com"         // required before you can send
    }
});
created.EnsureSuccessStatusCode();
string campaignId = (await created.Content.ReadFromJsonAsync<JsonElement>()).GetProperty("id").GetString()!;

// 2) Set the HTML content
using HttpResponseMessage content = await http.PutAsJsonAsync($"campaigns/{campaignId}/content",
    new { html = "<h1>New this month</h1><p>Here is what changed...</p>" });
content.EnsureSuccessStatusCode();

// 3a) Send a test to yourself first...
using HttpResponseMessage test = await http.PostAsJsonAsync($"campaigns/{campaignId}/actions/test",
    new { test_emails = new[] { "me@example.com" }, send_type = "html" });
test.EnsureSuccessStatusCode();

// 3b) ...then send it to the whole audience (204 No Content on success)
// using HttpResponseMessage sent = await http.PostAsync($"campaigns/{campaignId}/actions/send", null);
// sent.EnsureSuccessStatusCode();

Mailchimp adds the required footer (your address and an unsubscribe link) to campaigns. If the send fails with a 400, the response body lists what is missing, usually reply_to or a from address on a domain you haven't verified. Field names are from the Campaigns API reference.

Add or Update a Contact With the Marketing API

The Marketing API uses HTTP Basic auth with any user name and your API key as the password. The data center (such as us21) is the suffix of the key, and members are addressed by the MD5 hash of their lowercase email. A PUT creates the contact if it doesn't exist and updates it if it does:

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;

// Mailchimp Marketing API: add or update (upsert) one contact in an audience.
string apiKey = Environment.GetEnvironmentVariable("MAILCHIMP_API_KEY")!; // e.g. "abc123...-us21"
string listId = Environment.GetEnvironmentVariable("MAILCHIMP_LIST_ID")!;
string dataCenter = apiKey[(apiKey.LastIndexOf('-') + 1)..];             // "us21"

using var http = new HttpClient { BaseAddress = new Uri($"https://{dataCenter}.api.mailchimp.com/3.0/") };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"anystring:{apiKey}")));

string email = "customer@example.net";
// Members are addressed by the MD5 hash of the lowercase email address.
string subscriberHash = Convert.ToHexString(MD5.HashData(Encoding.UTF8.GetBytes(email.ToLowerInvariant()))).ToLowerInvariant();

var member = new
{
    email_address = email,
    status_if_new = "subscribed",   // only used when the contact doesn't exist yet
    merge_fields = new { FNAME = "Pat", LNAME = "Customer" }
};

using HttpResponseMessage response = await http.PutAsJsonAsync($"lists/{listId}/members/{subscriberHash}", member);
Console.WriteLine($"{(int)response.StatusCode}: {await response.Content.ReadAsStringAsync()}");

Syncing Customers to a Mailchimp Audience (Marketing API)

Background

Despite the countless ways people can get notifications, email is still a valuable way to maintain a relationship with your users and customers. MailChimp's API allows the building of lists that can contain custom fields for personalized email messages. MailChimp also manages the IP addresses used for sending to ensure higher deliverability than may be possible for many companies.

The NuGet package I am using with MailChimp is: MailChimp.Net.V3 (community maintained, not official; version 6.x targets .NET 10 as of November 2026) and the GitHub page for it is here. To use the API, you need an API key which is obtained from MailChimp's portal.

Workflow Options

Using MailChimp's API for sending email means that you will need to synchronize your own customer data with a copy in MailChimp. MailChimp needs to know if the user is subscribed or not, as well as their email address. The other fields, such as name, address, age, and so on are all optional. You can create your own "merge fields" with the API which can be any data of type string, number, date, image URL, etc.

Creating merge fields example (MailChimp.Net.V3; I've restored the generic types that were missing from the original listing):

public async Task EnsureCreatedUserFieldsAsync()
{
    string listId = this.mailChimpConfigs.ListId;
    List<MergeField> fields = (await this.manager.MergeFields.GetAllAsync(listId)).ToList();

    await Task.WhenAll(
        CreateFieldIfNotExistsAsync(fields, listId, "First Name", "FNAME", "text"),
        CreateFieldIfNotExistsAsync(fields, listId, "Last Name", "LNAME", "text"),
        CreateFieldIfNotExistsAsync(fields, listId, "User Created", "CREATED", "date", isRequired: true),
        CreateFieldIfNotExistsAsync(fields, listId, "Email Confirmed", "CONFIRMED", "text"));
}

private async Task CreateFieldIfNotExistsAsync(
    List<MergeField> existing,
    string listId,
    string name,
    string tag,
    string type,               // "text", "number", "date", "imageurl", ...
    bool isRequired = false)
{
    if (existing.Any(x => x.Tag == tag))
    {
        return;
    }

    try
    {
        await this.manager.MergeFields.AddAsync(listId, new MergeField
        {
            ListId = listId,
            Name = name,
            Tag = tag,          // merge tags are up to 10 characters, upper case by convention
            Required = isRequired,
            Type = type
        });
    }
    catch (MailChimpException ex)
    {
        throw new InvalidOperationException($"Failed to add merge field '{tag}' to list '{listId}': {ex.Detail}", ex);
    }
}

Once you have created your merge fields, you can start populating the list with actual customer data. You can always add more merge fields later, if you need.

One option to sync your customer data is to set some type of (daily) routine where you use the Batch Operation API. You post many operations in one request and Mailchimp processes them in the background. Mailchimp limits you to 500 pending batch requests at a time; I still split large syncs into chunks of 500 operations so a failed batch is easy to retry. This is a useful option if you need to send all of your requests quickly, such as in an AWS Lambda Function.

This is a batch request method to send a list of users. Since .NET 6, Enumerable.Chunk replaces the MoreLINQ Batch helper I originally used:

// One row per user you want to push to Mailchimp.
public sealed record MailChimpSyncItem(string EmailMD5Hash, string Json, bool IsDeleted);

private async Task SyncUserDataAsync(List<MailChimpSyncItem> users)
{
    const int MaxOperationsPerBatch = 500;

    foreach (MailChimpSyncItem[] chunk in users.Chunk(MaxOperationsPerBatch))
    {
        var operations = chunk.Select(item => new Operation
        {
            // DELETE archives the contact (it stops receiving email); PUT creates or updates it.
            Method = item.IsDeleted ? HttpMethod.Delete.Method : HttpMethod.Put.Method,
            Path = $"/lists/{this.mailChimpConfigs.ListId}/members/{item.EmailMD5Hash}",
            Body = item.IsDeleted ? null : item.Json
        }).ToList();

        // Returns immediately; Mailchimp processes the batch in the background.
        await this.manager.Batches.AddAsync(new BatchRequest { Operations = operations });
    }
}

A better approach than trying to sync your entire user list every day or so is to update each record as needed. Not only is this going to allow you to communicate with your users in a more timely fashion because of the fresher data, but it will prevent sending emails to users who have opted out sooner than your sync process (because it runs only daily or so). Updating an entire user list is slow and most data is not changing frequently enough to justify this approach beyond an initial import. Therefore, if possible update each user in MailChimp when they make updates to their data. This can be done by adding a message to a queue which can be processed by your custom MailChimp service.

Sending Emails and Managing Customers

Typically, a marketing department will be using the actual features inside of the MailChimp portal. As a developer, the goal is to give them the best data to act on for any kind of conditions and formatting they come up with. By automating the ability to add new merge fields and having a process in place that will automatically update the MailChimp list, the marketing people will have everything they need to send emails to customers.

One important note on managing the customers I have noticed was related to storing the subscription. Your own customer database may have all kinds of settings for keeping track of which emails your customer want to get, if any. Each of these settings can correspond to a different MailChimp list. Also, a global setting for emails is useful. In all of these cases, remember that there is only one version of truth, that is your customer database. It is your responsibility as a developer to make sure these settings are persisted to MailChimp's list(s) as quickly as possible to ensure expected delivery.

Frequently Asked Questions

Can I send an email with the Mailchimp API?

Yes. For one email to one person, use Mailchimp Transactional (messages/send). For a newsletter to an audience, use the Marketing API: create a campaign, set its content, and call actions/send.

Can I send a single email to one address with Mailchimp?

Only with Mailchimp Transactional, which requires a Standard or Premium plan plus the Transactional add-on (a 500-email demo is available to try it). The Marketing API can send a test of a campaign to specific addresses, but that is meant for previews, not for sending real messages.

Is the Mandrill API the same as Mailchimp Transactional?

Yes. Mandrill was renamed Mailchimp Transactional, and the API still lives at mandrillapp.com/api/1.4/.

What is the Mailchimp subscriber hash?

The MD5 hash of the contact's email address in lowercase, written as lowercase hex. The Marketing API uses it in member URLs such as /lists/{list_id}/members/{subscriber_hash}.

Do I need a NuGet package for Mailchimp in C#?

No. Both APIs are simple JSON over HTTPS, so HttpClient is enough, as the examples show. Community packages such as MailChimp.Net.V3 wrap the Marketing API if you prefer typed methods.

Related: sending SMS text messages from C#.



Join my email list!



ryan
About Me

With over 15 years in tech, I've excelled as a senior software engineer, specializing in ASP.NET, C#, SQL, Azure, and front-end technologies. I've led diverse projects across various sectors, from startups to global corporations, particularly during my decade in the San Francisco Bay Area.


Sign Up Hostinger Email