Dynamic vector tiles from PostGIS.

Paste a database URL and get a dynamic vector tile URL that updates seconds after a row changes. Then turn the same polygons into geofences that tell you when people arrive.

Free sandbox, no card. Works with RDS, Cloud SQL, Supabase, Neon, Crunchy Bridge or your own server.

14/8235/5419 and 14/8236/5419 re-rendered in 0.9s. The other 22 tiles didn't move.

From connection string to map in three steps

  1. Paste your database URL

    We check the connection, PostGIS version and permissions, and tell you if the URL goes through a pooler that can't carry live updates.

  2. Choose tables and columns

    Every table with a geometry column is listed with its SRID, row estimate and columns. Pick the properties that belong in the tiles and the zoom range.

  3. Paste the tile URL into your map

    You get a TileJSON URL and a z/x/y template with a publishable key, locked to the origins you allow. It works anywhere vector tiles do.

map.js
import maplibregl from "maplibre-gl";

const map = new maplibregl.Map({
  container: "map",
  style: {
    version: 8,
    sources: {
      parcels: {
        type: "vector",
        url: "https://tiles.mapsocket.com/t/acme/parcels.json?key=pk_live_…",
      },
    },
    layers: [{
      id: "parcels",
      type: "fill",
      source: "parcels",
      "source-layer": "parcels",
      paint: { "fill-color": "#2D6E9E", "fill-opacity": 0.3 },
    }],
  },
});

Edits reach the map without a rebuild

MapSocket listens to your database with Postgres LISTEN/NOTIFY. When a row changes, only the tiles that held its old or new shape are rewritten. A thousand-row edit re-renders each affected tile once.

  1. Row committed

    INSERT, UPDATE or DELETE on a published table

  2. Trigger sends NOTIFY

    Layer, row id, operation and the old bounds

  3. Batched

    Bursts are debounced and de-duplicated per tile

  4. Tiles re-encoded

    Only tiles touching the old or new geometry, at every zoom

  5. Your map refreshes

    update_complete arrives over SSE; webhooks fire too

Two ways to stay current

Live
The dashboard generates a trigger for each table, using your real id and geometry columns. You run it once; updates land in one to three seconds. Needs a direct connection, not a transaction pooler.
Snapshot
For read-only roles, pooled URLs or databases you can't add triggers to. We rebuild on a schedule, from every 5 minutes to once a week. Switch to live whenever you're ready.
trigger.sql
-- Generated for public.parcels; run once as the table owner
CREATE TRIGGER mapsocket_parcels
  AFTER INSERT OR UPDATE OR DELETE ON public.parcels
  FOR EACH ROW EXECUTE FUNCTION
    mapsocket_notify_tile_update('parcels', 'id', 'geom');
live-refresh.js
const tiles = "https://tiles.mapsocket.com/t/acme/parcels/{z}/{x}/{y}.pbf?key=pk_live_…";
const events = new EventSource("https://tiles.mapsocket.com/t/acme/parcels/events?key=pk_live_…");

// Sent after tilefeed rewrites the affected tiles
events.addEventListener("update_complete", (e) => {
  const { tiles_updated, affected_zooms } = JSON.parse(e.data);
  map.getSource("parcels").setTiles([`${tiles}&_t=${Date.now()}`]);
});

Geofencing on the same data

Send a location, get back the geofences it's in and what changed since the last one. Enter, exit and dwell events, place visits, country and state changes and trip arrivals go to your webhooks, signed so you can trust them.

Where geofences come from

  • Synced from a PostGIS table. Point at public.delivery_zones, choose the id and tag columns, and polygons stay in step with the table. Points and lines can be buffered into zones.
  • Drawn in the dashboard. Circles and polygons on the map, with a dwell time per geofence.
  • Created through the API. Upsert by tag and externalId, or import GeoJSON or a Radar-format CSV.
POST /v1/track
Location in, geofence events out
GET /v1/context
Which geofences contain a point, no state kept
GET /v1/search/geofences
Nearest geofences by distance and tag
POST /v1/trips
ETAs, approaching and arrival for deliveries and pickups

Every track also names the place (from OpenStreetMap), country and state, and runs fraud checks for impossible jumps and mocked locations. Publishable keys can track from browsers and phones; secret keys manage geofences and read events. Low-accuracy fixes are stored but don't fire events.

Request
curl https://api.mapsocket.com/v1/track \
  -H "Authorization: pk_live_…" \
  -H "Content-Type: application/json" \
  -d '{"userId":"driver-118","latitude":40.7411,"longitude":-73.9897,"accuracy":12}'
Response
{
  "user": {
    "userId": "driver-118",
    "geofenceIds": ["9c1e5a40-…"],
    "lastSeenAt": "2026-11-04T14:02:11Z"
  },
  "events": [{
    "type": "user.entered_geofence",
    "userId": "driver-118",
    "geofence": {
      "tag": "loading-bay",
      "externalId": "flatiron-02",
      "description": "Flatiron loading bay"
    },
    "confidence": 3,
    "createdAt": "2026-11-04T14:02:11Z"
  }]
}

Each event is also sent to your webhooks with a MapSocket-Signature header (HMAC-SHA256). Verify a signature

How it compares

If you need tiles generated per request from parameterised SQL functions, pg_tileserv or Martin is the better fit. MapSocket is for tables that change during the day and maps that should keep up.

Approach How tiles are made When a row changes What you operate
pg_tileserv A SQL query per tile request, on the fly Visible on the next request, but every request hits your database A server next to your database
Martin On the fly from PostGIS, or from MBTiles and PMTiles files Live when dynamic, at query cost; files need a rebuild A server, plus your own cache in front
Tippecanoe on a cron A full rebuild into MBTiles or PMTiles Wait for the next run, which rebuilds everything A scheduler, storage and a CDN
MapSocket Pre-built MBTiles, served from cache Only the affected tiles re-render, in seconds Nothing. Paste a URL.

Your database, your rules

You hand us a connection string, so here is exactly what we do with it.

What access does MapSocket need to my database?

A role that can SELECT the tables you publish. We recommend a dedicated read-only role; the dashboard gives you the exact CREATE ROLE and GRANT statements. For live updates you (not us) run a small trigger function on each table, which needs table-owner rights once.

How is my connection string stored?

Encrypted with AES-256-GCM before it touches our database, with the key held outside it. It's decrypted only inside the worker that builds your tiles, and it's never shown again in the dashboard or API, only host, database and user.

Can I allowlist your IPs?

Yes. All connections to your database come from a fixed set of egress IPs per region, listed in the dashboard next to each connection. Add them to your security group or pg_hba.conf and nothing else needs to be open.

Is the connection encrypted?

We connect with sslmode=require by default and refuse plain-text connections unless you explicitly turn that off for a private network.

Do you copy my tables?

No. Rows are read and encoded straight into vector tiles; the tile archive (MBTiles) is the only copy we keep. Delete a source and its tiles are deleted with it. Geofences you sync from a table are stored as geometries plus the columns you choose.

Pricing

Pay for the sources you run and the requests you serve. Your database stays yours; we never bill on rows.

Sandbox

Try it on a side project

$0 / month

  • 1 database, 1 tile source
  • Rebuild every 24 hours
  • 50k tile requests / month
  • 100 geofences, 100 tracked users
Start free

Starter

For a product with a map in it

$29 / month

  • Live updates from LISTEN/NOTIFY
  • 5 tile sources, 2 databases
  • 1M tile requests / month
  • 5,000 geofences, 5,000 tracked users
  • Webhooks
Choose Starter

Pro

For teams whose data moves all day

$149 / month

  • Everything in Starter
  • 25 sources, 10 databases
  • 10M tile requests / month
  • Geofences synced from PostGIS
  • Custom tile domain
  • PMTiles export to your bucket
Choose Pro

Enterprise

Dedicated workers, your cloud or ours

Let's talk

  • Dedicated tilefeed workers
  • VPC peering / PrivateLink
  • SSO and audit log
  • 99.95% uptime SLA
Contact us
Plan limits compared
Limit SandboxStarterProEnterprise
Tile sources 1525Unlimited
Databases 1210Unlimited
Tile requests 50k / mo1M / mo10M / moCustom
Geofences 1005,000100,000Unlimited
Tracked users 100 / mo5k / mo50k / moCustom
Georeferenced maps 110100Custom
Updates Rebuild every 24 hoursLive, seconds after a writeLive, seconds after a writeLive, seconds after a write
Custom tile domain NoNoYesYes

Go over your tile limit on a paid plan and tiles keep serving; we email you before anything changes. The Sandbox pauses at its limit.

Questions

Does it work with Supabase, Neon, RDS or Cloud SQL?

Yes, with one caveat: live updates use Postgres LISTEN, which doesn't work through transaction-mode poolers. Use the direct connection string (port 5432 on Supabase, the non-pooled host on Neon). If you can only use a pooled URL, pick snapshot mode and we'll rebuild on a schedule.

How fast are live updates?

Typically one to three seconds from COMMIT to a fresh tile. Notifications are debounced into small batches, so a bulk edit of 10,000 rows re-renders each affected tile once instead of 10,000 times.

Which map libraries can use the tiles?

Anything that reads Mapbox Vector Tiles and TileJSON: MapLibre GL, Mapbox GL, OpenLayers, Leaflet with a vector-tile plugin, deck.gl, QGIS.

Can I leave and run it myself?

Yes. MapSocket runs tilefeed, which is MIT licensed. Every source has an Export config button that gives you the tilefeed TOML, and you can download the MBTiles or a PMTiles archive at any time.

What counts as a tracked user?

A distinct userId that sent at least one /v1/track call in the month. Calls to /v1/context and geofence search don't count towards it.

Point it at a table you already have.

Most people have a live tile URL within five minutes of signing up.