WordPress & Drupal
How a CMS plugin connects a site to Mapsemble — the feed contract your plugin implements, and what each plan includes.
WordPress & Drupal
A CMS integration is one endpoint. Your site exposes its content as GeoJSON at a fixed path, Mapsemble calls that endpoint as visitors pan, filter and page, and nothing is ever copied out of your database.
| CMS | Feed path | Starterkit |
|---|---|---|
| Drupal | /mapsemble/features |
drupal-vanilla, drupal-store-locator |
| WordPress | /wp-json/mapsemble/v1/features |
wordpress-vanilla |
Pick the starterkit for your CMS when you create a map, enter your site URL, and the data source is wired for you.
What each plan includes
- Free — build maps and share them at their hosted link.
- Starter — embed a map on your own site (one licensed domain).
- Pro — serve the map from your own feed, with remote filter options and CMS-rendered popups.
You can build and test the whole integration on any plan: connect the feed, embed it on your site, see it work. Until the account is on Pro, the map carries a small notice asking to upgrade — it never stops working, and visitors still get a usable map.
The feed contract
Response
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"id": "42",
"geometry": { "type": "Point", "coordinates": [4.895, 52.370] },
"properties": { "title": "Rijksmuseum", "category": "museum" }
}
],
"totalCount": 1234
}
Three rules that are easy to get wrong:
- Every feature needs a top-level
id. It is the identity Mapsemble uses to hydrate a single feature when someone opens a popup. Use the CMS entity id (node/42, post ID42). - Coordinates are
[longitude, latitude]— the opposite order from most CMS geo fields. totalCountis the full number of matching features, ignoringoffsetandlimit. In WordPress that is$query->found_posts; in Drupal, the count query before the range is applied. Paging and result counts read from it.
Query parameters you receive
| Parameter | Meaning |
|---|---|
mapId |
Which Mapsemble map is asking |
extent |
Current viewport as a WKT POLYGON in EPSG:4326 |
offset, limit |
Paging window |
orderBy |
Sort key, passed through from the map's order-by config |
language |
Active widget language |
filters |
JSON object of {"filter-slug": value} |
remaining |
1 means markers only — skip per-feature render work |
When limit is absent, return everything. Mapsemble calls the feed without a
limit to hydrate one feature by id; a feed that quietly caps the response makes
popups fail with a 404. If your dataset is large, honour limit when it is present
and stream the rest when it is not.
Any filter you configure with a passthrough key arrives as its own top-level
parameter instead of inside filters. These names are reserved and cannot be used
as filter slugs: mapId, extent, offset, limit, orderBy, language,
filters, remaining.
Failure behaviour
A feed that errors, times out or returns malformed JSON renders an empty map — the failure is logged on our side, not shown to your visitor. Treat the endpoint as part of your page-render budget: it is called on every map interaction.
Filter options from your CMS
A filter can source its choices from your site instead of from the data. Set an options endpoint on the filter and return a JSON object:
{ "museum": "Museums", "gallery": "Galleries" }
Your endpoint receives mapsemble[{mapId}][filter]={slug}, plus
mapsemble[{mapId}][search]= for autocomplete filters. Labels can also be objects
with label and weight when you need to control ordering.
CMS-rendered popups
A remote feed can supply its own popup markup, so popups match your theme exactly.
Put it in properties.__popup, as either an object:
{ "__popup": { "html": "<div class='card'>…</div>", "css": ".card{…}", "width": 320 } }
…rendered inside a shadow root, or a URL string, loaded in an iframe. Use
__popup_mobile for a narrow-screen variant.
Cards are different. Mapsemble always builds the card itself for remote maps, so
properties.__card is overwritten. To theme cards, either design them in the card
editor or render them client-side with
featureRenderer, which takes over both cards and popups.
Caching
Responses are not cached by default: every render calls your feed (requests within a single render are collapsed into one). Turn on response caching in the data source settings to reuse responses for a TTL you choose.
When caching is on, pass cacheVersion to the embed — a content hash, or
post_modified in WordPress, or a cache tag in Drupal — and Mapsemble fetches fresh
data the moment that value changes rather than waiting for the TTL:
Mapsemble.init({ container: '#map', mapId: '…', cacheVersion: '2026-08-19T10:41:00Z' });
cacheVersion is never forwarded to your endpoint; it only participates in our cache
key.
The alternative: push instead of serve
If you would rather not expose an endpoint — or your content changes rarely — push
it into Mapsemble instead with
the features API: PUT up to 1000 features per request
with remoteField set to your CMS entity id, and Mapsemble handles filtering,
search and geocoding on its own copy. Available from the Starter plan.
Use a slug of lowercase letters, digits and underscores for remoteField
(remote_id, not remote-id) — anything else is rejected and matching by remote id
is skipped for that request.