Integrations

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.

Note that this is the data half. Putting a map on a page is a separate, smaller thing that needs none of it: the Drupal module's Mapsemble map block embeds any map from a map id, with no account connected and no feed involved. Feeding that map from your CMS is the step described here, and the block picks it up automatically once the map is wired.

A plugin can also skip the URL step entirely and supply the endpoint from the page at embed time — it knows its own site's feed path, so the site owner never has to enter it on the Mapsemble side:

Mapsemble.init({
  container: '#map',
  mapId: '…',
  endpoint: 'https://example.com/wp-json/mapsemble/v1/features',
});

The supplied URL takes precedence over the one configured in the builder for this embed's requests only (it is never stored), and must be a public https URL — anything else is rejected and the map renders empty with a remoteError.

What each plan includes

  • Free — build maps, embed them on your site, and share them at their hosted link. Embedding is free on every plan.
  • 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:

  1. 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 ID 42).
  2. Coordinates are [longitude, latitude] — the opposite order from most CMS geo fields.
  3. totalCount is the full number of matching features, ignoring offset and limit. 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 on every 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.