# Stadia Maps Documentation
Stadia Maps APIs provide a variety of geospatial functionality including routing, forward and reverse geocoding, place search, and time (zone and current) information. SDKs are available for many popular programming languages to make the data APIs easier to use. Stadia Maps also offers map solutions in the form of vector tiles (for smoother client-side rendering), raster tiles (easier on the client, but scale-dependent), and static map images which show an area and optional overlays. Most APIs are available via a free tier, but some require a paid subscription. Refer to https://stadiamaps.com/pricing for current prices. This file contains links to the most popular sections of the Stadia Maps developer documentation.
# General Info
# Authentication
This page covers everything you need to know to access map tiles and APIs from Stadia Maps.
## Choosing the right authentication method
| Use case | Recommended method | | --- | --- | | Local development (`localhost`) | No authentication needed | | Production websites | [Domain-based authentication](#domain-based-authentication) | | Mobile apps (iOS, Android) | [API key](#api-keys) | | Server-side applications | [API key](#api-keys) | | Desktop applications | [API key](#api-keys) | | Intranet websites | [API key](#api-keys) |
For production websites, domain-based authentication is the most secure option. It requires no code changes and prevents API key exposure. For everything else, use an API key and follow our [security best practices](../guides/securing-your-integration/).
## Local development
We've tried to make things as easy as possible for local web development. As long as you're running via a development server accessed via `localhost` or `127.0.0.1`, you don't need an API key! So your React, Vue, and even local WordPress installations will "just work" out of the box.
Local development rate limits
Requests made this way are subject to strict rate limits. If you start receiving HTTP 429 responses regularly, sign up for an account (it's free!) and create an [API key](#api-keys).
## Domain-based authentication
Domain-based authentication is the easiest form of authentication for production web apps. No additional application code is required, and you don't need to worry about anyone scraping your API keys. We recommend this for most browser-based applications.
1. Sign in to the [client dashboard](https://client.stadiamaps.com/dashboard/).
1. Click "Manage Properties."
1. Under "Authentication Configuration," click the button to add your domain.
Domain-based authentication works by validating the `Origin` and `Referer` headers that browsers automatically send with every request. These headers are set by the browser itself, not by JavaScript or your application code, making this method both secure and convenient for web applications.
Getting a 401 Unauthorized response or seeing a Stamen warning tile?
Domain authentication usually "just works" but sometimes it doesn't. These are a few common issues related to domain authentication.
- [`Referrer-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy) is set to `no-referrer`. Some platforms, such as Azure Static Web Apps, do this by default. Relaxing this header to something like `strict-origin-when-cross-origin` (if your site uses HTTPS; `origin-when-cross-origin` if using HTTP) will let us authenticate your requests while preserving privacy.
- Are you using a hosting service like Vercel, Netlify, or GitHub pages with a custom domain? In many configurations, the headers will indicate that the request is coming from a subdomain on your host rather than your custom domain. Check the `Origin` and `Referer` headers in your browser's web inspector/dev tools to verify.
- Is your page hosted on a subdomain? It's easy to forget to include the subdomain or (if appropriate) allow all subdomains. If your site is hosted on `a.b.example.com`, you need to set the subdomain to `a` and the domain to `b.example.com` as shown in the screenshot below:
## API keys
To access maps and APIs outside a web browser, including server-side code and mobile apps, you need an API key.
### Generating and revoking API keys
You can manage your API keys in the client dashboard.
1. Sign in to the [client dashboard](https://client.stadiamaps.com/dashboard/). (If you don't have an account yet, sign up for free; no credit card required!)
1. Click "Manage Properties."
1. If you have more than one property (ex: for several websites or apps), make sure you have selected the correct property from the dropdown at the top of the page.
1. Under "Authentication Configuration," you can generate, view or revoke your API key.
#### Video: How to generate your API key
### Using your API key
You can authenticate your requests by adding an API key to the query string or HTTP header.
The simplest is to add a query string parameter `api_key=YOUR-API-KEY` to your request URL. For example, to access the `/tz/lookup` API endpoint, your request URL might look like this.
Example URL with an API key placeholder
```
https://api.stadiamaps.com/tz/lookup/v1?lat=59.43696&lng=24.75357&api_key=YOUR-API-KEY
```
You can also use an `Authorization` HTTP header instead of a query string as shown below. Don't forget the `Stadia-Auth` prefix!
Example Authorization header with an API key placeholder
```
Authorization: Stadia-Auth YOUR-API-KEY
```
Vector stylesheets
If you're accessing vector tiles via API key (ex: in a mobile app) and you're using one of our styles, add an API key query parameter to the stylesheet URL. We'll rewrite the stylesheet so that all data sources requiring authentication use your key. This is the easiest way to get access to vector tiles in a mobile app or intranet website.
If you're using your own style with our data, see our [custom style guide](../custom-styles/) for tips on authenticating our data sources.
### Protecting your API keys
You should take care not to expose your API key unnecessarily. That's why we recommend domain-based authentication for web browser applications. We recommend only using API keys in cases where it is not likely to be leaked to an end user (ex: server-side and mobile applications).
If you are developing locally and exceed the rate limit for keyless access, be careful not to accidentally commit your API key to your repository.
Securing your API keys in apps
Securing your API keys requires special care whenever an application is available to end users outside your organization. This is always tricky and tends to be a bit different for every app, but we can offer some general best practices:
1. Avoid shipping API keys in an app binary when possible. For example, on iOS you can take advantage of on-demand resources to separate this from the IPA download.
1. Ensure that any long-term storage of API keys uses the platform's hardware-secured APIs (ex: Keychain or Android Keystore).
1. Rotate your API keys periodically. You can do this manually in the dashboard, or programmatically via our [Account Management API](../client-api/) if you have access.
1. Monitor your usage in the client dashboard and investigate any unexpected spikes.
For more detailed guidance, see our [Securing Your Integration](../guides/securing-your-integration/) guide.
## Next steps
- [Securing Your Integration](../guides/securing-your-integration/) — Best practices for mobile apps, server-side applications, and understanding our security model
- [Service Limits](../limits/) — Rate limits and usage tiers
- [Account Management API](../client-api/) — Programmatic management of properties, domains, and API keys
# EU Endpoints
Our [commitment to privacy](https://stadiamaps.com/private-location-apis-maps/) has been a foundational value from very beginning, and we take extreme care to ensure the privacy of our customers and users. While we are confident that our privacy practices and procedures satisfy GDPR requirements by default, we recognize that some customers prefer to guarantee that all network requests be handled by servers within the European Union.
To accommodate this preference, we offer EU-only endpoints for our maps and APIs. These endpoints ensure users always connect to EU-based servers. Our systems scrub PII at the network's edge, so any requests that must be ultimately handled by internal services in other regions are managed in a completely private manner.
## Using the EU Endpoints
Info
By utilizing EU endpoints, you will establish connections exclusively with two of our servers strategically positioned in the European Union: Frankfurt, Germany and Paris, France.
To use the EU endpoints, substitute the endpoints in our documentation as shown below.
### Map Endpoints
For map tiles and services, replace:
```
https://tiles.stadiamaps.com
```
with
```
https://tiles-eu.stadiamaps.com
```
Example: Using EU Endpoints for MapLibre & Alidade Smooth
```
Vector Map Demo
```
1. This line is the key change; note the `tiles-eu` host.
### API Endpoints
For routing, geocoding, time zone, and other services, replace:
```
https://api.stadiamaps.com
```
with
```
https://api-eu.stadiamaps.com
```
#### Example Code
Here are some code examples of using the EU endpoints with our official SDKs and cURL.
Installation Instructions
The Stadia Maps JavaScript/TypeScript SDK is available for any package manager that supports the npm registry.
```
npm install @stadiamaps/api
```
```
yarn add @stadiamaps/api
```
```
bun add @stadiamaps/api
```
```
import { GeospatialApi, Configuration{{ extra_js_imports }} } from '@stadiamaps/api';
// If you are writing for a backend application or can't use domain-based auth,
// then you'll need to add your API key like so:
//
// const config = new Configuration({ basePath: "https://api-eu.stadiamaps.com", apiKey: "YOUR-API-KEY" }); (1)
const config = new Configuration({ basePath: "https://api-eu.stadiamaps.com" });
const api = new GeospatialApi(config);
const res = await api.tzLookup({ lat: 40.71278, lng: -74.00611 });
```
1. Learn how to get an API key in our [authentication guide](../authentication/).
Installation Instructions
The Stadia Maps Python SDK is available through any package manager that supports PyPi.
```
pip install stadiamaps
```
```
uv add stadiamaps
```
```
poetry add stadiamaps
```
```
import os
import stadiamaps
from stadiamaps.rest import ApiException
configuration = stadiamaps.Configuration(host="https://api-eu.stadiamaps.com")
# Configure API key authentication (ex: via environment variable). (1)
configuration.api_key['ApiKeyAuth'] = os.environ["API_KEY"]
with stadiamaps.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = stadiamaps.GeospatialApi(api_client)
try:
res = api_instance.tz_lookup(40.71278, -74.00611)
except ApiException as e:
# Add your error handling here
print("Exception when calling the Stadia Maps API: %s\n" % e)
```
1. Learn how to get an API key in our [authentication guide](../authentication/).
Installation Instructions
If aren't already using Maven Central, add the repository in your Gradle build script.
```
repositories {
mavenCentral()
}
```
Then, add the API package and its dependencies.
build.gradle.kts
```
dependencies {
val retrofitVersion = "2.12.0"
// API package
implementation("com.stadiamaps:api:7.1.0")
// Dependencies
implementation("com.squareup.moshi:moshi-kotlin:1.15.2")
implementation("com.squareup.moshi:moshi-adapters:1.15.2")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
implementation("com.squareup.retrofit2:converter-moshi:$retrofitVersion")
implementation("com.squareup.retrofit2:converter-scalars:$retrofitVersion")
}
```
build.gradle
```
dependencies {
def retrofitVersion = "2.12.0"
// API package
implementation 'com.stadiamaps:api:7.1.0'
// Dependencies
implementation 'com.squareup.moshi:moshi-kotlin:1.15.2'
implementation 'com.squareup.moshi:moshi-adapters:1.15.2'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
implementation "com.squareup.retrofit2:retrofit:${retrofitVersion}"
implementation "com.squareup.retrofit2:converter-moshi:${retrofitVersion}"
implementation "com.squareup.retrofit2:converter-scalars:${retrofitVersion}"
}
```
Our API package is available on Maven Central. All you need to do is add a few dependencies to your `pom.xml`.
pom.xml
```
2.12.0
com.stadiamaps
api
7.1.0
com.squareup.moshi
moshi-kotlin
1.15.2
com.squareup.moshi
moshi-adapters
1.15.2
com.squareup.okhttp3
logging-interceptor
4.12.0
com.squareup.retrofit2
retrofit
${retrofit.version}
com.squareup.retrofit2
converter-moshi
${retrofit.version}
com.squareup.retrofit2
converter-scalars
${retrofit.version}
```
```
// Imports (at the top of your source file; we've used some wildcard imports for simplicity)
import com.stadiamaps.api.*
import com.stadiamaps.api.auth.ApiKeyAuth
import com.stadiamaps.api.infrastructure.*
import com.stadiamaps.api.models.*
// Set your API key (from an environment variable in this case) (1)
val apiKey = System.getenv("STADIA_API_KEY") ?: throw RuntimeException("API Key not set")
val client = ApiClient(baseUrl = "https://api-eu.stadiamaps.com")
client.addAuthorization("ApiKeyAuth", ApiKeyAuth("query", "api_key", apiKey))
// Configure a service for the group of APIs we want to talk to
val service = client.createService(GeospatialApi::class.java)
// Set up the request.
// Note: this code is blocking for demonstration purposes.
// If you're using Kotlin with coroutines,
// you can also use these asynchronously within suspend functions.
// Synchronous code can enqueue a callback to avoid blocking
// (you'll definitely want to do one of these instead when on the main thread of an app).
// See the docs for details: https://square.github.io/retrofit/2.x/retrofit/retrofit2/Call.html
val res = service.tzLookup(40.71278, -74.00611).execute()
if (res.isSuccessful) {
println("Found result: ${res.body()}")
} else {
println("Request failed with error code ${res.code()}")
}
```
1. Learn how to get an API key in our [authentication guide](../authentication/).
Installation Instructions
Our Swift SDK is distributed using the Swift Package Manager (SPM). Apple's [documentation](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app) shows how to add a Swift Package dependency to your Xcode project. On the Add Package screen, you can find our package by its repository URL: `https://github.com/stadiamaps/stadiamaps-api-swift`.
```
import StadiaMaps
// This setup code can go anywhere before you actually make an API call (typically in your app init)
func setupStadiaMapsAPI() {
// Set your API key (1)
StadiaMapsAPI.customHeaders = ["Authorization": "Stadia-Auth YOUR-API-KEY"]
// Configure the client to use our EU endpoint
StadiaMapsAPI.basePath = "https://api-eu.stadiamaps.com"
}
// This function demonstrates how to call the Stadia Maps API.
// If you have not yet adopted async/await in your Swift codebase, you can use the Task API
// to call async functions in a non-async context: https://developer.apple.com/documentation/swift/task.
func myFunction() async throws {
let res = try await GeospatialAPI.tzLookup(lat: 40.71278, lng: -74.00611)
// Do something with the response...
print(res)
}
```
1. Learn how to get an API key in our [authentication guide](../authentication/).
Installation Instructions
**Composer**
To install the package via [Composer](https://getcomposer.org/), add `stadiamaps/api` to your `composer.json`:
```
{
"require": {
"stadiamaps/api": "5.*"
}
}
```
Then run `composer install`.
**Manual Installation**
You can also download the files manually and include `autoload.php` in your scripts:
```
setApiKey('api_key', getenv('API_KEY'))->setHost('https://api-eu.stadiamaps.com');
$apiInstance = new OpenAPI\Client\Api\GeocodingApi(
new GuzzleHttp\Client(),
$config
);
try {
$result = $apiInstance->tzLookup(40.71278, -74.00611);
// Do something with the response...
print_r($result);
} catch (Exception $e) {
// Add your error handling here
echo 'Exception when calling the Stadia Maps API: ', $e->getMessage(), PHP_EOL;
}
```
1. Learn how to get an API key in our [authentication guide](../authentication/).
```
curl "https://api-eu.stadiamaps.com/tz/lookup/v1?lat=40.71278&lng=-74.00611&api_key=YOUR-API-KEY"
```
# Service Limits
## Which APIs can I access?
Your Stadia Maps account gives access to a subset of APIs based on your selected plan. If you try to access an API which you do not have access to, you will receive a `403 Forbidden` response. Throughout our documentation, we clarify when a feature is not available on all plans using a grid like the one below (indicating that the free tier cannot access a feature):
Free
Starter
Standard
Professional
A full list of features included with each plan is available on our [pricing page](https://stadiamaps.com/pricing/).
## Who can use the free tier?
Our free tier is available for development, evaluation, and non-commercial use (including academic use). Other uses require a paid subscription. See our [FAQ](https://stadiamaps.com/faqs/#acceptable-use) for more details on what uses are deemed to be commercial.
You can [upgrade your free subscription](../upgrading-subscription/) to a [paid plan](https://stadiamaps.com/pricing/) at any time.
## How do I know if I am going to run out of credits?
By default, our system will automatically send an email and show an alert in the [Dashboard](https://client.stadiamaps.com/dashboard/#/overview) when you have exhausted 80% of your credits and are about to be hard limited. To receive these important alerts, please use an email which you check regularly.
## What happens when I run out of credits?
After you use all of your credits for a billing period (calendar month for free accounts), our default action is to hard limit until the start of the next billing cycle or plan upgrade (all APIs will respond with HTTP 429 Rate Limit Exceeded). This protects you from unexpected bills.
If you want to keep serving requests beyond your selected plan's limit, you can enable additional usage in the [change plan](https://client.stadiamaps.com/dashboard/account/change-plan) section of the Dashboard.
This is an opt-in, pay-as-you-go billing option with straightforward pricing on the additional credits. See our [pricing page](https://stadiamaps.com/pricing/) for details.
## How can I add additional alerts based on my credit usage?
You can setup additional alerts as a percentage of credits used based on your plan's included monthly allotment in the [subscription management](https://client.stadiamaps.com/dashboard/account/subscription-details/) section of the Dashboard.
## What are your rate limits?
We reserve the right to impose rate limits on customers who are abusive or disruptive of service. [Contact enterprise sales](mailto:entsales@stadiamaps.com) to discuss higher volume usage.
We also enforce rate limits on [local development](../authentication/#local-development) to prevent abuse. While we do not publish exact limits for local development, it should be usable without issue for most applications. After the limit is exceeded, you will receive HTTP 429 responses for a while. Access via a registered domain or API key is not subject to the same restrictions.
If you expect to be sending requests at a high rate or want to ensure your use case won't be a problem, [send us an email](mailto:support@stadiamaps.com) and our ops team will coordinate with you to ensure smooth service.
Proxying and Bulk Downloading
Note that proxying and bulk downloading/caching (with few exceptions) are prohibited by our [general terms of service](https://stadiamaps.com/terms-of-service). Unless you have an enterprise agreement which allows this, such behavior will typically result in escalating de-prioritization of requests and ultimately a rate limit.
## What limits are there on API call complexity?
We impose the complexity limits on certain APIs. If you have a more demanding use case, [send us a message](mailto:support@stadiamaps.com), and we can discuss your use case. We're happy to discuss higher limits for enterprise customers.
### Standard Routing
The following complexity limits apply to standard routing requests.
| Routing profile/mode of travel | Max locations per route | Max b-line distance between all locations | | --- | --- | --- | | Automobile, bus, truck, taxi, and motorcycle | 50 | 5000 kilometers | | Bicycle, bikeshare, and motor scooter | 50 | 500 kilometers | | Low speed vehicle and pedestrian | 50 | 250 kilometers |
### Optimized Routing
The following limits apply to optimized routing requests.
| Routing profile/mode of travel | Max locations per route | Max b-line distance between all locations | | --- | --- | --- | | Automobile, bus, truck, and taxi | 50 | 400 kilometers | | All others | 50 | 200 kilometers |
### Time/Distance Matrix
We limit time/distance matrix complexity on two dimensions: the number of matrix elements and the straight-line distance between all locations.
Matrix elements are a simple multiplication of the dimensions of the input. For example, a request with 3 sources (origins) and 5 targets (destinations) has 3 x 5 = 15 elements. The size limits depend on your plan.
| Routing profile/mode of travel | Max elements (Standard) | Max elements (Professional) | Max b-line distance between all locations | | --- | --- | --- | --- | | Automobile, bus, truck, and taxi | 625 | 10,000 | 400 kilometers | | All others | 625 | 10,000 | 200 kilometers |
### Bulk Geocoding
Bulk geocoding requests may contain up to 5,000 queries.
# Upgrading Your Stadia Maps Subscription
We know that everyone's location needs are different depending on the project. At Stadia Maps, this is why we offer several options and subscription levels, from free all the way to [custom enterprise plans](mailto:entsales@stadiamaps.com).
By default, when you sign up, you are given a 14-day trial of our Professional tier. After the trial, if nothing is done, your account is converted to our Free level. If you need more features or credits, or are using our services for [commercial purposes](https://stadiamaps.com/faqs#acceptable-use), you will need to upgrade to one of our paid plans.
All of our plan details (credits, features, pricing, and credit schedule) are available on our [pricing page](https://stadiamaps.com/pricing). If you're unsure which plan is best suited for your needs, please [reach out](mailto:support@stadiamaps.com?subject=Help%20with%20choosing%20a%20plan). We are happy to help.
Once you have determined which plan suites your needs and are ready to upgrade, you will need to enter your payment details.
## Adding Payment Information
The first step to upgrading is making sure you have a current, valid payment method associated with your account. If one already exists, you can skip to the [Selecting a New Plan](#selecting-a-new-plan) section.
To add or update your payment method, you will need to head on over to the [*Add Payment Method*](https://client.stadiamaps.com/dashboard/account/add-payment/) section of the *Dashboard*. You can also select *Your Account Details* from the menu, and then click *Update Payment Method* as shown below.
Enter your payment method details, review the information, and once everything is correct, click *Add Payment Method*.
And that's it! With your payment information entered, you can now select a new plan.
## Selecting a New Plan
With a payment method associated with your account, you are now ready to choose your plan. Hop on over to the [*Change Plan*](https://client.stadiamaps.com/dashboard/#/account/change-plan) section of your account to make your selection. You can also click *Manage Subscription* from the *Dashboard* and go straight to *Change Plan*.
We offer three commercial plans: *Starter*, *Standard*, and *Professional*. Each provides their own specific API access, included credits, and additional usage prices.
A short summary of each is provided in this section. Identify the plan you want and click *Select* at the bottom.
Help selecting a plan
If you need help picking your plan, you can review our [pricing page](https://stadiamaps.com/pricing/) for additional details or [write us a note](mailto:support@stadiamaps.com?subject=Help%20with%20upgrading%20my%20account) and we will be happy to assist you.
### Additional Usage
Each plan has an amount of included credits issued every month. By default *Additional Usage* is disabled and your requests will be hard limited once your account hits the plan's credit limit. Don't worry though, you will receive emails as you approach this threshold.
If you think you may require more credits and would like to prevent your account from being hard limited, enable *Additional Usage*. You will automatically be billed on your next invoice for the credits used at the specified rate.
Make changes at any time
You can also enable and disable *Additional Usage* at any time to suit your needs.
After you have made your decision, review your selections, and then click *Preview Changes*.
### Preview Changes
A comparison of your current plan and the selected plan will be displayed. Please verify you are making the desired update and click *Confirm changes*.
Once confirmed, your credit card will be billed immediately (and every month thereafter) until a change is made.
If you are updating from one paid plan to another, the changes will be prorated. See our [billing FAQ](https://stadiamaps.com/faqs/#billing) for more details or [reach out](mailto:support@stadiamaps.com?subject=Proration%20question) to us.
### Upgrading Immediately During Your Trial
If you are currently in a Free trial, you have access to all features of your currently selected plan (the Professional level by default), but have a reduced credit limit. If you need to upgrade your trial to a paid account immediately, follow the steps above to [enter your billing information](#adding-payment-information) and [plan selection](#selecting-a-new-plan).
Once completed, you will see a new *Upgrade Now* button on your [*Dashboard*](https://client.stadiamaps.com/dashboard/#/overview). Clicking this will immediately charge your card on file and convert your account into an active subscription for the selected plan.
## Getting Help
If you experience any issues or have questions, please [reach out](mailto:support@stadiamaps.com?subject=Help%20with%20upgrading%20my%20account). We are happy to help.
# Map Styling
# Custom Map Styling
Want to change the look and feel of the map? Custom styles allow you to change the color scheme of an existing style, [change the language of place labels](../tutorials/changing-the-map-label-language/), and more.
## Vector Maps
One major benefit to vector map tiles is that you can change the style to suit your liking (even dynamically!). Our vector tiles are compatible with the popular [OpenMapTiles schema](https://openmaptiles.org/schema/), and these base layers can be styled to fit a wide range of use cases.
[MapLibre styles](https://maplibre.org/maplibre-style-spec/) are the most common method of styling vector tiles. Other renderers such as [OpenLayers](../vector/#openlayers) and [Tangram](../vector/#tangram) have their own styling tools as well, but we'll only cover GL JSON styles here as they are the most widely used. You can use these styles on the web via [MapLibre GL JS](../tutorials/vector-maps-with-maplibre-gl-js/), and on mobile via either [MapLibre Native](../native-multiplatform/maplibre-gl-native/) or [Flutter MapLibre GL](../native-multiplatform/flutter-maplibre-gl/).
### Modifying an Existing MapLibre Style
Tip
You are generally free to modify [Stadia Maps Styles](../themes/) for your own applications. For specific details per-style, check out our [attribution page](https://stadiamaps.com/attribution/).
We recommend using [Maputnik](https://maplibre.org/maputnik), an open-source style editor, to create and edit MapLibre styles. You can get started modifying one of our styles in three easy steps:
1. Select "Open" from the Maputnik menu bar.
1. Enter the JSON URL for one of our [styles](../themes/).
1. Customize it to your liking!
Once you're happy with the result, save the modified style using the "Export" button in the top menu. If you'll be accessing our map tiles via API key (ex: in a mobile app), be sure to enter your API key in the export dialog. Maputnik will do the rest!
### Hosting or Bundling your Custom Style
Once you're happy with the result, you'll need to host the style somewhere: either on the web (your server, CDN, etc.) or bundled with your app. Then, rather than loading one of our styles via URL, load your own!
Now all you have to do is load your custom style (URL or embedded JSON) instead of ours! (If you don't already have a map in your website or app, check out our documentation on maps for [web](../maps-for-web/) or [mobile, desktop & multi-platform apps](../native-multiplatform/overview/)).
### Advanced: Starting from Scratch or Porting from Another Vendor
If you're migrating from another vendor, the first thing you'll need to know is what schema their vector tiles follow. For OpenMapTiles-based sources, many things will "just work" (though even within OMT, there are a lot of variations and custom layers). For less easy cases, you will need to figure out how to map between their schema and the [OpenMapTiles schema](https://openmaptiles.org/schema/) on your own.
In addition to vector and satellite imagery layers, we also offer global elevation and other datasets. You can find these on our [additional data tilesets](../tilesets/) page, along with documentation and URLs for access.
Here is a minimal skeleton on which to build your own style, pre-filled with the URLs for our OpenMapTiles-compatible source, sprites (you can substitute `alidade_smooth` for any of our [map styles](../themes/)), and glyphs (fonts).
Tip
If you are not using [domain-based auth](../authentication/#domain-based-authentication) (ex: for a mobile app), you will need to add an API key to the OpenMapTiles URL query string: `https://tiles.stadiamaps.com/data/openmaptiles.json?api_key=YOUR-API-KEY`
If you need to always use EU servers for regulatory reasons, change the host to `tiles-eu.stadiamaps.com`. See [EU Endpoints](../eu-gdpr-endpoints/) for more info.
Skeletal MapLibre Style JSON Starter
```
{
"sources": {
"openmaptiles": {
"type": "vector",
"url": "https://tiles.stadiamaps.com/data/openmaptiles.json",
"attribution": "© Stadia Maps © OpenMapTiles © OpenStreetMap"
}
},
"sprite": "https://tiles.stadiamaps.com/styles/alidade-smooth/sprite",
"glyphs": "https://tiles.stadiamaps.com/fonts/{fontstack}/{range}.pbf",
"layers": [...]
}
```
### Advanced: Customizing Sprites
You can generate your own sprite files using [Spreet](https://github.com/flother/spreet), and host them on your server/CDN. Then, add them to your JSON style (using the `sprite` key).
Note that (as in the example JSON above) you should *not* include the suffix or extension in the URL. For example, if your output base name is `sprite`, you would use `https://my-domain.com/path/to/sprite` in your JSON style. MapLibre will automatically request `https://my-domain.com/path/to/sprite.json` and `https://my-domain.com/path/to/sprite@2x.png` respectively (`@2x` is appended automatically for retina/HiDPI displays).
### Advanced: Customizing Fonts
In our own themes, we have rolled up our default font into a "super-font" stack with the fallback order we want. This makes map styles easier to edit.
Stadia font families
- Stadia Regular
- Stadia Italic
- Stadia Bold
- Stadia Semibold
If you want to change the fallback order or remove specific fonts, you can mix and match from the full list below (collapsed).
Full individual font list
- Noto Sans Arabic Light
- Noto Sans Arabic Medium
- Noto Sans Arabic Regular
- Noto Sans Armenian Light
- Noto Sans Armenian Medium
- Noto Sans Armenian Regular
- Noto Sans Ethiopic Light
- Noto Sans Ethiopic Medium
- Noto Sans Ethiopic Regular
- Noto Sans Georgian Light
- Noto Sans Georgian Medium
- Noto Sans Georgian Regular
- Noto Sans Hebrew Light
- Noto Sans Hebrew Medium
- Noto Sans Hebrew Regular
- Noto Sans JP Light
- Noto Sans JP Medium
- Noto Sans JP Regular
- Noto Sans Light
- Noto Sans Medium
- Noto Sans Regular
- Noto Sans SC Light
- Noto Sans SC Medium
- Noto Sans SC Regular
- Noto Sans TC Light
- Noto Sans TC Medium
- Noto Sans TC Regular
- Noto Sans Thai Light
- Noto Sans Thai Medium
- Noto Sans Thai Regular
- Open Sans Bold
- Open Sans Italic
- Open Sans Regular
- Open Sans Semibold
- Roboto Bold
- Roboto Condensed Italic
- Roboto Italic
- Roboto Medium
- Roboto Regular
- SeoulNamsan B
- SeoulNamsan L
- SeoulNamsan M
As with sprites, if you want to use your own set of fonts, you will need to host these yourself.
Warning
Make sure that you are in compliance with any font licensing requirements before deploying maps with additional fonts.
You can use a tool like our own open-source [`build_pbf_glyphs`](https://github.com/stadiamaps/sdf_font_tools/tree/main/build_pbf_glyphs) CLI utility or [MapLibre FontMaker](https://maplibre.org/font-maker/) to generate the SDF font glyphs. You will then need to update `glyphs` key in your style JSON accordingly.
The [`text-font`](https://maplibre.org/maplibre-style-spec/layers/#text-font) property in a MapLibre Style is a list which specifies fonts in fallback order. This is quite handy, but introduces some complexity. We'll cover both approaches briefly.
#### Combining Glyphs Upfront
The easiest option is to never specify more than a single font in any font list. Then you can host your font glyphs in a directory of static files (as generated by `build_pbf_glyphs`). You *can* still have fallback fonts without any extra complexity server-side by combining glyphs upfront into "super fonts."
For example, you can roll up Latin script bold font and a Japanese bold font into a single "bold" font. Let's call this "MyFont Bold." You just need to specify the fonts and a fallback order (much as you would in the MapLibre style JSON). You can then reference "MyFont Bold" in your style, and host the glyphs for MyFont Bold in a directory of static files. Refer to the [`build_pbf_glyphs` README](https://github.com/stadiamaps/sdf_font_tools/tree/main/build_pbf_glyphs) for usage instructions.
#### Combining Glyphs Dynamically
If you need to handle fallback fonts dynamically via a list in your style, your server will need to know how to handle Mapbox-style fontstack requests. These combine requests for multiple fonts into a long URL. Your server software then selects the glyphs in the specified fallback order. We have open-sourced the Rust crate, [`pbf_font_tools`](https://github.com/stadiamaps/sdf_font_tools/tree/main/pbf_font_tools), which has this logic. We use this ourselves and as part of `build_pbf_glyphs`. If you're using Node.js, have a look at [glyph-pbf-composite](https://github.com/mapbox/glyph-pbf-composite).
## Raster Maps
Most customers ask us about custom raster styles because they are using Leaflet. If you want to keep using Leaflet, we have good news! You can use vector tiles in Leaflet with a custom style using the `maplibre-gl-leaflet` plugin. We put together a [tutorial](../tutorials/vector-tiles-with-leaflet/) on how to do this in just a few minutes. And this [example repository](https://github.com/stadiamaps/leaflet-custom-style) shows Leaflet in action with a custom style.
If you are still sure that you need raster tiles for your custom style, [contact us](mailto:support@stadiamaps.com) for a quote.
## Further Reading
The [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/) is the final authority on MapLibre styling. Even if you primarily use Maputnik for editing styles visually, this is still useful to have as a reference.
### Guides
- [Changing the Map Label Language (guide)](../tutorials/changing-the-map-label-language/)
- [Mapster WP Maps Plugin for WordPress (step-by-step tutorial)](../tutorials/wordpress-with-mapster/)
# Map Style Library
Stadia Maps offers multiple map styles, each with a different focus to help make the most of your map integration. This page showcases the styles we host at Stadia Maps. Quickly get started in popular frameworks like MapLibre GL JS, Leaflet, and OpenLayers with our example code.
Build your own custom styles!
Did you know? You can also [create your own custom style](../custom-styles/) to ensure your maps fit your application color scheme, focus, or other requirements perfectly.
## Stadia Maps Styles
Our homegrown styles are the result of years of refinement based on user feedback around their use cases.
### Alidade Smooth
Free
Starter
Standard
Professional
Our custom Alidade Smooth style is designed for maps that use a lot of markers or overlays. It features a muted color scheme and fewer points of interest to allow your added data to shine.
[Get started with Alidade Smooth](../map-styles/alidade-smooth/)
### Alidade Smooth Dark
Free
Starter
Standard
Professional
Alidade Smooth Dark, like its lighter cousin, is also designed to stay out of the way. It just flips the dark mode switch on the color scheme. With the lights out, your data can now *literally* shine.
[Get started with Alidade Smooth Dark](../map-styles/alidade-smooth-dark/)
### Alidade Satellite
Free
Starter
Standard
Professional
Carrying on the tradition of the Alidade family of styles, Alidade Satellite turns the earth itself into your canvas. This style uses satellite imagery as a base, and layers labels and outlines of major features on top to create a solid contextual backdrop for your data.
[Get started with Alidade Satellite](../map-styles/alidade-satellite/)
### Outdoors
Free
Starter
Standard
Professional
Make the great outdoors your playground. Based on the well-known OSM Bright style, the Stadia Outdoors style highlights useful outdoor features such as ski slopes, mountains, parks, and paths.
[Get started with Outdoors](../map-styles/outdoors/)
## Stadia × Stamen
Based on the original basemaps created for the Knight Foundation, the Stadia x Stamen partnership fuses Stamen's tradition of beautiful, functional maps with our strong and reliable platform, affordable and accessible to anyone.
### Stamen Toner
Free
Starter
Standard
Professional
The most popular of the excellent styles from Stamen Design, these high-contrast B+W (black and white) maps are the perfect backdrop for your colorful and eye-catching overlays.
[Get started with Stamen Toner](../map-styles/stamen-toner/)
### Stamen Terrain
Free
Starter
Standard
Professional
Orient yourself with the Stamen terrain maps, featuring hill shading and natural vegetation colors.
[Get started with Stamen Terrain](../map-styles/stamen-terrain/)
### Stamen Watercolor
Free
Starter
Standard
Professional
Reminiscent of hand drawn maps, the watercolor maps from Stamen Design apply raster effect area washes and organic edges over a paper texture to add warm pop to any map.
[Get started with Stamen Watercolor](../map-styles/stamen-watercolor/)
## Classic Styles
Classic, familiar styles that you've probably seen around the web.
### OSM Bright
Free
Starter
Standard
Professional
The OSM Bright style was developed as a sensible starting point for quickly making beautiful maps based on the OpenStreetMap database. It highlights the content in the map itself. We recommend using OSM bright where you are focused on providing directions, have minimal overlays or other data that might clash with the styling, or want a lot of POIs visible. Note that our outdoors style also works for many of these scenarios, but OSM Bright is a classic alternative.
[Get started with OSM Bright](../map-styles/osm-bright/)
# Add 3D Buildings to your Maps
Free
Starter
Standard
Professional
Most maps are flat, looking straight down at the earth from above. But sometimes you need to look at things from another angle. [Navigation apps](../../sdks/ferrostar/), for example, often have a camera pitch between 45 and 60 degrees to show the area around and in front of the vehicle better.
At this angle, your maps can really come to life with 3D buildings. This tutorial will show you how to add 3D buildings to any map style using MapLibre and vector basemap tiles from Stadia Maps.
## Build the Layer
The look and feel of your map is controlled by a stylesheet using a JSON [MapLibre style](https://maplibre.org/maplibre-style-spec/). It defines the data sources and layers, which order rendering should happen in, and various attributes of the layers that make up your map.
A [`fill-extrusion`](https://maplibre.org/maplibre-style-spec/layers/#fill-extrusion) layer lets us create a 3D effect by extruding portions of buildings from height information already present in the vector tile. The result isn't as detailed as a 3D model or point cloud, but it's *fast*, and saves precious data for mobile users.
A 3D building layer definition
```
{
"id": "3d-buildings",
"source": "openmaptiles",
"source-layer": "building",
"filter": [
"!",
[
"to-boolean",
["get", "hide_3d"]
]
],
"type": "fill-extrusion",
"minzoom": 13,
"paint": {
"fill-extrusion-color": "lightgray",
"fill-extrusion-height": [
"interpolate",
["linear"],
["zoom"],
13,
0,
16,
["get", "render_height"]
],
"fill-extrusion-base": [
"case",
[">=",
["get", "zoom"],
16
],
["get", "render_min_height"],
0
]
}
}
```
If you're familiar with MapLibre style documents already, this might look familiar. The first few property names describe themselves: an `id` gives a layer a unique identifier, `minzoom` specifies the minimum zoom at which the layer will render, and so on.
If you're starting from a [style](../../themes/) developed internally at Stadia Maps, it has an OpenMapTiles schema-compatible source named `openmaptiles`. If you're adding buildings to one of our Stamen-designed styles, change the source identifier to `stamen-omt`.
The `filter` is an example of an [expression](https://maplibre.org/maplibre-style-spec/expressions/), which lets you make a decision according to some rules. If you've written LISP before, this might look like a predicate function with weird syntax. In our example, we are excluding features in the `building` layer with `hide_3d` set to a truth-y value. (The `hide_3d` property is set on building parts which are underground.)
The `paint` property is where we specify how the building will look. The `fill-extrusion-color` determines the exterior color. We've selected a neutral light gray. The `fill-extrusion-height` and `fill-extrusion-base` properties determine how the building is extruded. The effect of our choices here is to make the building "scale in" as you zoom.
## Add the Layer to a Style
### Integrating into a Custom Style
If you're building a [custom style](../../custom-styles/) already, just add the new layer where you'd like it in your JSON! This currently requires a bit of work, but we're working to make that easier. [Let us know](mailto:support@stadiamaps.com) if you'd like to be kept up to date on custom style hosting.
### Modifying an Existing Style Dynamically
You can also modify an existing style in-place after it's loaded into the map! Use this approach with care, as things like layer identifiers or ordering may change over time.
Here's an example of how you could do this in MapLibre GL JS. MapLibre Native offers similar APIs.
```
map.addLayer({
'id': '3d-buildings',
'source': 'openmaptiles',
'source-layer': 'building',
'filter': [
"!",
["to-boolean",
["get", "hide_3d"]
]
],
'type': 'fill-extrusion',
'minzoom': 13,
'paint': {
'fill-extrusion-color': 'lightgray',
'fill-extrusion-height': [
'interpolate',
['linear'],
['zoom'],
13,
0,
16,
['get', 'render_height']
],
'fill-extrusion-base': ['case',
['>=', ['get', 'zoom'], 16],
['get', 'render_min_height'], 0
]
}
},
labelLayerId // Replace this with the ID of a layer in the style; the new layer will be added below this
);
```
## Full Example in MapLibre GL JS
Bringing everything together, here is some sample code which adds a 3D buildings layer to one of our styles. To keep it relatively simple and future-proof, we add the buildings directly under the first `symbol` layer.
To make experimenting easy, we've packaged the example code as a JSFiddle playground. Click the "Try it in JSFiddle" button to try it right from your web browser.
[Try it in JSFiddle](#)
```
Vector Map Demo
```
## Next Steps
Ready to add maps with 3D buildings to your website or app? Sign up for a free Stadia Maps account to get started!
[Get Started With a Free Account](https://client.stadiamaps.com/signup/)
If you want to craft even more detailed building styles, the MapLibre style [documentation](https://maplibre.org/maplibre-style-spec/layers/#fill-extrusion) covers other styling options including patterns, gradients, and more.
Finally, if you're building a navigation experience, check out [Ferrostar](../../sdks/ferrostar/), our highly extensible navigation SDK.
# Changing Specific Place Labels on the Map
Free
Starter
Standard
Professional
Have you found wanted to change the label of a particular feature on your map? Maybe you have to display a political viewpoint, or just want to add your own label to the map to make a place more prominent?
This tutorial will cover what you need to know, along with example code for MapLibre GL JS and MapLibre GL Leaflet.
For local development on a web server at `localhost` or `127.0.0.1`, you can get started without any API keys or domain setup!
For mobile, backend, and non-local web development, you'll need either domain auth or an API key. If you don't already have a Stadia Maps account, [sign up for a free](https://client.stadiamaps.com/signup/) to get started.
Domain-based authentication
Domain-based authentication is the easiest form of authentication for production web apps. No additional application code is required, and you don't need to worry about anyone scraping your API keys. We recommend this for most browser-based applications.
1. Sign in to the [client dashboard](https://client.stadiamaps.com/dashboard/).
1. Click "Manage Properties."
1. Under "Authentication Configuration," click the button to add your domain.
API key authentication
### Authenticating requests via API key
You can authenticate your requests by adding an API key to the query string or HTTP header.
Example URL with an API key placeholder
```
https://api.stadiamaps.com/tz/lookup/v1?lat=59.43696&lng=24.75357&api_key=YOUR-API-KEY
```
Example Authorization header with an API key placeholder
```
Authorization: Stadia-Auth YOUR-API-KEY
```
The simplest is to add a query string parameter `api_key=YOUR-API-KEY` to your request URL. For example, to access the `/tz/lookup` API endpoint, your request URL might look like this.
You can also use an `Authorization` HTTP header instead of a query string as shown below. Don't forget the `Stadia-Auth` prefix!
### How to get an API key
Don't have an API key yet? Follow these easy steps!
1. Sign in to the [client dashboard](https://client.stadiamaps.com/dashboard/). (If you don't have an account yet, sign up for free; no credit card required!)
1. Click "Manage Properties."
1. If you have more than one property (ex: for several websites or apps), make sure you have selected the correct property from the dropdown at the top of the page.
1. Under "Authentication Configuration," you can generate, view or revoke your API key.
#### Video: How to generate your API key
## Finding the label to change
The simplest approach to changing labels is to modify your map style. Normally the `text-field` is a simple property getter, like `["get", "name:en"]`. We'll replace the simple getter with conditional logic based on the ID.
For example, if you want to rename the Gulf of Mexico, here's how you'd change your style:
```
"text-field": [
- ["get", "name:en"]
+ "case",
+ ["==", ["id"], 3056391900],
+ "Gulf of America",
+ ["get", "name:en"]
],
```
While the mechanism is extremely simple, it *does* rely on you knowing two things: the feature and layer ID. We've made it easy to get these with the map below. Just find the label you're looking for, click on it, and copy the ID and source layer.
[© Stadia Maps](https://stadiamaps.com/) [© OpenMapTiles](https://openmaptiles.org/) [© OpenStreetMap](https://www.openstreetmap.org/copyright)
## Changing the label
Now that we have a feature ID and a source layer, we can go about modifying our stylesheet. Here's how you can do it on the fly in JavaScript!
To change the layer in MapLibre GL JS, you'll need to add some code in a style load listener. For a full example of how to set up a full-page map, check our [quickstart tutorial](../vector-maps-with-maplibre-gl-js/).
```
var map = new maplibregl.Map({
container: 'map',
style: 'https://tiles.stadiamaps.com/styles/alidade_smooth.json', // Style URL; see our documentation for more options
center: [-87, 24], // Initial focus coordinate
zoom: 4
});
map.on('style.load', (e) => {
const style = map.getStyle();
for (const targetLayer of style.layers) {
// We're interested in layers that display items from water_name
if (targetLayer["source-layer"] === 'water_name') {
// Add some switching logic for the text-field, matching against the feature ID: 3056391900
// If you have another feature you need update, you will need to make adjustments to the case condition below
targetLayer.layout['text-field'] = [
'case',
["==", ["id"], 3056391900],
"Gulf of America",
["get", "name:en"]
];
}
}
map.setStyle(style);
});
```
You can change labels in leaflet too, if you're using the MapLibre GL Leaflet plugin. For a full example of how to set up vector tiles in Leaflet, check our [quickstart tutorial](../vector-tiles-with-leaflet/). To change the layer in MapLibre GL Leaflet, you'll need to add some code in a style load listener.
```
const lmap = L.map('map').setView([24, -87], 5);
const gl = L.maplibreGL({
style: 'https://tiles.stadiamaps.com/styles/alidade_smooth.json',
attribution: '© Stadia Maps , © OpenMapTiles © OpenStreetMap ',
}).addTo(lmap);
const map = gl.getMaplibreMap();
map.on('style.load', (e) => {
const style = map.getStyle();
for (const targetLayer of style.layers) {
// We're interested in layers that display items from water_name
if (targetLayer["source-layer"] === 'water_name') {
// Add some switching logic for the text-field, matching against the feature ID: 3056391900
// If you have another feature you need update, you will need to make adjustments to the case condition below
targetLayer.layout['text-field'] = [
'case',
["==", ["id"], 3056391900],
"Gulf of America",
["get", "name:en"]
];
}
}
map.setStyle(style);
});
```
## Next Steps
And that's it. The above examples are extendable to more features by adding additional cases. If you're already hosting a custom style, you may rather edit the style directly, but this demonstrates the overall approach.
Once you're ready to move beyond `localhost` testing, sign up for a free Stadia Maps account, and we'll walk through the next steps to registering your domain or getting an API key.
[Get Started With a Free Account](https://client.stadiamaps.com/signup/)
# How to Change the Language of Place Labels
Free
Starter
Standard
Professional
Our [default map styles](../../themes/) show the names of places (countries, cities, towns, waterways, etc.) in up to two languages by default. The first is the "Latin" name (the local name if it happens to use a Latin script; otherwise the English name or a romanization), and the second is the local name (if the local name does not use a Latin script). We chose this approach to preserve readability for a large audience while still showing local names. However, some applications will benefit from explicit localization. This tutorial will show you how!
## Editing the style
The first step is to pick the style you want to localize. You can start with [one of our own](../../themes/), or [design your own](../../custom-styles/).
Next, it's time to customize the style! The property you need to look at is `text-field`, under the `layout` property of each layer. Our styles come with a rather complex expression that does string concatenation and fallback logic. The simplest way to change language labels is to replace the `text-field` key with a value like `{name:fr}`. The braces tell the renderer that it should use the value of the field for the text label. In this case, it will use the `name:fr` field, thus always picking the French name of the feature.
List of supported language codes
- `am` (Amharic)
- `ar` (Arabic)
- `az` (Azerbaijani, Latin)
- `be` (Belarusian)
- `bg` (Bulgarian)
- `br` (Breton, Latin)
- `bs` (Bosnian, Latin)
- `ca` (Catalan, Latin)
- `co` (Corsican, Latin)
- `cs` (Czech, Latin)
- `cy` (Welsh, Latin)
- `da` (Danish, Latin)
- `de` (German, Latin)
- `el` (Greek)
- `en` (English, Latin)
- `eo` (Esperanto, Latin)
- `es` (Spanish, Latin)
- `et` (Estonian, Latin)
- `eu` (Basque, Latin)
- `fi` (Finnish, Latin)
- `fr` (French, Latin)
- `fy` (Western Frisian, Latin)
- `ga` (Irish, Latin)
- `gd` (Scottish Gaelic, Latin)
- `he` (Hebrew)
- `hr` (Croatian, Latin)
- `hu` (Hungarian, Latin)
- `hy` (Armenian)
- `id` (Indonesian, Latin)
- `is` (Icelandic, Latin)
- `it` (Italian, Latin)
- `ja` (Japanese)
- `ja_kana` (Japanese Kana form)
- `ka` (Georgian)
- `kk` (Kazakh)
- `kn` (Kannada)
- `ko` (Korean)
- `ku` (Kurdish, Latin)
- `la` (Latin, Latin)
- `lb` (Luxembourgish, Latin)
- `lt` (Lithuanian, Latin)
- `lv` (Latvian, Latin)
- `mk` (Macedonian)
- `mt` (Maltese, Latin)
- `ml` (Malayalam)
- `nl` (Dutch, Latin)
- `no` (Norwegian, Latin)
- `oc` (Occitan (post 1500), Latin)
- `pl` (Polish, Latin)
- `pt` (Portuguese, Latin)
- `rm` (Romansh, Latin)
- `ro` (Romania, Latin)
- `ru` (Russian)
- `sk` (Slovak, Latin)
- `sl` (Slovene, Latin)
- `sq` (Albanian, Latin)
- `sr` (Serbian, Cyrillic)
- `sr-Latn` (Serbian, Latin)
- `sv` (Swedish, Latin)
- `th` (Thai)
- `tr` (Turkish, Latin)
- `uk` (Ukrainian)
- `zh` (Chinese)
But what if the city, mountain, river, etc. doesn't have a French name? You can easily add fallback logic using the `coalesce` expression. For example, the following will use a French name if one exists, falling back to a Latin-based name next, and then finally falling back to the local name if there is no Latin name in our data.
```
"text-field": [
"coalesce",
[
"get",
"name:fr"
],
[
"get",
"name:latin"
],
[
"get",
"name"
]
]
```
Do this for every layer in your style that makes sense to localize. Some (for example, route numbers) can be left alone.
## Using the style
Rather than using our hosted styles, you'll need to point your map rendering library (MapLibre, Leaflet, etc.) at the new style. You will need to host this on your web server or bundle it with your app as noted in our guide on [creating a custom style](../../custom-styles/).
We've collected everything together in an [example repository](https://github.com/stadiamaps/leaflet-custom-style). The example uses Leaflet, but it works equally well in MapLibre GL JS or any other framework that supports MapLibre JSON styles.
# Map Tiles and Static Images
# Raster Map Tiles
While vector tiles have replaced the classic image grid for many applications, raster map tiles are battle-tested and easy to render. They also benefit from a rich ecosystem of rendering libraries like OpenLayers and Leaflet.
All of our house styles are available as PNG raster tiles in 256x256 px and 512x512 px "retina" sizes.
## Tile URL Format
Most renderers use a format string like the one below and will automatically replace the placeholders for you. The only thing you'll need to replace yourself in most cases is the `