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
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.
We check the connection, PostGIS version and permissions, and tell you if the URL goes through a pooler that can't carry live updates.
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.
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.
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 },
}],
},
});
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.
INSERT, UPDATE or DELETE on a published table
Layer, row id, operation and the old bounds
Bursts are debounced and de-duplicated per tile
Only tiles touching the old or new geometry, at every zoom
update_complete arrives over SSE; webhooks fire too
-- 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'); 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()}`]);
}); 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.
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.
tag and externalId, or import GeoJSON or a Radar-format CSV.
POST /v1/trackGET /v1/contextGET /v1/search/geofencesPOST /v1/tripsEvery 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.
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}' {
"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
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. |
You hand us a connection string, so here is exactly what we do with it.
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.
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.
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.
We connect with sslmode=require by default and refuse plain-text connections unless you explicitly turn that off for a private network.
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.
Pay for the sources you run and the requests you serve. Your database stays yours; we never bill on rows.
Try it on a side project
$0 / month
For a product with a map in it
$29 / month
For teams whose data moves all day
$149 / month
Dedicated workers, your cloud or ours
Let's talk
| Limit | Sandbox | Starter | Pro | Enterprise |
|---|---|---|---|---|
| Tile sources | 1 | 5 | 25 | Unlimited |
| Databases | 1 | 2 | 10 | Unlimited |
| Tile requests | 50k / mo | 1M / mo | 10M / mo | Custom |
| Geofences | 100 | 5,000 | 100,000 | Unlimited |
| Tracked users | 100 / mo | 5k / mo | 50k / mo | Custom |
| Georeferenced maps | 1 | 10 | 100 | Custom |
| Updates | Rebuild every 24 hours | Live, seconds after a write | Live, seconds after a write | Live, seconds after a write |
| Custom tile domain | No | No | Yes | Yes |
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.
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.
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.
Anything that reads Mapbox Vector Tiles and TileJSON: MapLibre GL, Mapbox GL, OpenLayers, Leaflet with a vector-tile plugin, deck.gl, QGIS.
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.
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.
Most people have a live tile URL within five minutes of signing up.