Scenarios

What you can build with Maps MCP

Sixteen worked queries that answer real questions — “how many precincts have high poverty”, “where are the donor-rich areas”, “what did Trump flip in 2024”. Each scenario is one tool call, copy-paste in cURL / JavaScript / Python. The data is already in the system cache — no Census API key, no spreadsheet exports, no map-prep workflow.

How many swing precincts in NC have >25% below the poverty line?High-income, highly-educated areas that voted Harris (donor targeting)Rent-burdened areas that Trump flipped in 2024Drop any U.S. address — get every layer of contextBulk-enrich a CSV of addresses with Census demographicsFind tracts with low broadband adoption AND high poverty (digital di…Spanish-speaking precincts in Texas where Trump won — the 2024 GOP g…Define your own territories and roll demographics up to themBuild a persuasion universe in one queryGenerate door-knocking walk-list bundles with full demographicsCarve a state into organizer turfs and roll demographics upFind communities where in-person outreach beats digitalFind rent-burdened renters in winnable districtsDonor-rich neighborhoods that historically don't voteEmbed a live demographic card for any address in your appIngest your data, render a citable map, embed it anywhere
Political organizing query_precincts

How many swing precincts in NC have >25% below the poverty line?

Direct mail and door-knocking spend goes 10× further when you target precincts that are both economically anxious AND politically pickable. One filter does both.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_precincts","arguments":{"filter":{"state_in":["NC"],"margin_pct_abs_lte":10,"votes_total_gte":100,"poverty_pct_gte":25},"order_by":"pct_dem_lead asc","limit":500}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "query_precincts",
      arguments: {
        "filter": {
          "state_in": [
            "NC"
          ],
          "margin_pct_abs_lte": 10,
          "votes_total_gte": 100,
          "poverty_pct_gte": 25
        },
        "order_by": "pct_dem_lead asc",
        "limit": 500
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "query_precincts",
            "arguments": {
              "filter": {
                "state_in": [
                  "NC"
                ],
                "margin_pct_abs_lte": 10,
                "votes_total_gte": 100,
                "poverty_pct_gte": 25
              },
              "order_by": "pct_dem_lead asc",
              "limit": 500
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Count + summary stats + 5-row sample. Pass the result_set_id to create_map_view to publish a citable map for the campaign brief.
{
  "result_set_id": "rs_a7f3...",
  "count": 187,
  "stats": {
    "dem_wins": 64, "rep_wins": 123, "swing_count": 187,
    "mean_margin_pct": 2.4, "total_votes": 412,883
  },
  "sample": [
    {
      "external_id": "37119-018",
      "state": "NC", "county": "Mecklenburg",
      "values": {"pct_dem_lead": -0.014, "votes_total": 2155}
    }
  ],
  "filter_summary": "precincts in NC, margin within ±10pts, ≥100 votes, ≥25% below poverty"
}
Campaign field opsVoter outreachMail program targeting
Fundraising query_precincts

High-income, highly-educated areas that voted Harris (donor targeting)

Find precincts where median household income ≥ $150K and ≥60% have a bachelor's degree, that broke for the Democrat — the donor-rich zones.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_precincts","arguments":{"filter":{"winner_party":"DEM","median_hhi_gte":150000,"pct_bachelors_plus_gte":60,"votes_total_gte":200},"order_by":"votes_total desc","limit":200}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "query_precincts",
      arguments: {
        "filter": {
          "winner_party": "DEM",
          "median_hhi_gte": 150000,
          "pct_bachelors_plus_gte": 60,
          "votes_total_gte": 200
        },
        "order_by": "votes_total desc",
        "limit": 200
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "query_precincts",
            "arguments": {
              "filter": {
                "winner_party": "DEM",
                "median_hhi_gte": 150000,
                "pct_bachelors_plus_gte": 60,
                "votes_total_gte": 200
              },
              "order_by": "votes_total desc",
              "limit": 200
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Combine with bulk_demographics_for_addresses to score your existing donor file against each precinct's profile, then create_map_view to brief the finance committee.
{
  "result_set_id": "rs_4c11...",
  "count": 1,247,
  "stats": {"dem_wins": 1247, "mean_margin_pct": 38.4},
  "sample": [
    {"external_id": "36061-076", "state": "NY", "county": "New York",
     "values": {"pct_dem_lead": 0.41, "votes_total": 1812}}
  ]
}
Donor list scoringFinance committee briefingsMajor-gift prospecting
Policy research query_precincts

Rent-burdened areas that Trump flipped in 2024

Pair a housing-affordability stress test with a political-realignment view. The intersection is where economic-anxiety policy briefs actually land.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_precincts","arguments":{"filter":{"winner_party":"REP","rent_burden_pct_gte":50,"margin_pct_abs_lte":15},"order_by":"votes_total desc","limit":500}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "query_precincts",
      arguments: {
        "filter": {
          "winner_party": "REP",
          "rent_burden_pct_gte": 50,
          "margin_pct_abs_lte": 15
        },
        "order_by": "votes_total desc",
        "limit": 500
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "query_precincts",
            "arguments": {
              "filter": {
                "winner_party": "REP",
                "rent_burden_pct_gte": 50,
                "margin_pct_abs_lte": 15
              },
              "order_by": "votes_total desc",
              "limit": 500
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Filter compiles to one SQL query against the system precinct ACS cache (joined to the NYT 2024 precinct file). Returns under 200ms even at 5K-row cap.
{
  "result_set_id": "rs_8e91...",
  "count": 2,143,
  "stats": {
    "rep_wins": 2143,
    "mean_margin_pct": -7.8,
    "total_votes": 4.2M
  }
}
Think-tank reportsPolicy white papersHousing-affordability advocacy
Site analysis evaluate_site

Drop any U.S. address — get every layer of context

One call returns the address's state, county, congressional district, ZIP code area, Census tract, school district, AND voting precinct — each with 58 federal demographic variables and any public election data we host. Perfect for site selection, real-estate due diligence, or political canvassing prep.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"evaluate_site","arguments":{"address":"350 5th Ave, New York, NY 10118"}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "evaluate_site",
      arguments: {
        "address": "350 5th Ave, New York, NY 10118"
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "evaluate_site",
            "arguments": {
              "address": "350 5th Ave, New York, NY 10118"
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Returns the geocoded coords + one administrative record per layer, each with demographics (population, race, income brackets, poverty rate, education, employment, commute, rent burden, broadband) and overlay datasets (presidential results, Cook PVI, DW-NOMINATE).
{
  "match": {"matched": true, "matched_address": "350 5TH AVE, NEW YORK, NY, 10118",
            "lng": -73.985, "lat": 40.748},
  "administrative": [
    {"collection": "us-tracts", "external_id": "36061007600",
     "name": "Census Tract 76",
     "demographics": {
       "B01001_001E": 2455,     "B19013_001E": 164188,
       "pct_below_poverty": 6.0, "pct_hispanic": 14.7,
       "pct_bachelors_plus": 89.2, "mean_commute_mins": 22.2,
       /* ... 50+ more vars ... */
     },
     "overlays": { /* presidential results, Cook PVI, etc. */ }
    },
    /* + state, county, CD, sldu, sldl, ZCTA, school district, precinct */
  ]
}
Real estate due diligenceRetail site selectionVoter canvassing prepHealthcare market research
CRM enrichment bulk_demographics_for_addresses

Bulk-enrich a CSV of addresses with Census demographics

Geocode + Census ACS lookup + tract-level aggregation in one call. Up to 10,000 addresses per call, with the system cache backing every lookup so we don't hit api.census.gov per row.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"bulk_demographics_for_addresses","arguments":{"addresses":[{"id":"lead_001","address":"100 Fayetteville St, Raleigh, NC 27601"},{"id":"lead_002","address":"500 S Tryon St, Charlotte, NC 28202"}],"variables":["B01001_001E","B19013_001E","B17001_002E"]}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "bulk_demographics_for_addresses",
      arguments: {
        "addresses": [
          {
            "id": "lead_001",
            "address": "100 Fayetteville St, Raleigh, NC 27601"
          },
          {
            "id": "lead_002",
            "address": "500 S Tryon St, Charlotte, NC 28202"
          }
        ],
        "variables": [
          "B01001_001E",
          "B19013_001E",
          "B17001_002E"
        ]
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "bulk_demographics_for_addresses",
            "arguments": {
              "addresses": [
                {
                  "id": "lead_001",
                  "address": "100 Fayetteville St, Raleigh, NC 27601"
                },
                {
                  "id": "lead_002",
                  "address": "500 S Tryon St, Charlotte, NC 28202"
                }
              ],
              "variables": [
                "B01001_001E",
                "B19013_001E",
                "B17001_002E"
              ]
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Each row maps to its containing Census tract + the requested ACS variables. Use to score leads by household income, append demographic context to donor records, or build market-segmentation analyses.
{
  "matched": 2, "unmatched": 0, "tract_resolved": 2,
  "rows": [
    {"id": "lead_001", "matched_address": "100 FAYETTEVILLE ST, RALEIGH, NC, 27601",
     "tract_geoid": "37183052108",
     "values": {"B01001_001E": 2810, "B19013_001E": 88500, "B17001_002E": 134}},
    {"id": "lead_002", "matched_address": "500 S TRYON ST, CHARLOTTE, NC, 28202",
     "tract_geoid": "37119000102",
     "values": {"B01001_001E": 4521, "B19013_001E": 116200, "B17001_002E": 287}}
  ]
}
CRM data enrichmentLead scoringCustomer segmentation
Grant writing compare_geographies

Find tracts with low broadband adoption AND high poverty (digital divide)

Quantifying the digital divide for federal NDIA / state broadband grants. Returns tract-level rows where <70% have broadband and >20% are below the poverty line.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"compare_geographies","arguments":{"geographies":[{"collection":"us-tracts","version":"tiger-2024","state":"WV"}],"variables":["B28011_001E","B28011_002E","B17001_001E","B17001_002E"]}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "compare_geographies",
      arguments: {
        "geographies": [
          {
            "collection": "us-tracts",
            "version": "tiger-2024",
            "state": "WV"
          }
        ],
        "variables": [
          "B28011_001E",
          "B28011_002E",
          "B17001_001E",
          "B17001_002E"
        ]
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "compare_geographies",
            "arguments": {
              "geographies": [
                {
                  "collection": "us-tracts",
                  "version": "tiger-2024",
                  "state": "WV"
                }
              ],
              "variables": [
                "B28011_001E",
                "B28011_002E",
                "B17001_001E",
                "B17001_002E"
              ]
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Get the raw counts back, then filter on derived (broadband/total < 0.7) AND (poverty/total > 0.2) in your app. For a one-call version with derived ratios, use evaluate_site or query_precincts.
{
  "geographies": [{"collection": "us-tracts", "version": "tiger-2024", "state": "WV"}],
  "variables": [...],
  "rows": [
    {"external_id": "54077950100", "values": {
      "B28011_001E": 421, "B28011_002E": 257,
      "B17001_001E": 1180, "B17001_002E": 339
    }, "derived": {"pct_broadband": 61.0, "pct_below_poverty": 28.7}}
    /* ... */
  ]
}
NDIA / state broadband grantsDigital-equity reportsCommunity-needs assessments
Outreach research query_precincts

Spanish-speaking precincts in Texas where Trump won — the 2024 GOP gains

Identify the Hispanic-majority precincts that moved away from Democrats. Critical for both Democratic re-engagement and Republican fortification strategies.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_precincts","arguments":{"filter":{"state_in":["TX"],"winner_party":"REP","pct_hispanic_gte":50,"votes_total_gte":200},"order_by":"votes_total desc","limit":500}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "query_precincts",
      arguments: {
        "filter": {
          "state_in": [
            "TX"
          ],
          "winner_party": "REP",
          "pct_hispanic_gte": 50,
          "votes_total_gte": 200
        },
        "order_by": "votes_total desc",
        "limit": 500
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "query_precincts",
            "arguments": {
              "filter": {
                "state_in": [
                  "TX"
                ],
                "winner_party": "REP",
                "pct_hispanic_gte": 50,
                "votes_total_gte": 200
              },
              "order_by": "votes_total desc",
              "limit": 500
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Combine with classify_precincts to bucket the result into 'flip targets' (margin < 5 pts) vs 'safe GOP' (margin > 15 pts). Use create_map_view to publish a visual brief.
{
  "result_set_id": "rs_d34a...",
  "count": 1,892,
  "stats": {
    "rep_wins": 1892,
    "mean_margin_pct": -14.7,
    "total_votes": 1.1M
  }
}
Hispanic outreach strategyPolitical journalismRealignment research
Custom territories create_region_group

Define your own territories and roll demographics up to them

Sales territories, school catchments, campaign zones, underwriting books — anything you'd draw on a whiteboard. Hierarchical region groups, demographics auto-aggregated.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"create_region_group","arguments":{"slug":"swing-states-2024","name":"2024 swing states","regions":[{"name":"Sun Belt","members":[{"collection":"us-states","external_id":"04"},{"collection":"us-states","external_id":"13"},{"collection":"us-states","external_id":"32"}]},{"name":"Blue Wall","members":[{"collection":"us-states","external_id":"26"},{"collection":"us-states","external_id":"42"},{"collection":"us-states","external_id":"55"}]}]}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "create_region_group",
      arguments: {
        "slug": "swing-states-2024",
        "name": "2024 swing states",
        "regions": [
          {
            "name": "Sun Belt",
            "members": [
              {
                "collection": "us-states",
                "external_id": "04"
              },
              {
                "collection": "us-states",
                "external_id": "13"
              },
              {
                "collection": "us-states",
                "external_id": "32"
              }
            ]
          },
          {
            "name": "Blue Wall",
            "members": [
              {
                "collection": "us-states",
                "external_id": "26"
              },
              {
                "collection": "us-states",
                "external_id": "42"
              },
              {
                "collection": "us-states",
                "external_id": "55"
              }
            ]
          }
        ]
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "create_region_group",
            "arguments": {
              "slug": "swing-states-2024",
              "name": "2024 swing states",
              "regions": [
                {
                  "name": "Sun Belt",
                  "members": [
                    {
                      "collection": "us-states",
                      "external_id": "04"
                    },
                    {
                      "collection": "us-states",
                      "external_id": "13"
                    },
                    {
                      "collection": "us-states",
                      "external_id": "32"
                    }
                  ]
                },
                {
                  "name": "Blue Wall",
                  "members": [
                    {
                      "collection": "us-states",
                      "external_id": "26"
                    },
                    {
                      "collection": "us-states",
                      "external_id": "42"
                    },
                    {
                      "collection": "us-states",
                      "external_id": "55"
                    }
                  ]
                }
              ]
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Then call render_region_group_map with style='choropleth_value' + a workspace dataset to publish a custom-territory choropleth, OR query_counties with region_group='swing-states-2024' to scope queries to your zones.
{
  "region_group_id": "rg_8b21...",
  "slug": "swing-states-2024",
  "regions": [
    {"name": "Sun Belt", "member_count": 3, "population": 33.5M},
    {"name": "Blue Wall", "member_count": 3, "population": 28.2M}
  ],
  "dashboard_url": "https://mapsmcp.com/dashboard/regions/swing-states-2024"
}
Sales territory managementSchool catchment planningCampaign zone definitionInsurance underwriting books
Grassroots organizing query_precincts

Build a persuasion universe in one query

A persuasion universe is the set of geographies where a campaign's argument can actually move votes — competitive enough to matter, demographically aligned with the issue. One precinct query defines yours; the result_set powers walk lists, mail targeting, digital geofencing, and the precinct-by-precinct ROI dashboard you build on top.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_precincts","arguments":{"filter":{"state_in":["NC","GA"],"margin_pct_abs_lte":12,"votes_total_gte":150,"pct_hispanic_gte":30,"median_hhi_lte":65000},"order_by":"votes_total desc","limit":1500}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "query_precincts",
      arguments: {
        "filter": {
          "state_in": [
            "NC",
            "GA"
          ],
          "margin_pct_abs_lte": 12,
          "votes_total_gte": 150,
          "pct_hispanic_gte": 30,
          "median_hhi_lte": 65000
        },
        "order_by": "votes_total desc",
        "limit": 1500
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "query_precincts",
            "arguments": {
              "filter": {
                "state_in": [
                  "NC",
                  "GA"
                ],
                "margin_pct_abs_lte": 12,
                "votes_total_gte": 150,
                "pct_hispanic_gte": 30,
                "median_hhi_lte": 65000
              },
              "order_by": "votes_total desc",
              "limit": 1500
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Hand the result_set_id to create_map_view → publish_map for a citable persuasion-universe map. Or expose the result_set to your front-end and let organizers click precincts to add them to a walk-list dataset via ingest_dataset.
{
  "result_set_id": "rs_pers_8e3f...",
  "count": 1,247,
  "stats": {
    "swing_count": 1247,
    "mean_margin_pct": -2.1,
    "total_votes": 412,883
  },
  "filter_summary": "NC + GA, margin within ±12pts, ≥150 votes, ≥30% Hispanic, MHI ≤ $65K"
}
Campaign persuasion universeIssue advocacy targetingGOTV prioritizationField-program ROI modeling
Grassroots organizing bulk_demographics_for_addresses

Generate door-knocking walk-list bundles with full demographics

Every organizer wants a printable / mobile walk list with the demographics next to each door. Pass a CSV of addresses; get back geocoded coordinates + matched Census tract + median income + poverty rate + commute mode + the precinct each address is in. Pipe straight into your canvasser app.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"bulk_demographics_for_addresses","arguments":{"addresses":[{"id":"lead_001","address":"100 Fayetteville St, Raleigh, NC 27601"},{"id":"lead_002","address":"500 S Tryon St, Charlotte, NC 28202"},{"id":"lead_003","address":"200 W Trade St, Charlotte, NC 28202"}],"variables":["B01001_001E","B19013_001E","B17001_002E","B25070_010E","B16001_002E"]}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "bulk_demographics_for_addresses",
      arguments: {
        "addresses": [
          {
            "id": "lead_001",
            "address": "100 Fayetteville St, Raleigh, NC 27601"
          },
          {
            "id": "lead_002",
            "address": "500 S Tryon St, Charlotte, NC 28202"
          },
          {
            "id": "lead_003",
            "address": "200 W Trade St, Charlotte, NC 28202"
          }
        ],
        "variables": [
          "B01001_001E",
          "B19013_001E",
          "B17001_002E",
          "B25070_010E",
          "B16001_002E"
        ]
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "bulk_demographics_for_addresses",
            "arguments": {
              "addresses": [
                {
                  "id": "lead_001",
                  "address": "100 Fayetteville St, Raleigh, NC 27601"
                },
                {
                  "id": "lead_002",
                  "address": "500 S Tryon St, Charlotte, NC 28202"
                },
                {
                  "id": "lead_003",
                  "address": "200 W Trade St, Charlotte, NC 28202"
                }
              ],
              "variables": [
                "B01001_001E",
                "B19013_001E",
                "B17001_002E",
                "B25070_010E",
                "B16001_002E"
              ]
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Up to 10K addresses per call. The result is a flat array keyed by your `id` — one row per door — so you can join it back to your volunteer roster in your DB and hand each canvasser a JSON file scoped to their turf.
{
  "matched": 3, "unmatched": 0, "tract_resolved": 3,
  "rows": [
    {"id":"lead_001","tract_geoid":"37183052108",
     "values":{"B01001_001E":2810,"B19013_001E":88500,"B17001_002E":134,
               "B25070_010E":29,"B16001_002E":2480}},
    /* lead_002, lead_003 ... */
  ]
}
Door-knocking walk listsField-canvasser mobile appVolunteer turf assignmentMutual-aid neighborhood mapping
Grassroots organizing create_region_group

Carve a state into organizer turfs and roll demographics up

Most campaign software ships you 'pre-made' regions you can't tweak. Region groups let YOU draw the turfs — a captain per ZIP, an organizer per 3 precincts, whatever your hierarchy is — and every demographic + result is auto-aggregated up the tree.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"create_region_group","arguments":{"slug":"nc-house-12-turfs","name":"NC House District 12 organizing turfs","regions":[{"name":"West Charlotte (Org: Alicia)","members":[{"collection":"us-precincts-2024","external_id":"37119-011"},{"collection":"us-precincts-2024","external_id":"37119-012"},{"collection":"us-precincts-2024","external_id":"37119-013"}]},{"name":"South Charlotte (Org: Marcus)","members":[{"collection":"us-precincts-2024","external_id":"37119-024"},{"collection":"us-precincts-2024","external_id":"37119-025"}]}]}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "create_region_group",
      arguments: {
        "slug": "nc-house-12-turfs",
        "name": "NC House District 12 organizing turfs",
        "regions": [
          {
            "name": "West Charlotte (Org: Alicia)",
            "members": [
              {
                "collection": "us-precincts-2024",
                "external_id": "37119-011"
              },
              {
                "collection": "us-precincts-2024",
                "external_id": "37119-012"
              },
              {
                "collection": "us-precincts-2024",
                "external_id": "37119-013"
              }
            ]
          },
          {
            "name": "South Charlotte (Org: Marcus)",
            "members": [
              {
                "collection": "us-precincts-2024",
                "external_id": "37119-024"
              },
              {
                "collection": "us-precincts-2024",
                "external_id": "37119-025"
              }
            ]
          }
        ]
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "create_region_group",
            "arguments": {
              "slug": "nc-house-12-turfs",
              "name": "NC House District 12 organizing turfs",
              "regions": [
                {
                  "name": "West Charlotte (Org: Alicia)",
                  "members": [
                    {
                      "collection": "us-precincts-2024",
                      "external_id": "37119-011"
                    },
                    {
                      "collection": "us-precincts-2024",
                      "external_id": "37119-012"
                    },
                    {
                      "collection": "us-precincts-2024",
                      "external_id": "37119-013"
                    }
                  ]
                },
                {
                  "name": "South Charlotte (Org: Marcus)",
                  "members": [
                    {
                      "collection": "us-precincts-2024",
                      "external_id": "37119-024"
                    },
                    {
                      "collection": "us-precincts-2024",
                      "external_id": "37119-025"
                    }
                  ]
                }
              ]
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Then call render_region_group_map with a workspace dataset like donor density or volunteer count to publish a personalized recap map per organizer — auto-captioned, frozen at publish time, embed-ready.
{
  "region_group_id": "rg_nc12...",
  "regions": [
    {"name": "West Charlotte (Org: Alicia)", "members": 3, "population": 17.8K, "registered_voters": 12.1K},
    {"name": "South Charlotte (Org: Marcus)", "members": 2, "population": 11.2K, "registered_voters": 8.4K}
  ]
}
Turf assignment per organizerCustom canvass zonesPrecinct-captain dashboardsVolunteer-coverage heatmap
Issue advocacy query_precincts

Find communities where in-person outreach beats digital

Your members are deciding between a digital ad buy and a door-knock canvass. Pull the tracts where broadband adoption is below 70% AND poverty is above 20% — those are the places where in-person, on-the-ground organizing is non-substitutable.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_precincts","arguments":{"filter":{"pct_broadband_lte":70,"poverty_pct_gte":20,"votes_total_gte":100},"order_by":"pct_dem_lead asc","limit":800}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "query_precincts",
      arguments: {
        "filter": {
          "pct_broadband_lte": 70,
          "poverty_pct_gte": 20,
          "votes_total_gte": 100
        },
        "order_by": "pct_dem_lead asc",
        "limit": 800
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "query_precincts",
            "arguments": {
              "filter": {
                "pct_broadband_lte": 70,
                "poverty_pct_gte": 20,
                "votes_total_gte": 100
              },
              "order_by": "pct_dem_lead asc",
              "limit": 800
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Pair this with create_map_view to publish a digital-divide heatmap. Or hand the result_set to your CRM to auto-tag every contact in those precincts as 'in-person priority'.
{
  "count": 4,891,
  "stats": {"mean_poverty_pct": 27.4, "mean_broadband_pct": 56.2},
  "filter_summary": "precincts with broadband ≤70%, poverty ≥20%, ≥100 votes"
}
Digital-divide programsFederal broadband-grant proposalsField-vs-digital ROICommunity needs assessments
Issue advocacy query_precincts

Find rent-burdened renters in winnable districts

A housing campaign lives or dies on whether you find the renters who are paying 30-50%+ of income on rent AND live in precincts where their vote can swing an election. Two filters, one query.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_precincts","arguments":{"filter":{"rent_burden_pct_gte":45,"margin_pct_abs_lte":15,"votes_total_gte":200},"order_by":"rent_burden_pct_gte desc","limit":500}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "query_precincts",
      arguments: {
        "filter": {
          "rent_burden_pct_gte": 45,
          "margin_pct_abs_lte": 15,
          "votes_total_gte": 200
        },
        "order_by": "rent_burden_pct_gte desc",
        "limit": 500
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "query_precincts",
            "arguments": {
              "filter": {
                "rent_burden_pct_gte": 45,
                "margin_pct_abs_lte": 15,
                "votes_total_gte": 200
              },
              "order_by": "rent_burden_pct_gte desc",
              "limit": 500
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Add `state_in` to scope to your campaign geography. The result drives both your direct mail (matched by USPS via your CRM) AND the policy brief your candidate hands to the city council.
{
  "count": 1,302,
  "stats": {"mean_rent_burden_pct": 52.8, "mean_margin_pct": -3.4},
  "filter_summary": "rent burden ≥45%, margin within ±15pts, ≥200 votes"
}
Tenant-rights campaignsAffordable-housing advocacyRenter-mobilization GOTVCity-council policy briefs
Fundraising query_precincts

Donor-rich neighborhoods that historically don't vote

There are precincts where median household income is $200K+ but turnout is below 50%. Those are donors who'd give if asked but won't get themselves to a polling place. A finance committee briefing in one query.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"query_precincts","arguments":{"filter":{"median_hhi_gte":200000,"pct_bachelors_plus_gte":70,"votes_total_gte":50},"order_by":"votes_total asc","limit":300}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "query_precincts",
      arguments: {
        "filter": {
          "median_hhi_gte": 200000,
          "pct_bachelors_plus_gte": 70,
          "votes_total_gte": 50
        },
        "order_by": "votes_total asc",
        "limit": 300
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "query_precincts",
            "arguments": {
              "filter": {
                "median_hhi_gte": 200000,
                "pct_bachelors_plus_gte": 70,
                "votes_total_gte": 50
              },
              "order_by": "votes_total asc",
              "limit": 300
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Score your existing donor file against these precincts; the overlap is your fundraising ask list. Score your non-donor voter file against these precincts; the overlap is your finance-committee acquisition list.
{
  "count": 287,
  "stats": {"mean_mhi": 248000, "mean_bachelors_pct": 81.2, "mean_turnout_pct": 38.4},
  "sample": [
    {"external_id": "06037-7062", "state": "CA", "county": "Los Angeles"}
  ]
}
Finance committee briefingsDonor list segmentationMajor-gift acquisitionLow-engagement-high-capacity targeting
Platform integration evaluate_site

Embed a live demographic card for any address in your app

Your organizers want to drop an address into your mobile canvas app and instantly see who lives there — every demographic layer, who they voted for, what district they're in. One MCP call, one render, drops straight into your React component.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"evaluate_site","arguments":{"address":"500 S Tryon St, Charlotte, NC 28202"}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "evaluate_site",
      arguments: {
        "address": "500 S Tryon St, Charlotte, NC 28202"
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "evaluate_site",
            "arguments": {
              "address": "500 S Tryon St, Charlotte, NC 28202"
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Server-side rendered, no PII, public data only. Bake a /v/<slug>.png URL into your push notifications, embed an interactive iframe in your CRM ticket view, or surface key metrics inline in your canvas script.
{
  "match": {"matched_address": "500 S TRYON ST, CHARLOTTE, NC, 28202"},
  "administrative": [
    {"collection":"us-tracts","external_id":"37119000102",
     "demographics":{"B01001_001E":4521,"B19013_001E":116200,
                     "pct_below_poverty":12.4,"pct_hispanic":15.1,
                     "pct_rent_burdened":42.0}},
    /* + state, county, CD, sldu, sldl, ZCTA, school district, precinct */
  ]
}
Field-canvas mobile appCRM contact-view enrichmentVolunteer training materialDoor-knocker briefing card
Platform integration create_report

Ingest your data, render a citable map, embed it anywhere

You've got your own data — volunteer counts per precinct, donations per ZIP, attendance per ward. Ingest it, render a map, freeze the snapshot, and embed the result on your campaign site, in a newsletter, on a press release. Five MCP calls. Zero design work.

curl -X POST https://mapsmcp.com/mcp \
  -H "Authorization: Bearer $MAPSMCP_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"create_report","arguments":{"slug":"ward-volunteer-density-q1-2026","config":{"collection":"us-counties","version":"tiger-2024","datasets":[{"slug":"volunteer-count-by-ward","field":"volunteers_per_capita","palette":"sequential","classify":{"method":"quantile","bins":7}}]},"title":"Q1 volunteer density by ward","public":true}}}'
const r = await fetch("https://mapsmcp.com/mcp", {
  method: "POST",
  headers: {
    "Authorization": "Bearer " + process.env.MAPSMCP_API_KEY,
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
  },
  body: JSON.stringify({
    jsonrpc: "2.0", id: 1, method: "tools/call",
    params: {
      name: "create_report",
      arguments: {
        "slug": "ward-volunteer-density-q1-2026",
        "config": {
          "collection": "us-counties",
          "version": "tiger-2024",
          "datasets": [
            {
              "slug": "volunteer-count-by-ward",
              "field": "volunteers_per_capita",
              "palette": "sequential",
              "classify": {
                "method": "quantile",
                "bins": 7
              }
            }
          ]
        },
        "title": "Q1 volunteer density by ward",
        "public": true
      },
    },
  }),
});
// Server-sent events; the first 'data:' line carries the JSON-RPC payload.
const text = await r.text();
const payload = JSON.parse(text.match(/^data: (.*)$/m)[1]);
const result = JSON.parse(payload.result.content[0].text);
console.log(result);
import os, json, re, requests

resp = requests.post(
    "https://mapsmcp.com/mcp",
    headers={
        "Authorization": f"Bearer {os.environ['MAPSMCP_API_KEY']}",
        "Content-Type": "application/json",
        "Accept": "application/json, text/event-stream",
    },
    json={
        "jsonrpc": "2.0", "id": 1, "method": "tools/call",
        "params": {
            "name": "create_report",
            "arguments": {
              "slug": "ward-volunteer-density-q1-2026",
              "config": {
                "collection": "us-counties",
                "version": "tiger-2024",
                "datasets": [
                  {
                    "slug": "volunteer-count-by-ward",
                    "field": "volunteers_per_capita",
                    "palette": "sequential",
                    "classify": {
                      "method": "quantile",
                      "bins": 7
                    }
                  }
                ]
              },
              "title": "Q1 volunteer density by ward",
              "public": true
            },
        },
    },
)
# Server-sent events: pull the data line, parse JSON.
sse = re.search(r"^data: (.*)$", resp.text, re.M).group(1)
payload = json.loads(sse)
result = json.loads(payload["result"]["content"][0]["text"])
print(result)
What comes back: Returns a citable URL: https://mapsmcp.com/v/<slug>. Each report ships with an embed iframe, a citation.json with provenance (which dataset version, which boundary vintage, the exact threshold cuts), a downloadable static PNG, and (if you toggle freeze=true) a snapshot that stays valid even if the underlying data changes.
{
  "ok": true,
  "slug": "ward-volunteer-density-q1-2026",
  "public_url": "https://mapsmcp.com/v/ward-volunteer-density-q1-2026",
  "embed_url": "https://mapsmcp.com/embed/ward-volunteer-density-q1-2026",
  "citation_url": "https://mapsmcp.com/v/ward-volunteer-density-q1-2026/citation.json",
  "frozen_at": "2026-05-25T22:15:33Z"
}
Campaign-site embedsPress-release mapsNewsletter graphicsBoard-meeting decks

Pluggable. Copy-paste these into your app.

Free tier gets 50 calls/month, no card. Premium scales to 50K calls/month with batch APIs.

Get a free API key →