Tutorial

How to add parcel boundaries to a Mapbox or MapLibre map

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.

Updated · Parceled

Step by step

1. Get a key and the tile template

Create a key (see 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

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

// 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`.

Related

Query US property data from your code or your agent.

Connect Parceled