Mapbox Tiling Service

Recipe specification

Current version: 1

A tileset recipe is a JSON document composed of configuration options that tell Mapbox Tiling Service (MTS) how to turn tileset source data into vector tiles.

When writing rules in a recipe document, you can use expressions or base them on the properties in your data. Example rules that you might specify in a recipe document could include:

  • Tile uploaded tornado data from zooms 5 through 16.
  • Only tile the most populated cities within a dataset at zooms less than 7, then from zooms 8 through 16, only tile the top 20 most populated cities or towns per tile.
  • Only tile the top 5 flood-affected regions by area from the uploaded dataset until zoom 10, at zoom levels greater than 10, only show flooded regions with an area greater than 8 pixels.

Recipes must be constructed according to the rules in this recipe reference.

Recipe top-level fields

A recipe is a JSON object that must contain the following top-level fields:

Required fieldsDescriptionData type
version The version of the Mapbox Tiling Service recipe reference that the recipe uses. The current version number is 1. Any version value other than the current version or a previous version will result in an error.Integer
layers The LayerObject is an object in which the keys are the names of the layer and their values are objects that represent the configuration and options for the layer.Object
  "version": 1,
  "layers": { ... }


A layer object is the primary way of describing how a vector tile layer should be created. It specifies where to retrieve source data, the precision of the data through zoom levels, and how to transform features and render them into tiles. The following fields are the top-level options for a single layer:

Required fieldsDescriptionData type
source The source data to use for this layer. Tileset sources are created with the Create a tileset source endpoint of MTS.String
minzoom Describes the lowest zoom level for the tileset.Integer
maxzoom Describes the highest zoom level for the tileset. More info.Integer

You can further refine the resulting tileset with the following optional fields:

Optional fieldsDescriptionData type
features Specifies the output on a per feature basis. More info.Object
tiles Specifies the output on a per tile basis. More info.Object

The following describes every available field for a LayerObject and the JSON type that field must be.

  "minzoom": Integer,
  "maxzoom": Integer,
  "features": {
    "id": Expression,
    "attributes": {
      "zoom_element": Array,
      "set": Object,
      "allowed_output": Array
    "filter": Object,
    "simplification": Number or Expression or Object
  "tiles": {
    "id": Expression,
    "extent": Number or Expression
    "buffer_size": Number or Expression
    "limit": Array,
    "union": [
        "group_by": Array,
        "aggregate": Array,
        "maintain_direction": Boolean,
        "where": Expression

Basic example

The simplest example tileset recipe includes a layer name, a tileset source ID, a minimum zoom value, and a maximum zoom.

  "version": 1,
  "layers": {
    "trees": {
      "source": "mapbox://tileset-source/{username}/trees-data",
      "minzoom": 4,
      "maxzoom": 8

Multi-layer recipes

Recipes can also be arrays of recipe objects. This format can be used to create multi-layer tilesets. Recipes can have a maximum of 20 layers defined.

  "version": 1,
  "layers": {
    "trees": { ... },
    "parks": { ... },
    "paths": { ... }

Layer name configuration

The layer name key is required for each LayerObject (e.g. "trees", "parks", and "paths" in the above example). This is the unique identifier for the layer of data in your final tileset. For any map each layer name must be unique. The layer name must be a string with only underscores (_) and alphanumeric characters.

Source configuration

A source refers to a tileset source, which is a collection of geographic data stored as line-delimited GeoJSON on Tileset sources can be created using MTS's Create a tileset source endpoint.

Zoom level configuration

The minzoom and maxzoom configurations control the zoom levels at which your data will be tiled. They are required for a recipe. These values must be integers. The minzoom must be less than or equal to maxzoom, and both must be between the values of 0 and 16.


maxzoom does not determine which zoom level your maps zoom to. Your map will still zoom past the max zoom in your recipe because of overzooming.

The min and max zoom levels you choose directly impact the time it takes MTS to finish processing your data. Each additional zoom level increases the amount of data in the resulting tileset exponentially.

So, it’s important to first consider the use cases your map is trying to solve for when choosing your min and max zoom levels so that you don't incur any performance costs unnecessarily.

The following table will help you decide the appropriate minzoom and maxzoom based on your data precision:

Zoom levelPrecision in feetPrecision in metersUse CaseExample
032000 ft10000 m
116000 ft5000 m
28000 ft2500 m
34000 ft1250 m
42000 ft600 m
51000 ft300 m
6500 ft150 m
7250 ft80 m
8125 ft40 m
964 ft20 m
1032 ft10 mlow-resolution administrative boundaries (for example, countries and state boundaries)
1116 ft5 m
128 ft2 m
134 ft1 m
142 ft0.5 mhigh-resolution administrative boundaries (such as neighborhoods, census tracts, and towns), national park polygon data, national land use, and land cover polygon data
151 ft0.25 m
160.5 ft0.125 mbuilding/parcel polygon data, road polygon or line data, points of interest (such as hotels, restaurants, and venues)

Zoom levels greater than 16, which are applicable for use-cases like lane-level navigation maps or high-definition indoor maps, are not possible by default. If you have a use case that requires a zoom level greater than 16, reach out to us and let us know.

Feature configuration

The features configuration object is used to describe how features are individually processed into vector tiles. This field contains the following elements, which are evaluated in the order of the list provided below:

Optional fieldsDescriptionData type
id Gives you control over identifying and generating input features IDs. Read more about feature IDs and their customer operators in the "Identification" section. If you don't specify, you get the GeoJSON feature ID. If the GeoJSON ID does not exist the ID will be a random integer using the random operator.Number or Array<Expression>
attributes A JSON object that controls how attributes are modified and which attributes are allowed in the resulting tileset.Object
filter A filter expression determining which features should be kept. If no filter is defined, the default filter is true and all features will be kept.Array
simplification An integer expression or value that is greater than zero that controls the level of simplification that occurs for features, or an object that specifies more detailed simplification control (see the "Feature simplification" section. This integer value is relative to the extent provided for the tiles, with a larger value resulting in more simplification.Integer or Expression or Object
bbox Clips each feature to the specified [minlon, minlat, maxlon, maxlatbounding box.Array<Number>

Feature filters

The filter configuration is a single filter expression that results in a true or false evaluation for each feature as configured by the minzoom and maxzoom. This is the primary way to control which features are allowed into the final tileset on a per-feature basis. If no filter is provided, by default this results in a value of true for all features.

Filter expressions

In various configurations throughout the recipe, you have the ability to select relevant features by their attributes. This is possible with filter expressions, which use the Mapbox GL JS expression syntax defined in the Mapbox GL JS Style Spec.

Each filter is a JSON array that is evaluated for a boolean or value result using the following operations:

  • Type assertion:

    • array, boolean, number, object, string, typeof
  • Type conversion:

    • to-boolean, to-number, to-string
  • Quoting:

    • literal
  • Feature characteristics:

    • geometry-type, id, zoom, feature
  • Data retrieval:

    • properties, at, get, has, length
  • Comparison:

    • ==, !=, <, >, <=, >=, step
  • Boolean operations

    • !, all, any
  • Conditionals:

    • case, coalesce, match
  • Local variables

    • let, var
  • String manipulation

    • concat, downcase, upcase, hash
  • Arithmetic

    • -, +, /, *, ^, %, abs, ceil, floor, e, ln, ln2, log10, log2, max, min, round, sqrt, random
  • Trigonometry

    • acos, asin, atan, cos, sin, tan, pi

Feature attributes

The attributes configuration allows for manipulation of attribute data, generation of new attributes, and removal of attributes. The available options are described below, and implemented in the order they are listed:

Optional fieldDescriptionData type
zoom_element A JSON array that provides a list of attributes that are specified per zoom level in the source data.Array<String>

For each attributes with a specified zoom_element, the final output attribute at zoom level N will be the Nth element in the array in the source data. If no zoom_element is defined, no attributes are altered. If the zoom level is greater than or equal to the number of elements in the array, the last element is used.

Consider the following attributes for a single GeoJSON feature:

  "type": "Feature",
  "geometry": { ... },
  "properties": { "name": [ null, null, "Main", "Main St.", "Main Street" ] }

In this example, the zoom_element array includes name. Using the attributes from the GeoJSON above, this feature would have no name attribute at zoom levels 0 and 1, would have "Main" at zoom level 2, "Main St." at zoom level 3, and "Main Street" at zoom levels 4 and above.

  "features": {
    "attributes": {
      "zoom_element": [ "name" ]
Optional fieldDescriptionData type
set A JSON object mapping the names of attributes to be generated to filter expressions that return the new attribute values. Particularly helpful for generating ranking schemes to be used in styling expressions.Object

Consider the following attributes for a single GeoJSON feature:

  "type": "Feature",
  "geometry": { ... },
  "properties": { "place": "state", "name": "California" }

In this example, the set attribute defines a new attribute to be included in the final tileset, labelrank, that is derived from the existing data's place attribute. Using the "match" expression, it assigns a value to features based on the value of "place". If "place" is equal to "country", then the labelrank attribute for the resulting feature will have a value of 0, and so on. It also provides a default value of 5 for any features that have a "place" value that does not match any of the values explicitly provided.

  "features": {
    "attributes": {
      "set": {
        "labelrank": [
          "match", [ "get", "place" ],
          "country", 0,
          "state", 1,
          "region", 2, "province", 2,
          "district", 3, "county", 3,
          "municipality", 4, "city", 4,
Optional fieldDescriptionData type
allowed_output A JSON array of attributes that controls which attributes will be carried over into the resulting tileset.Array<String>

If an allowed_output array is provided only the attributes specified in the array will be saved to the tileset. This does not prevent the attributes that are not excluded by allowed_output from being used in filter expressions and other steps throughout the publish job.

In this example, features in the resulting tileset will only have two attributes, name_en and name_es:

  "features": {
    "attributes": {
      "allowed_output": [ "name_en", "name_es" ]

Feature simplification

Optional fieldDescriptionData type
simplification Indicates the desired level of simplification. Larger values result in more simplification, in which some vertices are removed. The default value is 4. The maximum value is 4096.Integer or Expression or Object

The simplification expression or value describes the maximum distance that a point can be from the straight line that connects its two neighbors and still be considered to be on the line, and can be removed safely. Any point with a distance that is larger than the simplification value is considered to be away from the line, and must be preserved.

As you increase the simplification value, the number of vertices in each feature decreases. The MTS recipe validator will reject any simplification value that is more than 4096. If you do not add a value for simplification, MTS will use 4, the default value.

The resulting shape depends on the original shape of your feature. For instance, if your original feature is circular and you increase simplification, the result will look more like a polygon. If your original feature is a curved line and you increase simplification, the result will look more like a straight line.

MTS uses the Ramer–Douglas–Peucker algorithm to simplify features.

The simplification may be a constant number, or it may be an expression that evaluates to a number. A typical use for an expression for the simplification would be to use a minimal simplification at the layer's maximum zoom level, so it can be overzoomed with high precision, but to use a larger simplification at lower zoom levels that will never be magnified far.

If the simplification is an object, it can contain the following fields:

  • distance: An expression evaluating to the desired simplification level, as described above.
  • outward_only: An expression evaluating to true or false. If outward_only is true, then Polygon and MultiPolygon features will be simplified only in ways that slightly increase their area, never in ways that slightly decrease their area, so the borders of adjacent polygons will often overlap slightly instead of sometimes being separated by small gaps. This will increase the quality of polygon unioning if the recipe contains a union rule, at the cost of increased tile size because of reduced opportunity for simplification. (See the "Feature union" section.)

Note: Even if the simplification is specified as 0, complex features will receive additional simplification so that they can be rendered correctly by Mapbox GL. Polygons or MultiPolygons with more than 65535 vertices and LineStrings and polygon rings with more than 6553 points will be simplified to reduce their complexity to these limits.

Tile configurations

The tiles object contains the following elements, which are evaluated as each tile is being assembled from its component features:

Optional fieldDescriptionData type
buffer_size Controls the size of the buffer that will be created in your vector tiles. buffer_size is a percentage of the size of a tile. The default buffer size is 0.5. Note that the value of this attribute cannot be larger than 100. An expression can also be used in buffer_size, but must still return a number between 0 and 100. Buffers are particularly helpful for label point layers to avoid cutting labels of at tile boundaries.Number or Array<Expression>
bbox Clips each feature to the specified [minlon, minlat, maxlon, maxlatbounding box.Array<Number>
extent Controls the size of the extent of a vector tile. extent is a precision control for vector tiles, and by default is 4096. It is not recommended in most situations you change this value. If you set this value it must be a power of 2 between 256 and 8192. You may also set this number using an expression.Number or Array<Expression>
layer_size Specifies the size limit in kilobytes of each layer. The default value is 500 KiB. The value cannot be larger than 500 or smaller than 1. Configuring layer_size can be helpful for improving rendering speed.Integer
id Define the output ID encoded in your vector tiles. Read more about feature IDs and their customer operators in the "Identification" section. If this option is not defined, it will default to the resulting value of features.idNumber or Array<Expression>
remove_filled Control which "filled" features are removed by specifying a filter-expression.Array<Expression>

During tile creation it is possible for polygons to completely cover a tile and the area surrounding its buffer. These are called as "filled features". If all features within a tile are filled features, it may be useful for some tilesets to not create any tiles at all and expect that clients will look to higher zoom levels. To do this, you can use the remove_filled recipe.

The syntax utilizes a filter-expression to allow control over what features exactly should be removed.

"tiles": {
  "remove_filled": expression

No tile will be created if:

  • All features within the tile are considered filled features
  • All features are matched by the filter-expression provided in remove_filled

The following example removes all tiles that contain only filled features:

"tiles": {
  "remove_filled": true

The following example removes all tiles that contain only filled features after zoom level 5:

"tiles": {
  "remove_filled": [">", [ "zoom" ], 5]
Optional fieldDescriptionData type
limit A limitation rule that reduces the number of features for the specified type to a specific number, choosing the lowest or highest-numbered features according to some attribute.Array<Expression>

The limit field must be an array of limitation rules, each of which is evaluated in sequence to potentially limit the total number of features to be included in the final tile. There are two ways in which data is limited using the limit field, where and where_in_distance.

The where style limits the total number of features in the tile and its buffer. Only the first number of features that match a filter-expression are kept and the rest are dropped from the tile. The sorting of these is based on attribute which is the key to a feature's attribute and either sorted lowest to highest or highest to lowest.

The format for a where style limit are the following syntaxes:

  • [ "lowest_where", filter-expression , number , attribute ]
  • [ "highest_where", filter-expression , number , attribute ]

The where_in_distance is the second style of limit and subdivides a tile into number of regions. Within each region only one feature is kept from the features selected by the filter-expression for the limit. The number of regions contained within a single tile is controlled by region_count. This should always be a value equal to 4 to a power in size (e.g. 1, 4, 16, 64, 256..), if the region_count is not equal to one of these numbers it will be rounded to the nearest value of one of these numbers in processing. The feature that is selected in a region is based on either the highest or lowest value based on the attribute field.

The format for a where_in_distance style limit are the following syntaxes:

  • [ "lowest_where_in_distance", filter-expression , region_count , attribute ]
  • [ "highest_where_in_distance", filter-expression , region_count , attribute ]
Optional fieldDescriptionData type
order Specify the order of a sequence of features in the final output tile. The features with the lowest values are placed in the tile first.String

You can order the sequence of features in the final output tile by a specified attribute. The attribute values must be comparable (all strings or all numbers).

This example orders the features by the sequence attribute:

"tiles": { "order": "sequence" }

Renderers, including Mapbox GL, draw or place features in the order they appear in the tile. LineString and Polygon features that are drawn early in the sequence may be covered by overlapping features that are drawn later in the sequence. Conversely, labels that are placed early in the sequence will prevent labels of features later in the sequence from being placed nearby.

You can follow these general guidelines:

  • The most important LineStrings and Polygons should have the highest values for their order attribute, so they are not covered by overlapping features.
  • The most important labels should have the lowest values for their order attribute, so they are placed first.

Read more about how label visibility is affected by the order of features in the Optimize map label placement guide.

If no order attribute is specified, the features will appear in an unpredictable order.

Feature union

Optional fieldDescriptionData type
union Join features based on whether or not a defined specified attribute matches.Object

You can union features together if a specified set of their attributes match. The simplest case unions all features that have exactly the same attributes:

"tiles": { "union": [ { } ] }

The union specification object can contain an expression to union only features that match the specified expression as well as having matching attributes:

"where": expression

Additional union options:

  • group_by: [attribute, attribute, attribute] - Use group_by to union features that have specified attributes that match, instead of requiring all attributes to match.
  • aggregate: { attribute: type, attribute: type } - Use aggregate to accumulate the specified attributes from the unioned features. Attributes that are not specified in either group_by or aggregate will be removed. Acceptable types of aggregation include sum (to add numbers), product (to multiply numbers), min (to choose the lowest number), max (to choose the highest number), mean (to take the average of numbers), comma (to concatenate with a comma), concat (to concatenate the feature data without a delimiter), arbitrary (to take the attribute from one of the source features), or arbitrary-non-null (to take the attribute from one of the source features where the attribute is not null).
  • maintain_direction: boolean - Use maintain_direction: false to make more compact unions of LineStrings for which directionality doesn't matter. This reverses some of the LineStrings if that helps to connect them.
  • simplification: expression or object - Specifies how features will be resimplified after unioning, as described in the Feature simplification section. For polygon unions, it is often useful to specify outward_only: true in the main simplification section, so the source features will slightly overlap and be merged into a single geometry. Then you can specify outward_only: false in the union's post-unioning simplification section, so additional simplification will be applied to the polygon after unioning has already taken their overlap into account.

For example, you could specify the following to union only features where highway=motorway, and keep the average of their speed_limit attributes:

"tiles": {
      "where": [ "==", [ "get", "highway" ], "motorway" ],
      "maintain_direction": true,
      "aggregate": { "speed_limit": "mean" }

This example uses maintain_direction: true (the default) because motorway roads are generally mapped as pairs of roadways whose direction indicates their one-way direction.

For a more elaborate union example, see the Census Blocks example in the Tilesets recipe examples

Using union with zoom based properties

You can use union on zoom-element properties. If you are using union with a zoom-element property, the union will occur after the feature has been assigned the value based on the zoom level.


You can configure the identification of features in both "Features" and "Tiles". Both follow the same patterns and have the same options. In general, is used for defining or generating input IDs, while is used for defining or generating output IDs in the final vector tiles.

  "features": {
    "id": [ <Expression> ]
  "tiles": {
    "id": [ <Expression> ]

All IDs encoded in the final vector tiles will be integers, as defined by the vector tile specification. If you provide a string value to the value will be automatically hashed to an integer between 0 and 2^53.

Custom expression operators

id References the ID value of the current feature. gets the top-level ID field from the GeoJSON feature. references the value as defined by the configuration. If the result of the expression is still string it will be converted or hashed to an integer and encoded in final vector tile.
hash Generates a unique integer from a string value. Particularly helpful for generating IDs from a combination of the feature attributes, like ["hash", ["concat", ["get", "name"], ["get", "code"]]].
random Generates a random integer between 0 and 2^53. This operator is used by default for any features that do not have an ID defined.
feature Provides a static, integer representation of the feature. The hash is generated from the geometry, geometry type, and properties sorted alphabetically. If this option is used in the section, it is only the part of the feature within the individual tile, not the entire original feature.

Order of operations

The following describes the order of operations and type conversions applied when using the and options. Remember the Mapbox Vector Tile specification requires all IDs to be encoded as positive integers between 0 and 2^53.

  • In general ID value in either or will be converted to an Integer or Float if possible and will otherwise return a string or Null value.
  • If is not defined, the top-level id of the GeoJSON feature will be used.

    • If there is no top-level id from the GeoJSON feature, a random integer will be generated.
    • If the value is set to null, no feature ID is set.
  • If is defined, the result of the expression will be converted to an Integer or Float if possible and will otherwise return a string.

    • If the expression returns a null value, a random integer will be generated. If you are trying to prevent feature IDs in your final tile, set to null.
  • If is not defined, the resulting value from will be used.
  • If is defined, the value of the resulting expression replaces whatever value was defined in
  • If the result of the expression is a...

    • string, it is hashed to an integer.
    • string representation of a number, it will be converted to the absolute value of that number truncated to an integer, modulo 2^53.
    • empty string, it will be treated as null and not encoded.
    • negative number, the absolute value will be used.
    • floating point number, it will be rounded and fit within 0 to 2^53.
    • boolean, it will be treated as null and not encoded.

If you don't want to encode any IDs in your final vector tile you can pass null to as your value instead of an expression array.

Duplicate IDs

While ID collision for unidentified features is unlikely, unique IDs aren't guaranteed since feature parsing occurs in a distributed environment. To guarantee unique identifiers, bring your own IDs. Duplicate feature IDs can cause unexpected behavior in tilesets and should be avoided.

More resources

Was this page helpful?