Skip to content

Bring your own data

Private overlays live under your account prefix on R2 and paint on top of the Canada basemap and the industry catalogs. The file is yours to prepare. unmap stores it and draws it. It does not reproject, index, search, or identify it.

Limits

CapValue
Overlays per account10
Size per overlay5 MB
FormatsGeoJSON (FeatureCollection, Feature, or a geometry) and PMTiles
AuthAPI key on /byod, session cookie on /account/byod

A larger file or an 11th overlay is 400 bad_request. An overlay stays until you delete it. Keys on the same account share the ten slots. A key with no account keeps its own files, and the dashboard lists the account, so those files do not appear there. This is included with API access. It is not a separate add-on, and each request counts as a byod call.

Upload, list, delete

curl -X POST "https://api.unmap.dev/byod?name=leases" \
  -H "Authorization: Bearer $UNMAP_API_KEY" \
  -H "Content-Type: application/geo+json" \
  --data-binary @leases.geojson
{
  "id": "ovl_aabbccddeeff",
  "name": "leases",
  "kind": "geojson",
  "bytes": 1284,
  "createdAt": "2026-09-11T18:00:00.000Z",
  "url": "https://api.unmap.dev/byod/ovl_aabbccddeeff/data",
  "overlay": "byod:ovl_aabbccddeeff"
}

GET /byod lists your overlays and repeats the limits. DELETE /byod/{id} removes the object. GET /byod/{id}/data is the file itself (same key). Multipart file + optional name is also accepted. The dashboard data tab uploads, lists, and deletes over the account session. The map still loads the file with your API key.

There is no replace. Uploading again mints a new id, and deleting the previous overlay frees the slot. A style that still names the old id is a 400.

Objects are stored at byod/{accountId}/{id}. Another account cannot list, read, or delete yours: the answer is 404, not a confirmation that the id exists.

From the SDK

import { Unmap } from "@unmap/sdk";
const unmap = new Unmap({ key: process.env.UNMAP_API_KEY! });
 
const input = document.querySelector<HTMLInputElement>("#overlay")!;
const { overlay } = await unmap.byod.upload(input.files![0]!, { name: "Service areas" });
const map = new Unmap({ key: process.env.UNMAP_API_KEY!, container: "#map", overlays: [overlay] });
 
const { overlays, limits } = await unmap.byod.list();
await unmap.byod.delete("ovl_aabbccddeeff");

upload() returns the whole record: overlay is the byod:<id> token that overlays takes, not the bare id. Pass a File straight from an input and the runtime handles the multipart encoding. A plain object goes as GeoJSON, and raw bytes go as PMTiles unless you pass { kind: "geojson" }. A refused upload throws a CatalogError with status 400 and code bad_request, carrying the server's own reason. Uploads and deletes are never retried, so a lost response cannot store the same file twice.

The file

Send GeoJSON as longitude, latitude, in degrees. unmap does not reproject. A projected file is stored unchanged and drawn in the wrong place.

The upload accepts the body when the JSON parses and its top-level type is a GeoJSON type, or when a PMTiles file begins with the PMTiles header. It does not check that the coordinates are numbers, or that they fall inside Canada.

MapLibre downloads the whole GeoJSON when the map loads. Five megabytes is the entire file. A larger area needs a PMTiles archive, which you add to the map yourself.

Paint it on a style

GeoJSON overlays compose through the existing overlays= hook:

GET /styles/muted.json?overlays=byod:ovl_aabbccddeeff,municipalities

overlays= replaces profile. List every catalog layer you still want in the same parameter.

const map = new Unmap({
  key,
  style: "muted",
  overlays: ["byod:ovl_aabbccddeeff", "municipalities"],
});

unmap adds a GeoJSON source pointing at /byod/{id}/data?key= and two layers, above roads and below labels, in the style's boundary color:

  • Point and MultiPoint are circles.
  • LineString, MultiLineString, Polygon, and MultiPolygon are strokes. There is no fill.
  • A GeometryCollection matches neither layer, so it is stored and not drawn.
  • Attributes stay in the file. These layers do not read them, so nothing is labeled and nothing is colored from a property.

Your API key is in that data URL. MapLibre fetches the file from the browser and cannot send an Authorization header, so treat the style document as a credential. A missing id is a 400. Deleting an overlay does not rewrite a style that still names it; that request stays 400 until you drop the token.

PMTiles

A PMTiles upload is stored and served at /byod/{id}/{z}/{x}/{y}?key=. Add the vector source yourself and name its source-layer. Style composition will not guess that name, so overlays=byod: on a PMTiles id is a 400. GET /byod/{id} returns the name, kind, and size, not the layer names inside the archive. A tile outside the archive's zoom range, or a tile with no bytes, is 204 with no body.

What this is not

  • A place to query, join, or restyle the attributes in your file.
  • A billed add-on. Usage is metered as byod.
  • A replacement for the public layer catalog.

Next steps

  • Layers for the catalog overlays you get without uploading anything.
  • Maps API for how an overlay composes onto a style.
  • Plans & Limits for how each of these requests counts as a call.