Remote Datasource
Connect your own API endpoint as a live datasource — Mapsemble fetches features on demand from your server.
Remote Datasource
Unlike CSV or Airtable imports that copy data into Mapsemble, a remote datasource keeps your data on your own server. Mapsemble calls your endpoint every time a user pans the map, zooms in or out, applies a filter, or pages through results. Your server returns the matching features and Mapsemble renders them.
This approach is ideal when:
- Your dataset is large or changes frequently — no need to re-import after every update.
- You already have a database — Drupal, WordPress, Laravel, a custom app — and want to expose it on a map.
- Data must stay on your infrastructure — for compliance, licensing, or privacy reasons.
How it works
- Build an endpoint on your server that returns GeoJSON-like features.
- In Mapsemble, create or edit a map and choose Remote URL as the datasource type.
- Paste your endpoint URL and save. Mapsemble starts fetching features immediately.
Quick Start
The simplest possible endpoint returns a JSON object with a features array and a totalCount:
{
"features": [
{
"type": "Feature",
"id": "1",
"geometry": {
"type": "Point",
"coordinates": [-3.703, 40.416]
},
"properties": {
"label": "Madrid"
}
},
{
"type": "Feature",
"id": "2",
"geometry": {
"type": "Point",
"coordinates": [2.173, 41.385]
},
"properties": {
"label": "Barcelona"
}
}
],
"totalCount": 2
}
Three rules to remember:
- Every feature must have an
id. This is the standard GeoJSONidfield (at the feature level, not insideproperties). Mapsemble uses it internally to track features across re-fetches and to identify features in remote marker, card, and popup endpoint requests. Use your primary key. - Coordinates are
[longitude, latitude]— GeoJSON order, not the Google Maps order. totalCountis required. It tells Mapsemble how many features match the current query so it can paginate correctly.
Connect to Mapsemble
- Create a new map (or open an existing one).
- Under Data Source, select Remote URL.
- Paste your endpoint URL (e.g.
https://example.com/api/map-features). - Click Save. Mapsemble will call your endpoint and display the returned features.
API Contract
Mapsemble appends query parameters to your URL. All parameters are optional — your endpoint should return sensible defaults when none are provided.
Query Parameters
| Parameter | Type | Description |
|---|---|---|
extent |
string | A WKT POLYGON describing the visible map area, e.g. POLYGON((-4.5 39.5, 3.5 39.5, 3.5 42.0, -4.5 42.0, -4.5 39.5)). |
offset |
int | Number of features to skip (pagination). Defaults to 0. |
limit |
int | Maximum number of features to return. Defaults to the map's page size. |
orderBy |
string | Sorting type slug, e.g. entropy for geographic distribution. |
filters |
string | A JSON-encoded string of active filters (see Filters section below). |
When no parameters are sent (the very first request), return all features. Mapsemble uses this initial response to calculate the map's starting bounds.
Response Format
{
"features": [
{
"type": "Feature",
"id": "node-42",
"geometry": {
"type": "Point",
"coordinates": [-3.703, 40.416]
},
"properties": {
"title": "Example Location",
"field_category": "restaurant",
"field_price": 25
}
}
],
"totalCount": 1234
}
features— Array of feature objects. Each feature must haveid(a stable unique identifier),geometry(withtypeandcoordinates), andproperties.totalCount— The total number of features matching the current filters (ignoringoffset/limit). Mapsemble uses this for pagination and the result count display.
Extent Filtering
The extent parameter is a WKT POLYGON with five coordinate pairs (the last repeats the first to close the ring). Coordinates are in longitude latitude order, separated by spaces, with pairs separated by commas:
POLYGON((-4.5 39.5, 3.5 39.5, 3.5 42.0, -4.5 42.0, -4.5 39.5))
This describes a bounding box. To extract min/max values:
- min longitude = -4.5 (left)
- max longitude = 3.5 (right)
- min latitude = 39.5 (bottom)
- max latitude = 42.0 (top)
Only return features whose coordinates fall within this bounding box.
Pagination
Mapsemble sends offset and limit to paginate through results:
offset=0&limit=25— first page (features 1–25)offset=25&limit=25— second page (features 26–50)
Your totalCount must reflect the total number of matching features (not just the current page). Mapsemble uses it to know how many pages exist and when to stop requesting.
Filters
The filters parameter is a URL-encoded JSON string. The keys are filter slugs (configured in your Mapsemble map settings) and the values depend on the filter type:
Select filter (single value):
{"field_category": "cafe"}
Checkboxes filter (multiple values — match any):
{"field_category": ["cafe", "restaurant"]}
Range filter (numeric min/max):
{"field_price": {"min": 5, "max": 20}}
Text search:
{"title": "sol"}
Date range:
{"field_date": {"start": "2026-01-01", "end": "2026-12-31"}}
Combined filters (multiple filters at once):
{
"field_category": ["cafe", "restaurant"],
"field_price": {"min": 5, "max": 20},
"title": "sol"
}
Apply all filters with AND logic — a feature must match every active filter to be included.
External Filters from the Host Page
When Mapsemble is embedded on a website, the host page can set its own filters using JavaScript — without using Mapsemble's built-in filter UI. These external filters are merged into the same filters JSON parameter that your endpoint already receives.
// Host page sets filters via the widget.js API
Mapsemble.setFilters('YOUR_MAP_ID', {
category: 'restaurant',
city: 'Madrid',
});
Your endpoint receives:
?filters={"category":"restaurant","city":"Madrid"}
If Mapsemble also has its own filters active (e.g. a date range filter configured in the map settings), both are merged:
?filters={"category":"restaurant","city":"Madrid","field_date":{"start":"2026-04-01","end":"2026-04-30"}}
Your endpoint doesn't need to distinguish between Mapsemble filters and host-page filters — they all arrive in the same filters parameter. Just decode the JSON and apply each key as a filter condition.
See the Widget.js Reference for the full setFilters() API.
Entropy Sorting
When orderBy=entropy is sent, Mapsemble expects geographically distributed results rather than clustered ones. Entropy sorting spreads features across the visible area so the first page gives a representative sample of the whole map. This is a specific orderBy value that Mapsemble sends when geographic distribution is desired.
Implementing entropy sorting is optional. If you skip it, Mapsemble still works correctly, but the first page of results may cluster in one area of the map (e.g. all features from the same city).
MySQL / MariaDB Reference
This section provides ready-to-use SQL patterns for a common setup: a Drupal-style geofield table with bounding-box columns (left, right, top, bottom).
Extent Filtering
Given a table with bbox columns (field_geo_left, field_geo_right, field_geo_top, field_geo_bottom) and point columns (field_geo_lon, field_geo_lat):
SELECT *
FROM map_features
WHERE field_geo_lon BETWEEN :min_lon AND :max_lon
AND field_geo_lat BETWEEN :min_lat AND :max_lat
Where :min_lon, :max_lon, :min_lat, :max_lat are extracted from the WKT POLYGON as described in the Extent Filtering section above.
If your table stores bounding boxes per feature (e.g. polygons), use overlap logic instead:
SELECT *
FROM map_features
WHERE field_geo_right >= :min_lon
AND field_geo_left <= :max_lon
AND field_geo_top >= :min_lat
AND field_geo_bottom <= :max_lat
Entropy Sorting
MySQL 8.0+ / MariaDB 10.7+ — use ST_GeoHash with window functions:
SELECT *, ROW_NUMBER() OVER (
PARTITION BY LEFT(ST_GeoHash(ST_PointFromText(
CONCAT('POINT(', field_geo_lon, ' ', field_geo_lat, ')')
), 3), 3)
ORDER BY RAND()
) AS geo_rank
FROM map_features
WHERE field_geo_lon BETWEEN :min_lon AND :max_lon
AND field_geo_lat BETWEEN :min_lat AND :max_lat
ORDER BY geo_rank, ST_GeoHash(ST_PointFromText(
CONCAT('POINT(', field_geo_lon, ' ', field_geo_lat, ')')
), 6)
LIMIT :limit OFFSET :offset
This groups features into geohash cells, numbers them within each cell, then interleaves the groups so the first results are spread across the map.
Fallback (all MySQL/MariaDB versions) — use a grid based on FLOOR:
SELECT *, FLOOR(field_geo_lon * 10) AS grid_x,
FLOOR(field_geo_lat * 10) AS grid_y
FROM map_features
WHERE field_geo_lon BETWEEN :min_lon AND :max_lon
AND field_geo_lat BETWEEN :min_lat AND :max_lat
ORDER BY (grid_x * 7 + grid_y * 13) MOD 1000, field_geo_lon
LIMIT :limit OFFSET :offset
The prime multipliers and modulo create a pseudo-random distribution across grid cells.
Filter Examples
Select filter (taxonomy term):
SELECT f.*
FROM map_features f
INNER JOIN feature_terms ft ON ft.feature_id = f.id
INNER JOIN terms t ON t.id = ft.term_id
WHERE t.name = :category
Range filter (numeric field):
SELECT *
FROM map_features
WHERE field_price BETWEEN :min_price AND :max_price
Performance Recommendations
-
Add a spatial or bbox index. At minimum, create a composite index on your longitude and latitude columns:
CREATE INDEX idx_geo_bbox ON map_features ( field_geo_lon, field_geo_lat ); -
Cache
totalCountfor expensive queries. If your dataset is large (100k+ rows) and filters are complex, consider caching the count in a summary table or using a separateCOUNT(*)query with the sameWHEREclause. Note:SQL_CALC_FOUND_ROWSis deprecated in MySQL 8.0.17+ and should be replaced with a separateCOUNT(*)query. -
Large datasets (1M+ rows). Consider MySQL spatial indexes with
SPATIAL INDEXon aPOINTcolumn for better performance than bbox column comparisons.
Database Compatibility
| Feature | MySQL 5.7 | MySQL 8.0+ | MariaDB 10.6 | MariaDB 10.7+ | PostgreSQL |
|---|---|---|---|---|---|
| Extent filtering (bbox) | Yes | Yes | Yes | Yes | Yes |
| Entropy (geohash + window) | No | Yes | No | Yes | Yes |
| Entropy (FLOOR fallback) | Yes | Yes | Yes | Yes | Yes |
| Spatial indexes | Yes | Yes | Yes | Yes | Yes (PostGIS) |