# Mailchimp

> Mailchimp Marketing API integration for email campaign management. Use when needing to (1) manage audiences/lists and their members, (2) create, update, send, or schedule email campaigns, (3) create campaigns from templates with programmatic content population via mc:edit sections, (4) pull campaign performance reports (opens, clicks, subscriber activity), (5) manage email templates (classic/legacy builder), or (6) manage member tags. Covers all core Mailchimp email marketing operations.

- Skill: `buzzmatic/mailchimp` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add buzzmatic/mailchimp`
- Raw SKILL.md: https://api.skillmd.com/api/skills/buzzmatic/mailchimp/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- Author: Buzzmatic (https://skillmd.com/u/buzzmatic)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/buzzmatic/mailchimp

---


# Mailchimp Skill

Manage Mailchimp email campaigns, audiences, templates, and reports via the Mailchimp Marketing API v3.

## Authentication

Set `MAILCHIMP_API_KEY` in your environment or `.env` file. The API key format is `{key}-{dc}` where `dc` is the datacenter (e.g., `us7`). The scripts auto-extract the datacenter to build the correct base URL.

```bash
export MAILCHIMP_API_KEY="your-api-key-us7"
```

## Important: Template Types & Content Population

Mailchimp has two editor types with very different API support:

| Feature | Classic/Legacy (`content_type: "template"`) | New Editor (`content_type: "multichannel"`) |
|---------|---------------------------------------------|---------------------------------------------|
| Editable regions | `mc:edit="section_name"` attributes | `data-block-id` attributes |
| API content population | Full support via `template.sections` | NOT supported |
| Template creation via API | `POST /templates` | Not available |
| Default content discovery | `GET /templates/{id}/default-content` | Returns empty |

**For programmatic content control, always use classic/legacy templates with `mc:edit` regions.** The Mailchimp API v3 has no endpoints for editing multichannel (new editor) content blocks.

### How Template Sections Work

1. Build an HTML email template with `mc:edit` attributes on editable elements:
   ```html
   <td mc:edit="header_title"><h1>Default Title</h1></td>
   <td mc:edit="hero_image"><img src="placeholder.jpg"/></td>
   <td mc:edit="body_text"><p>Default body content</p></td>
   ```

2. Upload the template via `POST /templates` (creates a classic template)

3. Discover available sections:
   ```bash
   python scripts/templates.py content --template-id 12345
   # Returns { "sections": { "header_title": "...", "hero_image": "...", ... } }
   ```

4. Create a campaign and populate all sections in one call:
   ```bash
   python scripts/campaign_content.py set --campaign-id abc123 \
     --template-id 12345 --sections '{"header_title": "<h1>April News</h1>", "hero_image": "<img src=\"https://example.com/photo.jpg\"/>"}'
   ```

   Both text and image sections are populated via `template.sections`. Image sections contain `<img>` tags with the desired `src` URL.

### Working with Existing Multichannel Templates

If existing templates use the multichannel editor, the `create-from-template` command provides a workaround:

1. It finds a sent campaign that used the template
2. Replicates it (preserving the multichannel content)
3. Updates campaign settings (subject, from, etc.)

**Warning:** Setting raw HTML via `PUT /content` on a multichannel campaign converts it to legacy builder and breaks the visual editor rendering. Only update settings on replicated multichannel campaigns, never the content.

## Scripts

All scripts live in `scripts/` and share a common `auth.py` module. They:
- Accept `--api-key` flag or read `MAILCHIMP_API_KEY` env var
- Support `--output` flag to save JSON results to a file
- Print JSON to stdout by default
- Handle pagination (`--count`, `--offset`)
- Include retry logic with exponential backoff on 429/5xx errors

### Audiences (`audiences.py`)

Manage audiences (lists), members, search, and tags.

```bash
# List all audiences
python scripts/audiences.py list [--count 10] [--offset 0]

# Get audience details
python scripts/audiences.py get --list-id abc123

# List members of an audience
python scripts/audiences.py members --list-id abc123 [--status subscribed] [--count 100]

# Add a member
python scripts/audiences.py add-member --list-id abc123 --email user@example.com \
  [--status subscribed] [--merge-fields '{"FNAME":"John","LNAME":"Doe"}']

# Update a member
python scripts/audiences.py update-member --list-id abc123 --email user@example.com \
  [--status unsubscribed] [--merge-fields '{"FNAME":"Johnny"}']

# Remove (archive) a member
python scripts/audiences.py remove-member --list-id abc123 --email user@example.com

# Search members across all audiences (or within one)
python scripts/audiences.py search --query "john" [--list-id abc123]

# List tags for an audience
python scripts/audiences.py tags --list-id abc123

# Add tags to a member
python scripts/audiences.py tag-member --list-id abc123 --email user@example.com \
  --tags '["VIP","Newsletter"]' --status active

# Remove tags from a member
python scripts/audiences.py untag-member --list-id abc123 --email user@example.com \
  --tags '["VIP"]'
```

### Campaigns (`campaigns.py`)

CRUD operations plus send, schedule, unschedule, replicate, and create-from-template.

```bash
# List campaigns
python scripts/campaigns.py list [--status sent] [--count 20] [--offset 0] \
  [--since-send-time 2025-01-01] [--before-send-time 2025-12-31]

# Get campaign details
python scripts/campaigns.py get --campaign-id abc123

# Create a campaign (blank)
python scripts/campaigns.py create --list-id abc123 --subject "Newsletter" \
  --from-name "Company" --reply-to "info@company.com" \
  [--type regular] [--title "Internal Title"] [--preview-text "Preview..."]

# Create from template (finds and replicates a sent campaign using the template)
python scripts/campaigns.py create-from-template --template-id 12345 \
  --subject "April Newsletter" --from-name "Company" --reply-to "info@co.com" \
  [--title "April 2026"] [--preview-text "Preview..."] \
  [--source-campaign-id abc123] [--list-id xyz789]

# Update campaign settings
python scripts/campaigns.py update --campaign-id abc123 \
  [--subject "New Subject"] [--from-name "New Name"] [--reply-to "new@co.com"] \
  [--title "New Title"] [--preview-text "New preview"]

# Delete a campaign
python scripts/campaigns.py delete --campaign-id abc123

# Send a campaign (IRREVERSIBLE - sends to all recipients)
python scripts/campaigns.py send --campaign-id abc123

# Schedule a campaign
python scripts/campaigns.py schedule --campaign-id abc123 \
  --schedule-time "2025-06-01T10:00:00+00:00"

# Unschedule a campaign
python scripts/campaigns.py unschedule --campaign-id abc123

# Replicate (copy) a campaign
python scripts/campaigns.py replicate --campaign-id abc123
```

### Campaign Content (`campaign_content.py`)

Get or set campaign HTML content. Supports raw HTML, HTML files, template references, and template sections for programmatic content population.

```bash
# Get campaign content (returns HTML, plain_text, archive_html)
python scripts/campaign_content.py get --campaign-id abc123

# Set content from inline HTML
python scripts/campaign_content.py set --campaign-id abc123 --html "<html>...</html>"

# Set content from an HTML file
python scripts/campaign_content.py set --campaign-id abc123 --html-file path/to/email.html

# Set content from a template (uses template defaults)
python scripts/campaign_content.py set --campaign-id abc123 --template-id 12345

# Set content from a template with custom sections (programmatic population)
python scripts/campaign_content.py set --campaign-id abc123 --template-id 12345 \
  --sections '{"header_title": "<h1>Custom Title</h1>", "body": "<p>Custom body</p>"}'
```

### Reports (`reports.py`)

Campaign performance analytics.

```bash
# Campaign report summary (opens, clicks, bounce rate, etc.)
python scripts/reports.py summary --campaign-id abc123

# Open details (who opened)
python scripts/reports.py opens --campaign-id abc123 [--count 100] [--offset 0]

# Click details (which links were clicked)
python scripts/reports.py clicks --campaign-id abc123 [--count 100] [--offset 0]

# Subscriber activity for a campaign
python scripts/reports.py activity --campaign-id abc123 [--count 100] [--offset 0]
```

### Templates (`templates.py`)

List, inspect, and get default content for email templates.

```bash
# List templates
python scripts/templates.py list [--count 20] [--offset 0] [--type user]

# Get template info (name, type, content_type, dates)
python scripts/templates.py get --template-id 12345

# Get template default content (returns mc:edit sections with their default HTML)
python scripts/templates.py content --template-id 12345
```

## Typical Workflow: Create & Populate a Newsletter

```bash
# 1. Create a campaign
python scripts/campaigns.py create --list-id 090ac27822 \
  --subject "May 2026 Newsletter" --from-name "Your Company" \
  --reply-to "news@example.com" --title "May 2026 Newsletter" \
  --preview-text "The latest product news"
# Returns: {"id": "abc123", ...}

# 2. Discover template sections
python scripts/templates.py content --template-id 13613235
# Returns: {"sections": {"header_title": "...", "hero_image": "...", ...}}

# 3. Populate content via template + sections
python scripts/campaign_content.py set --campaign-id abc123 --template-id 13613235 \
  --sections '{
    "header_title": "<h1>MAY IN REWIND<br><span style=\"color:#FBAB13;\">AT YOUR COMPANY</span></h1>",
    "hero_image": "<img src=\"https://images.unsplash.com/photo-xxx?w=600\" style=\"width:100%;border-radius:12px;\"/>",
    "key_takeaways": "<ul><li>First item</li><li>Second item</li></ul>",
    "success_progress": "<p>We shipped feature X...</p>"
  }'

# 4. Verify content
python scripts/campaign_content.py get --campaign-id abc123

# 5. Schedule or send
python scripts/campaigns.py schedule --campaign-id abc123 \
  --schedule-time "2026-05-01T08:00:00+00:00"
```

## Pagination

Mailchimp uses `offset` and `count` parameters:
- `count`: Number of records per request (default 10, max 1000)
- `offset`: Number of records to skip

All list commands accept `--count` and `--offset`. To fetch all records, increase `--count` to 1000 or paginate manually.

## Error Handling

Scripts handle common HTTP errors:
- `400` Bad Request: Invalid parameters
- `401` Unauthorized: Invalid API key
- `403` Forbidden: Insufficient permissions
- `404` Not Found: Invalid resource ID
- `429` Too Many Requests: Rate limited (auto-retry with backoff)
- `5xx` Server Error: Transient (auto-retry with backoff)

## API Reference

- Base URL: `https://{dc}.api.mailchimp.com/3.0/`
- Auth: HTTP Basic (`("anystring", api_key)`)
- Full docs: https://mailchimp.com/developer/marketing/api/

