# Tile providers

How to point StaticMaps at tile providers other than OpenStreetMap, and what each one's
terms allow. Every entry is a `StaticMaps.Source.Template` configuration you can paste
into `source:`.

Last checked against each provider's own documentation on 2026-09-29. Providers change
their URLs, limits and terms; read the linked terms before you depend on one. Where a
detail could not be confirmed from the provider's documentation, the entry says to check
it rather than guessing.

## Read this first: two licence traps

**Server-side caching.** StaticMaps keeps fetched tiles in its tile cache by default.
MapTiler, Stadia Maps, CARTO and Mapbox forbid storing their tiles on a server, and
Thunderforest forbids caching proxies. For those providers, set `cache: false`, and
every render fetches its tiles fresh:

```elixir
source: {StaticMaps.Source.Template, url: "…", attribution: "…", cache: false}
```

**Static images.** Rendering tiles into an image on your server and serving that image is
itself restricted by some providers. MapTiler and Mapbox forbid serving "a screenshot or
other static image" in place of accessing their API, and Stadia Maps allows saving and
redistributing rendered images only on a paid plan. `cache: false` does not fix this.
You need a plan or licence that covers static maps.

The providers without either restriction are the community OpenStreetMap servers below
and your own tiles, and those ask for light use only.

## Attribution

Each entry gives the credit the provider requires, which goes in the source's
`:attribution` and is drawn on every image. Most providers also require links, such as
to `openstreetmap.org/copyright`. An image cannot carry a link, so put the linked credit
next to the image on your page as well.

## Commercial providers

### MapTiler Cloud

```elixir
source: {StaticMaps.Source.Template,
         url: "https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}{r}.png?key=YOUR_KEY",
         attribution: "© MapTiler © OpenStreetMap contributors",
         cache: false}
```

- **Tiles:** 512 px by default; the `/256/` path segment serves 256 px, and `@2x` gives
  high-density tiles. Maximum zoom: check MapTiler's docs.
- **Account:** key required. The free plan is for non-commercial use and development.
- **Licence:** forbids server-side caching and serving static images instead of the API.
  The free plan also requires the MapTiler logo.
- [Terms](https://www.maptiler.com/terms/cloud/) ·
  [Tile API](https://docs.maptiler.com/cloud/api/maps/) ·
  [Attribution](https://docs.maptiler.com/guides/map-design/attribution/add-attribution/)

### Stadia Maps

Includes the Stamen styles Stadia hosts.

```elixir
source: {StaticMaps.Source.Template,
         url: "https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.png?api_key=YOUR_KEY",
         attribution: "© Stadia Maps © OpenMapTiles © OpenStreetMap",
         zoom: 0..20,
         cache: false}
```

- **Styles:** `alidade_smooth`, `alidade_smooth_dark`, `stamen_toner`, `stamen_terrain`,
  `stamen_watercolor` and others. Stamen styles add "© Stamen Design" to the credit.
  Watercolor uses `.jpg`, goes to zoom 16, and is credited
  "© Stadia Maps © Stamen Design © OpenStreetMap".
- **Tiles:** 256 px; `@2x` gives 512 px. Most styles go to zoom 20. An EU endpoint is
  `tiles-eu.stadiamaps.com`.
- **Account:** key required for server use. The key can also go in an
  `Authorization: Stadia-Auth YOUR_KEY` header, through a source that implements
  `headers/2`. The free tier is for non-commercial or evaluation use.
- **Licence:** forbids proxying and server-side caching. Saving and redistributing
  rendered images needs a paid plan.
- [Terms](https://stadiamaps.com/terms-of-service/) ·
  [Raster tiles](https://docs.stadiamaps.com/raster/) ·
  [Attribution](https://docs.stadiamaps.com/attribution/)

### Thunderforest

```elixir
source: {StaticMaps.Source.Template,
         url: "https://api.thunderforest.com/cycle/{z}/{x}/{y}{r}.png?apikey=YOUR_KEY",
         attribution: "Maps © Thunderforest, Data © OpenStreetMap contributors",
         zoom: 0..22,
         cache: false}
```

- **Styles:** `cycle`, `transport`, `landscape`, `outdoors`, `atlas` and others.
- **Tiles:** `@2x` for high density; zoom 0–22. The base tile size is not stated in the
  docs; 256 px is the usual size, but check before relying on it.
- **Credit:** must name both Thunderforest and OpenStreetMap contributors, with links to
  thunderforest.com and openstreetmap.org/copyright. The wording above is one way to do
  that; Thunderforest does not prescribe an exact string.
- **Account:** key required. The free Hobby plan allows 150,000 tiles a month.
- **Licence:** "Caching proxies or other redistribution methods are not permitted", and
  pre-downloading needs an appropriate plan.
- [Terms](https://www.thunderforest.com/terms/) ·
  [Tile API](https://www.thunderforest.com/docs/map-tiles-api/)

### Mapbox Static Tiles

```elixir
source: {StaticMaps.Source.Template,
         url: "https://api.mapbox.com/styles/v1/mapbox/streets-v12/tiles/512/{z}/{x}/{y}{r}?access_token=YOUR_TOKEN",
         attribution: "© Mapbox © OpenStreetMap",
         tile_size: 512,
         zoom: 0..22,
         cache: false}
```

- **Tiles:** 512 px, or 256 px at four times the request count; `@2x` doubles them.
  Zoom 0–22. The Mapbox Standard styles are not available through this API.
- **Account:** token required. There is a monthly free allowance; check Mapbox's pricing
  page for the current figure.
- **Licence:** caching is allowed only on an end user's device, and serving "a
  screenshot or other static image" instead of the API is forbidden. The Mapbox logo is
  also required.
- [Product terms](https://www.mapbox.com/legal/product-terms) ·
  [Static Tiles API](https://docs.mapbox.com/api/maps/static-tiles/) ·
  [Attribution](https://docs.mapbox.com/help/dive-deeper/attribution/)

### CARTO basemaps

```elixir
source: {StaticMaps.Source.Template,
         url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png?key=YOUR_KEY",
         subdomains: ~w(a b c d),
         attribution: "© OpenStreetMap, © CARTO",
         zoom: 0..20,
         cache: false}
```

- **Styles:** `light_all` (Positron), `dark_all` (Dark Matter), `rastertiles/voyager`,
  and `_nolabels` and `_only_labels` variants.
- **Tiles:** `@2x` for high density; zoom 0–20. The base tile size is not stated; check
  CARTO's docs.
- **Account:** a key is now required; older guides show keyless URLs that no longer
  apply. Free up to a monthly tile allowance, which is lower for commercial use.
- **Licence:** credit must be prominent on each map. Server-side caching and proxying are
  forbidden.
- [Terms](https://carto.com/legal/basemap-terms/) ·
  [Styles](https://github.com/CartoDB/basemap-styles) ·
  [Keys](https://carto.com/basemaps/apikey/)

### Esri (ArcGIS)

Esri's servers put the row before the column: `{z}/{y}/{x}`. `Source.Template` handles
that, since placeholders can go in any order.

```elixir
source: {StaticMaps.Source.Template,
         url: "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
         attribution: "Source: Esri, Vantor, Earthstar Geographics, and the GIS User Community",
         zoom: 0..23}
```

- **Services:** the long-standing `World_Imagery` and `World_Street_Map` services serve
  256-px tiles. `World_Street_Map` is no longer updated. The current product, the Static
  Basemap Tiles service, serves 512-px tiles at zoom 0–22 and needs an ArcGIS Location
  Platform account; how its token is passed is not covered in the docs this guide was
  checked against, so read its API reference.
- **Licence:** the Esri Master License Agreement. The service pages say the layers are
  "not intended to be used to export tiles for offline" use. Whether the older services
  need a key, and what their terms say about server-side rendering, is not clear from
  their pages; confirm with Esri before production use.
- [World Imagery](https://goto.arcgisonline.com/maps/World_Imagery) ·
  [Static Basemap Tiles](https://developers.arcgis.com/rest/static-basemap-tiles/)

## Community servers

These run on donated or community resources, need no key, and allow server-side
rendering with caching. They ask for light use and offer no uptime guarantee. The
OpenStreetMap Foundation's tile policy covers only `tile.openstreetmap.org`, but the same
courtesy applies: an identifying `User-Agent`, which StaticMaps sends, honouring cache
headers, which the tile cache does, and no bulk downloading.

### OpenStreetMap France and Humanitarian (HOT)

```elixir
source: {StaticMaps.Source.Template,
         url: "https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png",
         subdomains: ~w(a b c),
         attribution: "données © OpenStreetMap/ODbL - rendu OSM France",
         zoom: 0..20}

source: {StaticMaps.Source.Template,
         url: "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
         subdomains: ~w(a b c),
         attribution: "données © OpenStreetMap/ODbL - Tiles courtesy of Humanitarian OpenStreetMap Team",
         zoom: 0..19}
```

- **Credit:** must be visible in the map corner, with a link to
  `https://osm.org/copyright`.
- **Licence:** "Any abusive usage may result in traffic limitation or blocking."
- [Usage](https://www.openstreetmap.fr/fonds-de-carte/)

### OpenTopoMap

```elixir
source: {StaticMaps.Source.Template,
         url: "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png",
         subdomains: ~w(a b c),
         attribution: "Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © OpenTopoMap (CC-BY-SA)",
         zoom: 0..15}
```

- **Zoom:** the maximum is not stated reliably; zoom 16 and 17 are reported as
  unreliable, so the configuration above stops at 15. Raise it if it works for you.
- **Licence:** free, including commercial use, with attribution and share-alike, as long
  as the server is not overloaded by bulk downloads. The credit above is the German
  original from OpenTopoMap's about page.
- [About and terms](https://opentopomap.org/about)

### CyclOSM

```elixir
source: {StaticMaps.Source.Template,
         url: "https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png",
         subdomains: ~w(a b c),
         attribution: "CyclOSM & OSM-FR, © OpenStreetMap contributors",
         zoom: 0..20}
```

- **Credit:** OpenStreetMap contributors, and ideally CyclOSM and OSM-FR. The exact
  wording is not prescribed.
- **Licence:** reusable under the OpenStreetMap tile usage policy.
- [CyclOSM](https://www.cyclosm.org/) ·
  [Tile policy](https://operations.osmfoundation.org/policies/tiles/)

## Your own tile servers

Several servers serving identical tiles are mirrors: pass a list of templates, and each
tile goes to a mirror chosen from its position, falling back to the next mirror if that
one fails.

```elixir
source: {StaticMaps.Source.Template,
         url: ["https://tiles1.example.com/{z}/{x}/{y}.png",
               "https://tiles2.example.com/{z}/{x}/{y}.png"],
         attribution: "© OpenStreetMap contributors",
         max_concurrency: 16}
```

`:max_concurrency` is the limit across all the mirrors together, so no single server
ever sees more than that many requests, even while another mirror is down and its tiles
go elsewhere. Mirrors must serve the same tiles; to fall back to a different provider,
list it as a fallback source instead: `source: [primary, fallback]`.
