Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.mavera.io/llms.txt

Use this file to discover all available pages before exploring further.

Scenario

Your Mailchimp audience has segments — VIP customers, newsletter-only subscribers, recent signups, inactive members. Each segment represents a real behavioral group. You pull segment definitions and member data, aggregate demographics and engagement patterns per segment, and create a Mavera Custom Persona for each. The result is a persona library built from your actual email audience, not hypothetical demographics.

Architecture

Code

import os, requests, time
from collections import defaultdict

MC_KEY = os.environ["MAILCHIMP_API_KEY"]
MC_DC = os.environ["MAILCHIMP_DC"]
MV = os.environ["MAVERA_API_KEY"]
MB = "https://app.mavera.io/api/v1"
MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
MC_BASE = f"https://{MC_DC}.api.mailchimp.com/3.0"
mc_auth = ("anystring", MC_KEY)

lists_r = requests.get(f"{MC_BASE}/lists", auth=mc_auth, params={"count": 5})
lists_r.raise_for_status()
lists = lists_r.json().get("lists", [])
LIST_ID = lists[0]["id"] if lists else os.environ.get("MAILCHIMP_LIST_ID", "")

segs_r = requests.get(f"{MC_BASE}/lists/{LIST_ID}/segments", auth=mc_auth, params={"count": 20})
segs_r.raise_for_status()
segments = segs_r.json().get("segments", [])

print(f"List: {LIST_ID} | Segments: {len(segments)}\n")

created = []
for seg in segments[:8]:
    seg_id = seg["id"]
    seg_name = seg["name"]
    member_count = seg.get("member_count", 0)

    members_r = requests.get(
        f"{MC_BASE}/lists/{LIST_ID}/segments/{seg_id}/members",
        auth=mc_auth,
        params={"count": 100, "fields": "members.email_address,members.merge_fields,members.stats,members.status,members.source"},
    )
    members = members_r.json().get("members", [])

    sources = defaultdict(int)
    statuses = defaultdict(int)
    avg_open = []
    avg_click = []

    for m in members:
        if m.get("source"):
            sources[m["source"]] += 1
        statuses[m.get("status", "unknown")] += 1
        stats = m.get("stats", {})
        avg_open.append(stats.get("avg_open_rate", 0))
        avg_click.append(stats.get("avg_click_rate", 0))

    top_sources = sorted(sources, key=sources.get, reverse=True)[:3]
    seg_avg_open = sum(avg_open) / max(len(avg_open), 1)
    seg_avg_click = sum(avg_click) / max(len(avg_click), 1)

    persona = requests.post(f"{MB}/personas", headers=MH, json={
        "name": f"Mailchimp: {seg_name}",
        "description": (
            f"Email subscriber segment from Mailchimp. {member_count} members. "
            f"Avg open rate: {seg_avg_open:.0%}. Avg click rate: {seg_avg_click:.0%}. "
            f"Top signup sources: {', '.join(top_sources) if top_sources else 'N/A'}. "
            f"Segment type: {seg.get('type', 'saved')}."
        ),
        "demographic": {
            "source": "mailchimp_segment",
            "segment_id": str(seg_id),
            "member_count": member_count,
            "signup_sources": top_sources,
        },
        "psychographic": {
            "avg_open_rate": seg_avg_open,
            "avg_click_rate": seg_avg_click,
            "engagement_level": "high" if seg_avg_open > 0.3 else "medium" if seg_avg_open > 0.15 else "low",
        },
    }).json()

    created.append({"segment": seg_name, "id": persona["id"], "members": member_count})
    print(f"  {seg_name}: {persona['id']} ({member_count} members, {seg_avg_open:.0%} open)")
    time.sleep(0.5)

print(f"\nCreated {len(created)} subscriber personas")

Example Output

{
  "list": "abc123def",
  "personas": [
    { "segment": "VIP Customers", "id": "per_mc_vip_1", "members": 450, "avg_open": "42%" },
    { "segment": "Recent Signups (30d)", "id": "per_mc_new_2", "members": 1200, "avg_open": "38%" },
    { "segment": "Newsletter Only", "id": "per_mc_nl_3", "members": 3400, "avg_open": "18%" },
    { "segment": "Inactive 90d+", "id": "per_mc_in_4", "members": 2100, "avg_open": "4%" }
  ]
}

Error Handling

Mailchimp enforces 10 concurrent connections per API key. The code uses sequential requests with 500ms delays. For parallel processing, use a connection pool limited to 8.
The /segments/{id}/members endpoint returns max 1,000 members per request. Use offset for pagination. Each page counts as one connection.
Member merge_fields (FNAME, LNAME, etc.) depend on your audience configuration. The code uses stats which are always available regardless of merge field setup.

What’s Next

Mailchimp Integration

Back to Mailchimp integration overview

Campaign Content Optimization

Train brand voice from winning campaigns

Personas API

Full reference for POST /api/v1/personas

All Integrations

50+ API integrations with full code