Ultimaps MCP
officialTurn data into map images: choropleth, category and pin maps of the world, countries, states, counties and ZIP codes.
What can you do with Ultimaps MCP?
- Render a map from data — Ask your assistant to turn region values into a colored choropleth map image with legend and title.
- Highlight specific regions — Request a map where named states, counties or countries are filled with chosen hex colors.
- Place pins by coordinates — Ask for labeled pins at given latitudes and longitudes, optionally combined with a choropleth.
- List available maps — Have your assistant look up which map IDs exist, such as
united-statesoreurope. - Check region keys before rendering — Run a dry run to see which region names matched, which were corrected and which failed.
- Look up a map's regions — Ask which regions a given map contains so you can build valid keys for a render.
Documentation
Map Image API
Data in, map image out. One URL renders a choropleth, category or pin map of any country, state, county or ZIP area as a PNG. No account, no key, no map library in your stack.
https://api.ultimaps.com/v1/renders?spec=%7B%22mapId%22%3A%22united-states%22%2C%22regions%22%3A%7B%22US-CA%22%3A%22%231D4ED8%22%2C%22US-TX%22%3A%22%23F59E0B%22%2C%22New%20York%22%3A%22%2310B981%22%7D%2C%22title%22%3A%7B%22text%22%3A%22Where%20we%20operate%22%7D%2C%22style%22%3A%7B%22labels%22%3A%7B%22show%22%3Atrue%7D%7D%2C%22output%22%3A%7B%22width%22%3A1200%7D%7D
That is the whole request. The spec parameter is URL-encoded JSON, and the response is the image itself.
Rendered live by the URL on the left, cached for 24 hours.
Drops in anywhere
The URL returns the image, so it works in an <img> tag, a README, a Notion page or a Google Sheets cell.
GET or POST
GET takes every feature but caps the spec at 6KB, and it always renders keyless PNG up to 1600px. Send the same JSON to POST /v1/renders for a bigger payload, a key for a larger canvas, or a Pro key for SVG.
Editable afterwards
Every image carries a Link header that opens the render in Ultimaps Studio as a real map. Keyless renders open for anyone with the link. A keyed render opens only for someone signed in to that key's workspace.
Cookbook
Six complete requests. Every one is validated against the live request schema in CI, so you can copy them as they are, swap the mapId and the values, and go. Each image is the response the request next to it returned, watermark included, on the free keyless tier.
Highlight a few regions
The simplest useful request. You name regions and give each one a color. Everything else takes the map default.
{
"mapId": "united-states",
"regions": {
"US-CA": "#1D4ED8",
"US-TX": "#F59E0B",
"New York": "#10B981"
},
"title": {
"text": "Where we operate"
},
"style": {
"labels": {
"show": true
}
},
"output": {
"width": 1200
}
}
curl https://api.ultimaps.com/v1/renders \
-H "Content-Type: application/json" \
-d '{
"mapId": "united-states",
"regions": {
"US-CA": "#1D4ED8",
"US-TX": "#F59E0B",
"New York": "#10B981"
},
"title": {
"text": "Where we operate"
},
"style": {
"labels": {
"show": true
}
},
"output": {
"width": 1200
}
}' \
-o map.png

Map of the United States titled "Where we operate", with California blue, Texas orange and New York green, every other state in the theme default and labeled with its abbreviation
- Region keys are flexible. "US-CA", "California" and "CA" all reach the same region.
- Colors are hex strings. Regions you leave out keep the theme default.
- "style.labels.show" prints every region name. To leave some regions unlabeled, list their keys in "style.labels.hide".
Choropleth from numbers
Give the API raw values and it picks the classes, the colors and the legend. This is the request most people want.
{
"mapId": "united-states",
"choropleth": {
"values": {
"California": 39.5,
"Texas": 30.5,
"Florida": 22.6,
"New York": 19.6,
"Pennsylvania": 13,
"Illinois": 12.5,
"Ohio": 11.8,
"Georgia": 11,
"North Carolina": 10.8,
"Michigan": 10
},
"type": "groups",
"palette": "blues",
"classes": 5,
"method": "quantile",
"noDataColor": "#EEEEEE",
"noData": true,
"format": {
"decimals": 1,
"suffix": "M"
}
},
"legend": {
"position": "left",
"title": "Population (millions)"
},
"title": {
"text": "Population by state, 2025"
},
"style": {
"labels": {
"show": true,
"content": "value"
}
},
"output": {
"width": 1600,
"scale": 1
}
}
curl https://api.ultimaps.com/v1/renders \
-H "Content-Type: application/json" \
-d '{
"mapId": "united-states",
"choropleth": {
"values": {
"California": 39.5,
"Texas": 30.5,
"Florida": 22.6,
"New York": 19.6,
"Pennsylvania": 13,
"Illinois": 12.5,
"Ohio": 11.8,
"Georgia": 11,
"North Carolina": 10.8,
"Michigan": 10
},
"type": "groups",
"palette": "blues",
"classes": 5,
"method": "quantile",
"noDataColor": "#EEEEEE",
"noData": true,
"format": {
"decimals": 1,
"suffix": "M"
}
},
"legend": {
"position": "left",
"title": "Population (millions)"
},
"title": {
"text": "Population by state, 2025"
},
"style": {
"labels": {
"show": true,
"content": "value"
}
},
"output": {
"width": 1600,
"scale": 1
}
}' \
-o map.png

Choropleth map of US state population in 2025, shaded across five blue quantile classes with the break labels in a legend and each value printed in millions on its state
- Leave out "type", "classes" and "method" and the API detects them from your data.
- "palette" takes any of the 26 built-in palettes. "noDataColor" paints regions your data does not cover, and "noData" adds a "No data" row for them to the legend.
- "legend.title" names what the colors measure. Give every data legend one.
- "format" controls how the numbers print in the legend and on the map, not the image format.
- "style.labels.content" is "value" here. "name_value" prints the state name with its number below it.
Pins
Latitude and longitude markers. Pins compose with everything else, so you can drop them on a choropleth or on a plain map.
SVG output needs a Pro key. Drop "format" for PNG on any tier.
{
"mapId": "united-states",
"style": {
"theme": "paper",
"defaultRegionColor": "#F1F5F9"
},
"locations": [
{
"title": "Austin HQ",
"lat": 30.2672,
"lon": -97.7431,
"color": "#1D4ED8"
},
{
"title": "Denver",
"lat": 39.7392,
"lon": -104.9903,
"labelPosition": "right"
},
{
"title": "Seattle",
"lat": 47.6062,
"lon": -122.3321
}
],
"output": {
"width": 1400,
"format": "svg"
}
}
curl https://api.ultimaps.com/v1/renders \
-H "Content-Type: application/json" \
-d '{
"mapId": "united-states",
"style": {
"theme": "paper",
"defaultRegionColor": "#F1F5F9"
},
"locations": [
{
"title": "Austin HQ",
"lat": 30.2672,
"lon": -97.7431,
"color": "#1D4ED8"
},
{
"title": "Denver",
"lat": 39.7392,
"lon": -104.9903,
"labelPosition": "right"
},
{
"title": "Seattle",
"lat": 47.6062,
"lon": -122.3321
}
],
"output": {
"width": 1400,
"format": "svg"
}
}' \
-o map.svg

Shown as PNG — the request asks for SVG. Same map either way.
- Each pin takes its own color, label side and label visibility.
- Pins are placed by coordinates. The API does not geocode addresses.
SVG needs a Pro key. The keyless GET path returns PNG only.
Check a request before you render it
Dry run returns JSON instead of an image: which of your keys matched, which did not, what got corrected and what the breaks came out as. It costs no quota.
{
"mapId": "united-states",
"choropleth": {
"values": {
"Calfornia": 39.5,
"Texas": 30.5,
"Florida": 22.6,
"Atlantis": 1
}
},
"dryRun": true
}
curl https://api.ultimaps.com/v1/renders \
-H "Content-Type: application/json" \
-d '{
"mapId": "united-states",
"choropleth": {
"values": {
"Calfornia": 39.5,
"Texas": 30.5,
"Florida": 22.6,
"Atlantis": 1
}
},
"dryRun": true
}'
- The typo "Calfornia" comes back corrected to California. "Atlantis" comes back unmatched.
- Use this while you wire up your data, then flip "dryRun" off.
Open the dry-run JSON this returns
Fail on bad keys instead of guessing
By default unmatched keys are skipped. Set "onUnmatched" to "error" and the API returns a 400 with per-key suggestions, which is what you want in a scheduled job.
{
"mapId": "united-states",
"choropleth": {
"values": {
"California": 39.5,
"Texassss": 30.5,
"Atlantis": 1
}
},
"onUnmatched": "error"
}
curl https://api.ultimaps.com/v1/renders \
-H "Content-Type: application/json" \
-d '{
"mapId": "united-states",
"choropleth": {
"values": {
"California": 39.5,
"Texassss": 30.5,
"Atlantis": 1
}
},
"onUnmatched": "error"
}' \
-o map.png
- The 400 is an RFC 9457 problem document. Branch on "code", not on the message.
Full field reference, including all 26 palettes, the four break methods, themes, extra layers and number formatting: the API reference.
Maps you can render
189 maps, from world and continent maps down to US counties and ZIP code areas. The mapId is the map's slug on this site, and it never changes once it is published.
canada europe france-departments india world united-arab-emirates united-kingdom-counties united-states-canada united-states
Keys and limits
A key raises the rate limits and the canvas size. A Pro key removes the watermark and unlocks SVG. Create one in Studio under Workspace, then API. Keys are shown once.
curl https://api.ultimaps.com/v1/renders \
-H "Authorization: Bearer $ULTIMAPS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mapId": "united-states",
"choropleth": {
"values": {
"California": 39.5,
"Texas": 30.5,
"Florida": 22.6,
"New York": 19.6,
"Pennsylvania": 13,
"Illinois": 12.5,
"Ohio": 11.8,
"Georgia": 11,
"North Carolina": 10.8,
"Michigan": 10
},
"type": "groups",
"palette": "blues",
"classes": 5,
"method": "quantile",
"noDataColor": "#EEEEEE",
"noData": true,
"format": {
"decimals": 1,
"suffix": "M"
}
},
"legend": {
"position": "left",
"title": "Population (millions)"
},
"title": {
"text": "Population by state, 2025"
},
"style": {
"labels": {
"show": true,
"content": "value"
}
},
"output": {
"width": 1600,
"scale": 1
}
}' \
-o map.png
| Tier | Auth | Formats | Attribution | Canvas | Rate limit | Monthly |
|---|---|---|---|---|---|---|
| Keyless | none | PNG | full watermark | ≤ 1600 px, scale 1 | 30/hour per IP, burst 5/min | no monthly cap |
| Free key | Bearer um_live_… | PNG | full watermark | ≤ 1600 px, scale ≤ 2 | 10/min, 50/day | 500 renders |
| Pro key | Bearer um_live_… | PNG, SVG | none | ≤ 4000 px, scale ≤ 4 | 30/min, 1,000/day | 5,000 renders |
Monthly quota is a billing state and returns 402, never retryable. Rate and concurrency limits return 429 with Retry-After. Dry runs never consume quota. Check GET /v1/usage for where you stand.
From ChatGPT, Claude or any MCP client
Use this API from ChatGPT, Claude, Cursor or any MCP client and get the map back in the chat. Add https://api.ultimaps.com/mcp by URL, or run the @ultimaps/mcp package on your computer. Neither needs an account.
Set up the Ultimaps MCP server
Not in v1
v1 renders images. It does not do any of this:
- Publishing interactive or embeddable maps
- PDF output
- Geocoding addresses to coordinates
- Reading back the geometry behind a map
If you need one of these, tell us which and we will let you know when it exists. What people ask for here is what we build next.
Reference
API reference
Every endpoint and field, live against the running API.
Error codes
Every code, its HTTP status, and whether to retry it.
openapi.json
OpenAPI 3.1 contract. Generate a client from it.
llms-full.txt
The whole API as one plain-text file for coding agents.
MCP server
Add https://api.ultimaps.com/mcp by URL, or run @ultimaps/mcp locally. No account needed.
Frequently Asked Questions
Is there a choropleth API?
Yes, that is the main thing this API does. Post a set of region keys and numbers and you get back a classified, colored, legended map as a PNG. The API picks the break method, the class count and the palette from your data unless you set them yourself.
How do I generate a map image from a URL?
Put your request JSON in the spec query parameter of GET /v1/renders and the response is the PNG itself. That URL works in an img tag, a markdown image, a Notion image block or a Google Sheets IMAGE() formula, with no key and no account.
Can I use the map image API without an API key?
Yes. The keyless tier renders PNG up to 1600 by 1600 pixels at 30 renders per hour per IP, with an Ultimaps watermark. A key raises the limits, and a Pro key removes the watermark and adds SVG.
Is this a county map API? Can I get county boundaries out of it?
It renders county maps as images, including all 3,143 US counties, but it does not serve boundary geometry. If you need GeoJSON or shapefiles to process yourself, use Census TIGER or Natural Earth instead. This API returns pictures.
Does it geocode addresses?
No. Pins are placed by latitude and longitude, and region colors are matched by region key or name. Geocoding is a Studio feature, not an API one.
Is there an MCP server?
Yes. Add https://api.ultimaps.com/mcp by URL in ChatGPT, Claude or any MCP client, or run the @ultimaps/mcp package locally, and your assistant gets render_map, list_maps and get_map_regions with no account needed. Setup steps for each client are at ultimaps.com/mcp/.
Can I get SVG instead of PNG?
Yes, with a Pro key. Set output.format to svg. Keyless and free keys return PNG.
What happens if my region names do not match?
Keys are matched case-insensitively against region codes, titles, common aliases and normalized titles, so US-CA, California and CA all reach the same region, and unambiguous typos are corrected and reported. By default unmatched keys are skipped and reported in a response header. Set onUnmatched to error and the request fails with per-key suggestions instead.
How do I put a map in a GitHub README?
Use the keyless GET URL as a markdown image. GitHub proxies it through Camo, and because the API sends a 24 hour cache header the image refreshes daily rather than freezing.
Can I render a map server side?
Yes. Every render happens on our servers, so there is no browser, no headless Chrome and no map library in your stack. A single HTTP call returns the finished image.