Working With The MailChimp API In .NET: List Management

This is the fourth in a series of articles about using the MailChimp.NET.V3 NuGet package to interact with the MailChimp REST API.

Now that we’ve learned how to use the MailChimpManager class to interact with the MailChimp.NET REST API, let’s start exploring the objects available in the API and how they can help us manage our email marketing.

First up: Lists and, by inference, Members.

In MailChimp, a Member is basically a person; and that person’s primary attribute is an email address.

Each Member can belong to one or more Lists. The purpose of a List is to gather together Members by topic.

For example, if you own a bicycle and ski shop, it would make sense to have a List of bicycling customers, and another List of skiing customers. If someone likes both activities, he would be a Member of both Lists. But if he only cared for skiing, he would be in the skiing List, but not the bicycling List.

MailChimp also has Segments, which are basically sub-groupings of a List’s Members.

For example, it is possible to identify List Members who consistently click links in your Campaigns. You might want to Segment these users into a group — either saved or temporarily — for a special email promotion, because you know they have a strong likelihood of following through.

Segmenting is a powerful tool and, as a result, it can easily be misused. Even within my company’s relatively sophisticated marketing operation, we have used Segments where new Lists would have been the better choice.

Therefore, I may revisit Segments at some future date, but other than this aside, we won’t be talking about Segments again during this tutorial.

Create a List

I strongly discourage you from creating Lists via the MailChimp REST API.

Not for technical reasons — creating a List is easy and effective — but for practical reasons.

MailChimp notes that among the most common causes of spam complaints are people getting emails they didn’t expressly agree to receive, or that they weren’t expecting, even if they agreed to get them.

Creating Lists dynamically is an open invitation to create a lot of stale lists, in my opinion. And it’s also likely you’ll be adding members who didn’t overtly agree to join that List.

In other words, Lists created via the API are probably trouble. Think twice before making them.

That said, I’ll still show you how to do it, but don’t blame me if your MailChimp account is banned because you went crazy mailing to ill-advised Lists made via the API.

To create a List, we first instantiate a List object that sets at least the minimum variables required, namely:

  • A display name;
  • a Contact, so that people know who sent mail to the List;
  • a string that is added to each email sent to the List, reminding people how they got to be in that List;
  • default settings for new Campaigns, including the email address that sent the message and a subject line; and
  • a Boolean indicating whether to send HTML email; false means to send only plain-text emails.
var list = new List
{
	Name = "API Created List",
	Contact = new Contact
	{
		Company = "Foo Inc.",
		Address1 = "123 Main St.",
		City = "Anytown",
		State = "ME",
		Zip = "04000",
		Country = "US"
	},
	PermissionReminder = "You were added to this list as part of an automated process that also created this list.",
	CampaignDefaults = new CampaignDefaults
	{
		FromEmail = "me@myserver.com",
		FromName = "John Doe",
		Subject = "Email message from dynamically created List",
		Language = "en-us"
	},
	EmailTypeOption = true
};

With the List defaults set, we can next call the AddOrUpdateAsync method to actually create the List. When we call that method, the API should return the created List object — which will basically be the List we sent to it, but with an Id assigned to it.

Since we’re using an ASP.NET MVC project to demo this, I’ll have an ActionResult return a View that takes, as its model, the List returned by the API.

Update, 14 August 2016: I’ve restructured the code in this post to use a specific controller, as part of adding additional tutorials to later posts.
using System.Web.Mvc;
using MailChimp.Net;
using System.Threading.Tasks;
using MailChimp.Net.Models;
using MailChimp.Net.Core;
using System.Net;
using System;

namespace MailChimpNetDemo.Controllers
{
    public class MailChimpListController : Controller
    {
        private static MailChimpManager Manager = new MailChimpManager();

        public async Task<ActionResult> Create()
        {
            var list = new List
            {
                Name = "API Created List",
                Contact = new Contact
                {
                    Company = "Foo Inc.",
                    Address1 = "123 Main St.",
                    City = "Anytown",
                    State = "ME",
                    Zip = "04000",
                    Country = "US"
                },
                PermissionReminder = "You were added to this list as part of an automated process that also created this list.",
                CampaignDefaults = new CampaignDefaults
                {
                    FromEmail = "me@myserver.com",
                    FromName = "John Doe",
                    Subject = "Email message from dynamically created List",
                    Language = "en/us"
                },
                EmailTypeOption = true
            };

            try
            {
                var model = await Manager.Lists.AddOrUpdateAsync(list);
                return View(model);
            }
            catch(MailChimpException)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
            }
            catch(Exception)
            {
                return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
            }
        }
    }
}
A quick note on error handling: As you see, I will throw an HTTP status code of 504 if I get an error back from the API or 503 if I get errors back from the MailChimp.NET.V3 library itself. I’m doing this for simplicity; you can institute any error handling you like.

I doubt you will see any errors coming from the MailChimp.NET.V3 library itself. However, it can raise a number of different kinds of errors when it encounters internal difficulties, so you should be prepared to catch them.

Far more likely is a MailChimpException, which is thrown any time MailChimp.NET.V3 encounters a problem with the MailChimp API, be it lack of response, refusal to connect, transmitting jibberish. … Whenever the MailChimp API does something other than return what was expected, a MailChimpException is raised.

The View is just a Detail of the List model:

@model MailChimp.Net.Models.List
@{
    ViewBag.Title = "Created List";
}

<h2>Created List</h2>

<table class="table table-striped">
    <thead>
        <tr>
            <th>Property</th>
            <th>Value</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>Id</td>
            <td>@Model.Id</td>
        </tr>
        <tr>
            <td>Name</td>
            <td>@Model.Name</td>
        </tr>
        <tr>
            <td>Contact.Company</td>
            <td>@Model.Contact.Company</td>
        </tr>
        <tr>
            <td>Contact.Address1</td>
            <td>@Model.Contact.Address1</td>
        </tr>
        <tr>
            <td>Contact.City</td>
            <td>@Model.Contact.City</td>
        </tr>
        <tr>
            <td>Contact.State</td>
            <td>@Model.Contact.State</td>
        </tr>
        <tr>
            <td>Contact.Zip</td>
            <td>@Model.Contact.Zip</td>
        </tr>
        <tr>
            <td>PermissionReminder</td>
            <td>@Model.PermissionReminder</td>
        </tr>
        <tr>
            <td>CampaignDefaults.FromEmail</td>
            <td>@Model.CampaignDefaults.FromEmail</td>
        </tr>
        <tr>
            <td>CampaignDefaults.FromName</td>
            <td>@Model.CampaignDefaults.FromName</td>
        </tr>
        <tr>
            <td>CampaignDefaults.Subject</td>
            <td>@Model.CampaignDefaults.Subject</td>
        </tr>
        <tr>
            <td>CampaignDefaults.Language</td>
            <td>@Model.CampaignDefaults.Language</td>
        </tr>
        <tr>
            <td>EmailTypeOption</td>
            <td>@Model.EmailTypeOption</td>
        </tr>
    </tbody>
</table>

When I run my code, the View shows me the List my code created, and a look at the MailChimp Dashboard also shows this list.

This slideshow requires JavaScript.

Update a List

Updating a list is exactly the same as creating a List, except we need to provide the Id of the List we want to update:

public async Task<ActionResult> Update()
{
	var list = new List
	{
		Id = "f809a0eba9",
		Name = "API Created List - EDITED",
		Contact = new Contact
		{
			Company = "FooBar Inc.",
			Address1 = "123 Main St.",
			City = "Anytown",
			State = "ME",
			Zip = "04000",
			Country = "US"
		},
		PermissionReminder = "You were added to this list as part of an automated process that also created this list.",
		CampaignDefaults = new CampaignDefaults
		{
			FromEmail = "you@yourserver.com",
			FromName = "Jane Smith",
			Subject = "Email message from dynamically created List",
			Language = "en-us"
		},
		EmailTypeOption = true
	};

	try
	{
		var model = await Manager.Lists.AddOrUpdateAsync(list);
		return View(model);
	}
	catch (MailChimpException)
	{
		return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
	}
	catch (Exception)
	{
		return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
	}
}

And after running the code, you can see the changes reflected both in the View and the MailChimp Dashboard.

This slideshow requires JavaScript.

Retrieve a List

Retrieving a List is quite straightforward. You simply pass its Id to the GetAsync method:

public async Task<ActionResult> Detail()
{
	try
	{
		var model = await Manager.Lists.GetAsync("f809a0eba9");
		return View(model);
	}
	catch (MailChimpException mce)
	{
		return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
	}
	catch (Exception ex)
	{
		return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
	}
}

And the View I’ll use will be its ID, Name and DateCreated.

@model MailChimp.Net.Models.List
@{
    ViewBag.Title = "View List";
}

<h2>View List</h2>

<table class="table table-striped">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Created On</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>@Model.Id</td>
            <td>@Model.Name</td>
            <td>@Model.DateCreated</td>
        </tr>
    </tbody>
</table>

Here’s what that View renders:

The GetList View.
The GetList View.

Retrieve multiple Lists

Getting a list of Lists is a bit more complicated.

If you want to see every list on your account, you can call the GetAllAsync method without any arguments.

Otherwise, you’ll create a ListRequest class, which will allow you to apply several filters to your query, mostly around the time that the list was created or last used to send a Campaign.

public async Task<ActionResult> Index()
{
	var options = new ListRequest
	{
		Limit = 10
	};

	try
	{
		var model = await Manager.Lists.GetAllAsync(options);
		return View(model);
	}
	catch (MailChimpException mce)
	{
		return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
	}
	catch (Exception ex)
	{
		return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
	}
}
@model IEnumerable<MailChimp.Net.Models.List>
@{
    ViewBag.Title = "All Lists";
}

<h2>All Lists</h2>

<table class="table table-striped">
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Created On</th>
        </tr>
    </thead>
    <tbody>
        @foreach(var list in Model)
        {
            <tr>
                <td>@list.Id</td>
                <td>@list.Name</td>
                <td>@list.DateCreated</td>
            </tr>
        }
    </tbody>
</table>

Here’s what that View looks like:

Screenshot of the GetLists View.
Screenshot of the GetLists View.

Delete a List

I strongly discourage you from programming List deletion.

Deleting a List via the MailChimp REST API is permanent and unrecoverable. Says MailChimp:

If you delete a list, you’ll lose the list history—including subscriber activity, unsubscribes, complaints, and bounces. You’ll also lose subscribers’ email addresses, unless you exported and backed up your list.

You have been warned. Do not blame me if it goes badly.

Deleting a List is the evil twin of retrieving a List: Just pass the List’s Id as an argument and it will be destroyed.

public async Task<ActionResult> Delete()
{
	try
	{
		await Manager.Lists.DeleteAsync("f809a0eba9");
		return RedirectToAction("GetLists");
	}
	catch (MailChimpException)
	{
		return new HttpStatusCodeResult(HttpStatusCode.BadGateway, mce.Message);
	}
	catch (Exception)
	{
		return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable, ex.Message);
	}
}

Note that at Line 146, rather than returning a View specific to this ActionResult, I’m just going to send people back to the list of Lists, which should show that the List was removed.

Also, note that the DeleteAsync method is the async version of a void function; it does return a Task but not a Task<TResult>.

This slideshow requires JavaScript.

Final notes

Let me emphasise, one more time: It’s a bad idea to create or delete a List via the API. These tasks belong in the MailChimp Dashboard, with a great deal of sober reflection prior to acting.

Github repo for the demo solution: https://github.com/dougvdotcom/MailChimpNetDemo

There will be a YouTube companion video soon.

Next up: Managing Members. I’ll also demonstrate sending a Campaign to a List using a predefined Template before closing out this tutorial series.

Leave a Reply

  • Check out the Commenting Guidelines before commenting, please!
  • Want to share code? Please put it into a GitHub Gist, CodePen or pastebin and link to that in your comment.
  • Just have a line or two of markup? Wrap them in an appropriate SyntaxHighlighter Evolved shortcode for your programming language, please!