---
title: "Render a map (keyless, cacheable URL)"
method: GET
path: "/v1/renders"
tags: ["Renders"]
---

# Render a map (keyless, cacheable URL)

`GET /v1/renders`

## Request

- Base URL: `https://api.ultimaps.com`
- URL: `https://api.ultimaps.com/v1/renders`
- Auth: none required

The keyless twin of `POST /v1/renders`, for `<img>` tags, Markdown and
no-code tools. Same request object, URL-encoded as JSON in `spec`.
Always the keyless tier (PNG, full attribution, ≤ 1600 px wide) and
publicly cacheable for 24 hours (`Cache-Control: public, max-age=86400`),
so repeat loads of an embedded map do not count against the limit.
Because the cache key is the URL, a *changed* spec is a new URL. For
dashboards that re-render on fresh data that is exactly what you want.

This endpoint never accepts a key. Sending one returns `400`. Use POST
with your key instead.

An oversized, missing, or malformed `spec` is always
`400 validation_error` (never `413`).

```
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%7D%7D
```

## Query parameters

- `spec` string, required

## Response `200`

The rendered PNG (or the dry-run report when `spec` sets `dryRun: true`). The image carries `Content-Disposition: inline; filename="<mapId>.png"`. The dry-run report does not.

- DryRunResponse
  - `dryRun` true, required
  - `mapId` string, required
  - `choropleth` object, nullable: The resolved visualization plan. Present in choropleth mode, null otherwise. Under `type: auto` this is what the detection picked. `classes` is the effective count, since tied data collapses duplicate edges even for explicit requests.
    - `type` 'gradient' | 'steps' | 'groups'
    - `method` 'quantile' | 'equalInterval' | 'jenks' | 'pretty'
    - `classes` integer
    - `palette` string
    - `midpoint` number: The value the neutral color sits on, present when the palette is centered: the requested `midpoint`, or 0 for diverging data around zero. A diverging palette on one-sided data without a `midpoint` has none.
    - `reason` string: Why this type was picked, e.g. "Highly skewed data - steps show distribution better".
    - `breaks` number[]: The class edges actually drawn (`classes + 1` values, fewer if tied data collapsed edges).
    - `format` FormatSpec: Structured number format (Intl-backed; en-US unless `locale` is set). Field exclusivity: `preset` and `d3` are each mutually exclusive with every other field except `locale`, `decimals` and `significantDigits` are mutually exclusive, and `currency` requires `style: "currency"`. Examples: `{"decimals": 0, "suffix": " km²"}` gives "1,234 km²", `{"compact": true, "decimals": 1, "trim": true}` gives "39.5M", `{"style": "percent", "decimals": 1}` turns 0.423 into "42.3%".
      - `preset` 'auto' | 'plain' | 'compact' | 'percent' | 'currency': Alias for the bare-string preset form.
      - `style` 'decimal' | 'percent' | 'currency': "percent" MULTIPLIES BY 100. The input is a fraction (0.42 prints as "42%"). Data already in percent points should use `suffix: "%"` instead.
      - `currency` string: ISO-4217 code, rendered with its Intl symbol ("€", "CA$"). Only valid with style "currency".
      - `compact` boolean: Compact notation: K/M/B/T (never SI "G").
      - `decimals` integer: Fraction digits. Min equals max, for uniform labels, unless `trim` is set.
      - `significantDigits` integer
      - `trim` boolean: Strip trailing zeros.
      - `grouping` boolean: Thousands separators.
      - `sign` 'auto' | 'always' | 'exceptZero': Maps 1:1 to Intl signDisplay. "exceptZero" is the usual choice for diverging legends.
      - `prefix` string: Literal, inserted AFTER any leading sign ("-€1,234", never "€-1,234").
      - `suffix` string: Literal, e.g. " per km²".
      - `d3` string: Escape hatch: a raw d3-format spec. Invalid grammar is a 400.
      - `locale` 'en-US' | 'de-DE' | 'fr-FR' | 'es-ES' | 'it-IT' | 'pt-BR' | 'de-CH' | 'en-IN' | 'nl-NL' | 'pl-PL' | 'sv-SE': Number style: separators, currency placement and compact suffixes. `{"decimals": 1, "locale": "de-DE"}` turns 1234567.5 into "1.234.567,5"; with `compact: true` it gives "1,2 Mio.". Combines with `preset` and `d3`; on its own (`{"locale": "de-DE"}`) it is the automatic format in that number style.
    - `regionsWithoutData` integer: Regions of the map with no value, drawn in the no-data fill. Above 0, send `choropleth.noData: true` so the legend explains them.
  - `legend` object, nullable: Exactly the legend the render would draw. Its shape follows the resolved type. groups/steps choropleths and categories return an array of `{label, color}` (steps labels are the class boundaries, groups labels are ranges). A gradient returns `{points: [min, mid|null, max], stops: [hex…]}`, the continuous scale's domain and preview colors. With `choropleth.noData` the No data row is the last item (a gradient adds it as `noData: {label, color}`). Null for regions-only requests.
  - `warnings` object[]: Non-fatal request adjustments (same objects as the `X-Ultimaps-Warnings` header on image responses).
    - `code` 'legend_position_coerced' | 'percent_values_look_scaled' | 'legend_overflow' | 'layer_unavailable' | 'labels_unavailable' | 'midpoint_ignored' | 'nodata_low_contrast' | 'legend_heading_dropped' | 'labels_overlap' | 'label_lists_ignored': Non-fatal request adjustment. Additive-only: codes may be appended, never renamed or removed.
    - `message` string
    - `count` integer: `labels_overlap` only: region labels still overlapping.
    - `shortened` integer: `labels_overlap` only: labels shortened.
    - `keys` string[]: `labels_overlap` only: first overlapping region keys (up to 10), in map order.
  - `regionMatching` RegionMatching, required
    - `matchedKeys` integer, required: Input keys that resolved, summed across the request's choropleth/categories/regions blocks and the style.labels.hide list. A region referenced by two keys counts twice (the later value wins). This counts keys, not distinct regions.
    - `corrected` object[], required
      - `input` string
      - `matchedTo` string: The published `key` of the region the input was corrected to.
      - `title` string
      - `via` string
    - `unmatched` object[], required
      - `input` string
      - `suggestions` string[]: Closest regions as `"Title (key)"`, or just the title on maps whose key is the title.

## Other responses

- `400`: The request failed validation.
- `403`: The request exceeds what the caller's tier allows: SVG without a Pro key, a keyless request over 1600×1600 px at scale 1, or a free-key request over 1600×1600 px at scale 2. It costs no quota. Carries upgrade_url and, for keyless callers, signup_url.
- `404`: The mapId does not exist (or is not accessible).
- `429`: Rate or concurrency limit hit. Retryable, so honor Retry-After. It also covers the daily per-key cap, where Retry-After points to midnight UTC.
- `500`: Unexpected server error. Safe to retry with backoff.
- `502`: The render engine failed. Retrying may help.
- `504`: The render exceeded the 30-second budget. Retryable.

## Changes

- **2026-10-20** `b913e49453e5`: 1 info
  - added the optional property `choropleth/format` to the response with the `200` status
- **2026-10-20** `b2539061d706`: 5 warning, 5 info
  - added the new `label_lists_ignored` enum value to the `warnings/items/code` response property for the response status `200`
  - added the new `labels_overlap` enum value to the `warnings/items/code` response property for the response status `200`
  - added the new `legend_heading_dropped` enum value to the `warnings/items/code` response property for the response status `200`
  - added the new `midpoint_ignored` enum value to the `warnings/items/code` response property for the response status `200`
  - …6 more

[Operation history](https://skmtc.dev/ultimaps/apis/ultimaps-public-api/changes/v1/renders/get.md)

---

[API](https://skmtc.dev/ultimaps/apis/ultimaps-public-api.md) · [All operations](https://skmtc.dev/ultimaps/apis/ultimaps-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ultimaps/apis/ultimaps-public-api/revisions/b913e49453e5?raw)
