API documentation
Every Reforger map Enfumap has, its named places, and positions described in words. JSON over HTTPS, read only.
- No key needed
- Any site can call it (CORS)
- Versioned: v1
- Cached, fast
- 10 requests a second
https://enfumap.com/api/v1Every answer has the same shape: { "ok": true, "data": … } when it worked, and { "ok": false, "error": "code", "message": "what went wrong" } with a 4xx status when it didn't.
Coordinates
Positions are world metres, the same numbers the game and its scripts use. x runs east and z runs north, both from the map's bottom-left corner. A map is worldSize metres across.
As on the in-game map. 3 + 3 digits for a 100 m square (047 070), 4 + 4 for 10 m (0477 0709).
Bearings are degrees clockwise from north. locate also gives the 8 point direction: N, NE, E…
Errors
unknown_map404There's no live map with that slug. List them with /api/v1/maps.bad_position400x or z is missing or isn't a number.off_map400The position is outside the map (it's worldSize metres across).rate_limited429Over the rate limit. Wait the seconds in the Retry-After header, then carry on.Rate limits
Each address can make 10 requests a second, with bursts of up to 40 at once, so a page that fires off a few calls as it loads is fine. Go over it and you get a 429 with "error": "rate_limited" and a Retry-After header saying how many seconds to wait. Nothing is blocked for longer than that.
Map data hardly ever changes, so the easiest way to stay well under the limit is to keep what you fetch: a map's places only change when the map is rebuilt, and its tiles.url carries a version that changes when it does. Building something that needs more? Get in touch.
List maps
/api/v1/mapsEvery live map: its title, whether it's official or community made, its size, how many named places it has, a preview image and the address of its tiles.
No parameters.
curl "https://enfumap.com/api/v1/maps"
Press Try it to call the live API.
Get a map
/api/v1/maps/{slug}One map, with everything needed to draw it: tile address, tile size, zoom range and metres per pixel at full detail, plus its author and Workshop ID.
slugrequiredpathThe map's name in its address: everon, arland, kolguyev…curl "https://enfumap.com/api/v1/maps/everon"
Press Try it to call the live API.
Search places
/api/v1/maps/{slug}/placesThe map's named places: towns, villages, hills, bays, lakes and landmarks, with their world position. Search by name (accents and punctuation don't matter), filter by kind, or leave q off to get every place.
slugrequiredpathThe map.qstringName to search for. Every word has to match, so "saint pierre" finds Saint-Pierre.kindstringComma separated kinds: capital, city, town, village, local, landmark, hill, ridge, valley, island, stream, lake, bay, sea.limitintegerHow many to return, 1 to 500. Defaults to 20 when searching, 500 otherwise.curl "https://enfumap.com/api/v1/maps/everon/places?q=saint%20pierre"
Press Try it to call the live API.
Describe a position
/api/v1/maps/{slug}/locateTurns a position into words: the in-game grid reference and where it is from the nearest settlement, like "306 m NE of Montignac". Made for kill feeds, reports and server logs.
slugrequiredpathThe map.xrequirednumberMetres east from the map's left edge.zrequirednumberMetres north from the map's bottom edge.curl "https://enfumap.com/api/v1/maps/everon/locate?x=5000&z=7300"
Press Try it to call the live API.
Health
/api/v1/healthWhether the API is up and how many maps it's serving. Handy for uptime checks.
No parameters.
curl "https://enfumap.com/api/v1/health"
Press Try it to call the live API.
Drawing a map
Tiles are 256 px WebP in the usual {z}/{x}/{y} layout, zoom 0 being the whole map in one tile, and each zoom drawn from the game's map data rather than scaled up. A tile's address changes when its map is rebuilt, so they're safe to cache forever. With Leaflet, a flat projection that maps metres straight to pixels does it:
import L from "leaflet";
const { data: map } = await (await fetch("https://enfumap.com/api/v1/maps/everon")).json();
const s = map.tiles.tileSize / map.worldSize;
const view = L.map("map", {
crs: L.extend({}, L.CRS.Simple, { transformation: new L.Transformation(s, 0, -s, map.tiles.tileSize) }),
maxZoom: map.tiles.maxNativeZoom + 2,
});
L.tileLayer("https://enfumap.com" + map.tiles.url, {
tileSize: map.tiles.tileSize,
maxNativeZoom: map.tiles.maxNativeZoom,
noWrap: true,
}).addTo(view);
// positions are [z, x] in world metres
view.setView([7094, 4773], 4);
L.marker([7094, 4773]).addTo(view).bindPopup("Montignac");