Places endpoints
Search places, fetch place detail and KPIs, visit time series with benchmarks, hourly heatmaps, trade areas, cross-shopping, CSV export, nearby places and multi-place comparison.
Places are the core of MapDove’s foot-traffic data. All endpoints below require authentication and are relative to https://api.mapdove.com/api/v1. Endpoints that take from/to follow the date-range rules — default last 90 days, clamped to your plan’s history.
| Method | Path | Purpose |
|---|---|---|
| GET | /places |
Search and list places |
| GET | /places/geojson |
All matching places as GeoJSON points |
| GET | /places/:id |
Place detail and KPIs |
| GET | /places/:id/visits |
Visit series with category benchmark |
| GET | /places/:id/hourly |
7 × 24 hourly heatmap |
| GET | /places/:id/trade-area |
Modeled trade area |
| GET | /places/:id/cross-shopping |
Places with overlapping visitor bases |
| GET | /places/:id/export.csv |
Daily data as CSV |
| GET | /places/:id/nearby |
Places within a radius |
| GET | /compare |
Up to six places side by side |
The PlaceSummary object
Most endpoints return places as PlaceSummary:
{
"id": 42, "name": "Bean Republic · Downtown", "address": "1408 Congress Ave, Downtown", "city": "austin",
"category": { "slug": "coffee", "name": "Coffee & Cafés", "color": "#92400e" },
"brand": { "slug": "bean-republic", "name": "Bean Republic" },
"neighborhood": "Downtown", "lng": -97.7412, "lat": 30.2689, "sqft": 1900,
"visits": 34567, "visits_prev": 33012, "change_pct": 4.7,
"avg_daily_visits": 384, "rank_in_category": 3
}
brand is null for independents. visits covers the requested range; visits_prev the previous equal-length range. distance_km is added by /nearby.
List places
GET /places
| Param | Description |
|---|---|
city |
required city slug |
category |
category slug |
brand |
brand slug |
q |
case-insensitive name search |
bbox |
minLng,minLat,maxLng,maxLat |
sort |
visits (default), change or name |
limit |
default 100, max 500 |
offset |
default 0 |
from, to |
date range |
curl "https://api.mapdove.com/api/v1/places?city=austin&category=coffee&sort=visits&limit=2" \
-H "Authorization: Bearer $MAPDOVE_KEY"
const params = new URLSearchParams({ city: 'austin', category: 'coffee', limit: '2' });
const { total, items } = await fetch(`https://api.mapdove.com/api/v1/places?${params}`, {
headers: { Authorization: `Bearer ${key}` },
}).then((r) => r.json());
r = requests.get(f"{BASE}/places", headers=H,
params={"city": "austin", "category": "coffee", "limit": 2}, timeout=30)
data = r.json()
print(data["total"], [p["name"] for p in data["items"]])
{
"total": 34,
"items": [
{ "id": 42, "name": "Bean Republic · Downtown", "visits": 34567, "change_pct": 4.7, "avg_daily_visits": 384, "rank_in_category": 1, "…": "…" },
{ "id": 57, "name": "Copperline Coffee · The Domain", "visits": 31204, "change_pct": -1.3, "avg_daily_visits": 347, "rank_in_category": 2, "…": "…" }
]
}
Places as GeoJSON
GET /places/geojson — every matching place as a point, for maps. Params: city (required), category, brand.
curl "https://api.mapdove.com/api/v1/places/geojson?city=phoenix&category=grocery" \
-H "Authorization: Bearer $MAPDOVE_KEY"
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [-112.0281, 33.5104] },
"properties": { "id": 611, "name": "FreshCart Market · Biltmore", "category": "grocery", "color": "#16a34a", "brand": "freshcart", "visits_30d": 51230 }
}
]
}
Place detail
GET /places/:id — PlaceSummary plus opening date, footprint polygon, the resolved range and KPIs. Params: from, to.
curl "https://api.mapdove.com/api/v1/places/42?from=2026-08-01&to=2026-10-31" \
-H "Authorization: Bearer $MAPDOVE_KEY"
place = requests.get(f"{BASE}/places/42", headers=H,
params={"from": "2026-08-01", "to": "2026-10-31"}, timeout=30).json()
print(place["kpis"]["frequency"], place["kpis"]["percentile_in_city"])
{
"id": 42, "name": "Bean Republic · Downtown", "…": "…PlaceSummary fields…",
"opened_on": "2016-03-02",
"footprint": { "type": "Polygon", "coordinates": [["…"]] },
"range": { "from": "2026-08-01", "to": "2026-10-31" },
"kpis": {
"visits": 35321, "visitors": 16014, "frequency": 2.21, "avg_dwell_min": 19.5,
"avg_daily_visits": 384, "change_pct": 2.1, "yoy_trend_pct": 6.0,
"category_avg_daily_visits": 300, "percentile_in_city": 81,
"busiest_dow": 2, "busiest_hour": 8
}
}
busiest_dow uses 0 = Sunday. KPI definitions: Core concepts.
Visits
GET /places/:id/visits — a time series and the category benchmark (average per place for the same category and city).
| Param | Description |
|---|---|
from, to |
date range |
granularity |
day (default), week or month |
curl "https://api.mapdove.com/api/v1/places/42/visits?granularity=week" \
-H "Authorization: Bearer $MAPDOVE_KEY"
{
"granularity": "week",
"series": [
{ "date": "2026-08-03", "visits": 2710, "visitors": 2105, "dwell_min": 19.2 },
{ "date": "2026-08-10", "visits": 2754, "visitors": 2139, "dwell_min": 19.4 }
],
"benchmark": [
{ "date": "2026-08-03", "visits": 2090, "visitors": 1630, "dwell_min": 18.1 },
{ "date": "2026-08-10", "visits": 2112, "visitors": 1647, "dwell_min": 18.0 }
]
}
Hourly
GET /places/:id/hourly — average visits by weekday and hour. Params: from, to.
curl https://api.mapdove.com/api/v1/places/42/hourly \
-H "Authorization: Bearer $MAPDOVE_KEY"
{
"matrix": [
[0, 0, 0, 0, 0, 3.1, 12.4, 24.8, 27.9, "…24 values…"],
"…7 rows, 0 = Sunday…"
],
"peak": { "dow": 2, "hour": 8 }
}
matrix[dow][hour] is average visits in that hour. Hourly shape comes from a category profile in the demo dataset (Demo data).
Trade area
GET /places/:id/trade-area — the hexes supplying the first 85% of modeled visitors, with a demographic summary. No params. See Trade areas.
curl https://api.mapdove.com/api/v1/places/42/trade-area \
-H "Authorization: Bearer $MAPDOVE_KEY"
{
"summary": {
"hexes": 52, "area_km2": 41.2, "population": 96310, "households": 44820,
"median_income": 98200, "median_age": 33.4, "pct_college": 51.2, "pct_family": 27.9,
"avg_distance_km": 3.1, "p50_distance_km": 2.4,
"segments": [
{ "slug": "urban-professionals", "name": "Urban Professionals", "color": "#14b8a6", "share": 0.46 },
{ "slug": "young-renters", "name": "Young Renters", "color": "#ec4899", "share": 0.21 }
],
"neighborhoods": [ { "name": "Downtown", "share": 0.31 }, { "name": "East Austin", "share": 0.18 } ]
},
"geojson": {
"type": "FeatureCollection",
"features": [
{ "type": "Feature", "id": 812, "geometry": { "type": "Polygon", "coordinates": ["…"] },
"properties": { "hex_id": 812, "share": 0.06412, "cum_share": 0.0641, "population": 2210, "median_income": 121000, "segment": "urban-professionals" } }
]
}
}
Cross-shopping
GET /places/:id/cross-shopping — places whose modeled visitor-origin footprint overlaps most, ranked by affinity. See Cross-shopping.
| Param | Description |
|---|---|
limit |
default 10, max 50 |
curl "https://api.mapdove.com/api/v1/places/42/cross-shopping?limit=3" \
-H "Authorization: Bearer $MAPDOVE_KEY"
[
{ "place": { "id": 133, "name": "IronPulse Fitness · Downtown", "category": { "slug": "fitness", "…": "…" }, "…": "…" }, "overlap": 0.66, "affinity": 0.89 },
{ "place": { "id": 201, "name": "Urban Loom · South Congress", "…": "…" }, "overlap": 0.58, "affinity": 0.78 },
{ "place": { "id": 77, "name": "Downtown Fresh Market", "…": "…" }, "overlap": 0.71, "affinity": 0.71 }
]
Export CSV
GET /places/:id/export.csv — daily rows for the range. Params: from, to. Requires a plan with exports (Pro and above); otherwise 402 plan_limit. Each successful download counts as one export.
curl "https://api.mapdove.com/api/v1/places/42/export.csv?from=2026-10-01&to=2026-10-31" \
-H "Authorization: Bearer $MAPDOVE_KEY" -o place-42.csv
day,visits,visitors,dwell_min
2026-10-01,402,318,19.6
2026-10-02,397,311,18.9
Nearby
GET /places/:id/nearby — other places within a radius, closest first, with distance_km.
| Param | Description |
|---|---|
radius_km |
default 2 |
category |
optional category slug |
curl "https://api.mapdove.com/api/v1/places/42/nearby?radius_km=1&category=coffee" \
-H "Authorization: Bearer $MAPDOVE_KEY"
[
{ "id": 63, "name": "Downtown Espresso Bar", "avg_daily_visits": 262, "change_pct": 0.8, "distance_km": 0.42, "…": "…" }
]
Compare
GET /compare — up to six places on a common basis.
| Param | Description |
|---|---|
ids |
required, comma-separated, up to 6 |
from, to |
date range |
granularity |
day, week or month |
curl "https://api.mapdove.com/api/v1/compare?ids=42,57,63&granularity=month" \
-H "Authorization: Bearer $MAPDOVE_KEY"
const cmp = await fetch('https://api.mapdove.com/api/v1/compare?ids=42,57,63&granularity=month', {
headers: { Authorization: `Bearer ${key}` },
}).then((r) => r.json());
for (const p of cmp.places) console.log(p.name, cmp.series[p.id].length);
{
"places": [ { "id": 42, "name": "Bean Republic · Downtown", "…": "…" }, { "id": 57, "…": "…" }, { "id": 63, "…": "…" } ],
"series": {
"42": [ { "date": "2026-08-01", "visits": 11902, "visitors": 6120, "dwell_min": 19.3 } ],
"57": [ { "date": "2026-08-01", "visits": 10811, "visitors": 5502, "dwell_min": 18.4 } ]
},
"hourly": {
"42": [0, 0, 0, 0, 0, 3.5, 13.9, 27.8, 31.3, 24.3, "…24 values…"]
}
}
hourly[id] is the average visits per hour of day across the range.