# How to add parcel boundaries to a Mapbox or MapLibre map

> Show US property lines on your own web map with hosted vector tiles: get a tile URL, add a vector source and line/fill layers, filter by field, and keep your key off the client.

Source: https://www.parceled.ai/guides/add-parcel-boundaries-to-mapbox  
Publisher: Parceled (https://www.parceled.ai)  
Updated: 2026-10-06

To show parcel boundaries on a Mapbox GL JS or MapLibre map, add a vector tile source that serves parcel polygons and draw it with a line or fill layer. With Parceled you call `get_tile_endpoints` to get the tile URL template and the `source-layer` name, add the source with `map.addSource`, add a `line` layer for property lines, and proxy the tiles through your server so your API key never reaches the browser.

## Step by step

### 1. Get a key and the tile template

Create a key (see [docs](https://www.parceled.ai/docs)), then call `get_tile_endpoints` (free) over MCP, REST or GraphQL. Each layer comes back with a `urlTemplate`, `layerName` (the `source-layer`), `minZoom` and `maxZoom`.

### 2. Add the source and layers

```js
map.on('load', () => {
  map.addSource('parcels', {
    type: 'vector',
    // Your server proxies /tiles/parcels/{z}/{x}/{y} to Parceled with the key.
    tiles: ['https://your-app.example.com/tiles/parcels/{z}/{x}/{y}'],
    minzoom: 0,
    maxzoom: 14,
  })
  map.addLayer({
    id: 'parcel-fill',
    type: 'fill',
    source: 'parcels',
    'source-layer': 'parcels_a',
    paint: { 'fill-color': '#dc4a5a', 'fill-opacity': 0.08 },
  })
  map.addLayer({
    id: 'parcel-lines',
    type: 'line',
    source: 'parcels',
    'source-layer': 'parcels_a',
    paint: { 'line-color': '#dc4a5a', 'line-width': 0.8 },
  })
})
```

### 3. Proxy tiles to protect your key

```ts
// Next.js route: app/tiles/parcels/[z]/[x]/[y]/route.ts
export async function GET(_req: Request, { params }) {
  const { z, x, y } = await params
  const upstream = `https://www.parceled.ai/api/parceled/tiles/${process.env.PARCELED_LAYER_ID}/${z}/${x}/${y}?key=${process.env.PARCELED_KEY}`
  const res = await fetch(upstream)
  return new Response(res.body, {
    status: res.status,
    headers: { 'Content-Type': 'application/x-protobuf', 'Cache-Control': 'public, max-age=86400' },
  })
}
```

### 4. Filter and interact

Call `get_layer_sources` for the list of filterable fields, then use normal Mapbox expressions, for example `map.setFilter('parcel-lines', ['==', ['get', 'zip5'], '80202'])`. Use `queryRenderedFeatures` on click to read a parcel's ID, then fetch its full record with `search_parcels`.

## Does it work with MapLibre?

Yes. The tiles are standard Mapbox Vector Tiles (pbf), and the same `addSource`/`addLayer` code works in MapLibre GL JS, as well as in deck.gl's MVTLayer, OpenLayers and QGIS.

## What does it cost?

5,000 tile requests a month are free; after that, 1 credit ($0.01) per 1,000 tiles. Caching tiles at your proxy (as above) cuts repeat requests.

## Frequently asked questions

### Why do parcels disappear when I zoom out?

Parcel layers are only drawn from a minimum zoom level, because millions of tiny polygons at country scale are unreadable and heavy. Check `minZoom` from `get_tile_endpoints`.
