Node.js

Mapricot's API is plain HTTPS and JSON, so Node.js can call it with its built-in fetch, with no package to install. This page calls each API: geocoding, places, routing, and IP geolocation, and shows where the free map tiles fit. It was tested with Node.js 22, and works with Node.js 18 and later.

Set up

Create a server key in the dashboard with the products you'll use: Places for geocoding and places, Routing, and IP geolocation. Set it as an environment variable, so it stays out of your code:

export MAPRICOT_KEY="mapricot_sk_…"

Then add a function that sends the key with every request, retries when the API is busy, and throws on errors. The examples on this page go in an ES module, such as mapricot.mjs, so they can use await at the top level:

const key = process.env.MAPRICOT_KEY;

async function mapricot(path, { params, body } = {}) {
  const url = `https://api.mapricot.com${path}${params ? `?${new URLSearchParams(params)}` : ""}`;
  for (let attempt = 1; ; attempt++) {
    const response = await fetch(url, {
      method: body ? "POST" : "GET",
      headers: { Authorization: `Bearer ${key}`, ...(body && { "Content-Type": "application/json" }) },
      body: body && JSON.stringify(body),
      signal: AbortSignal.timeout(30_000),
    });
    if ((response.status === 429 || response.status === 503) && attempt < 4) {
      await new Promise((resolve) => setTimeout(resolve, 1000 * 2 ** attempt));
      continue;
    }
    const json = await response.json();
    if (!response.ok) throw new Error(`Mapricot returned ${response.status}: ${json.error.message}`);
    return json;
  }
}

Geocode an address

/v1/search turns an address or place name into coordinates:

const { results } = await mapricot("/v1/search", { params: { q: "128 SW 3rd Ave, Portland", limit: 1 } });
for (const result of results) {
  console.log(result.label, result.lat, result.lon);
}
128 SW 3rd Avenue, Portland, OR, USA 45.521736 -122.673276

Each result also has its address in parts under address, and a type, such as address, street, or locality, that says how exact the match is. To limit results to some countries, pass country: "US,CA". Geocoding lists every parameter and field.

Find the address at a point

/v1/reverse turns coordinates into the nearest address:

const { results: [nearest] } = await mapricot("/v1/reverse", { params: { lat: 45.5217, lon: -122.6733, limit: 1 } });
console.log(nearest.label, nearest.distanceMeters);
128 SW 3rd Avenue, Portland, OR, USA 4

Geocode a list of addresses

const addresses = ["128 SW 3rd Ave, Portland", "1600 Pennsylvania Avenue NW, Washington"];
const geocoded = [];
for (const address of addresses) {
  const { results } = await mapricot("/v1/search", { params: { q: address, limit: 1 } });
  geocoded.push({ address, lat: results[0]?.lat, lon: results[0]?.lon, label: results[0]?.label });
}
console.table(geocoded);

It sends one request at a time, on purpose. Each account has a limit on requests in progress at once, so Promise.all over many addresses gets 429 responses. Addresses with no match get empty coordinates. Compare label with your address to check the match.

Each address is one request. The first 250,000 every 30 days are free, then they cost $0.10 per 1,000. See pricing.

Suggest places as someone types

/v1/autocomplete takes the text typed so far. focus ranks results near a point first:

const { results: [suggestion] } = await mapricot("/v1/autocomplete", { params: { q: "stumpt", focus: "45.52,-122.68", limit: 1 } });
console.log(suggestion.label, suggestion.id);
Stumptown Coffee Roasters, Portland, OR, USA mp_b3BlbnN0cmVldG1hcDp2ZW51ZTpub2RlLzExOTg2MjI2NjYw

Find places nearby

/v1/nearby lists places of a category around a point, nearest first:

const nearby = await mapricot("/v1/nearby", {
  params: { lat: 45.52, lon: -122.68, category: "food:coffee_shop", radius: 1000, limit: 1 },
});
for (const place of nearby.results) {
  console.log(place.name, place.distanceMeters);
}
Public Domain 37

Look up a place by ID

Every result has an id. Save it, and /v1/places/{id} fetches that place again later:

const { result: place } = await mapricot(`/v1/places/${suggestion.id}`);
console.log(place.label);
Stumptown Coffee Roasters, Portland, OR, USA

Get a route

/v1/route takes a JSON body with two to 20 waypoints and a mode of drive, walk, or bicycle:

const { routes } = await mapricot("/v1/route", {
  body: { waypoints: [{ lat: 43.7396, lon: 7.4275 }, { lat: 43.734, lon: 7.4222 }], mode: "walk" },
});
console.log(routes[0].distanceMeters, routes[0].durationSeconds);
1044 739

Distances are in meters and times in seconds. When no path connects the waypoints, routes is empty. The route's line is in polyline, encoded with six decimal places. To get its points, npm install @mapbox/polyline and decode with precision 6, as decoding a route polyline explains:

import polyline from "@mapbox/polyline";

const points = polyline.decode(routes[0].polyline, 6);

Look up an IP address

/v1/ip/{ip} gives the approximate location of an IPv4 or IPv6 address:

const location = await mapricot("/v1/ip/8.8.8.8");
console.log(location.city, location.countryCode);
Mountain View US

Show a map

Map tiles are free and need no key. MapLibre GL JS draws them in the browser from https://tiles.mapricot.com/styles/light.json, so Node.js doesn't fetch them: send your results to a web page, and draw them there, as Map tiles shows. MapLibre takes coordinates as [lon, lat].

Errors and retries

mapricot throws an Error for any error response, with the status and the API's message:

try {
  await mapricot("/v1/search", { params: { q: "" } });
} catch (error) {
  console.log(error.message);
}
Mapricot returned 400: The service rejected the request.

It retries 429 and 503 responses up to three times, waiting 2, 4, then 8 seconds. Errors aren't billed, so retrying them costs nothing. A 429 that keeps coming back can also mean the account is out of credit. The API reference lists every status.

Questions

Can I use node-geocoder with Mapricot? node-geocoder doesn't include a provider for Mapricot. The mapricot function above does the same job with no dependencies, and takes every parameter the API does.

Is there a Mapricot npm package? Not yet. The API is small enough that fetch covers it, as above.

Can I call the API from the browser instead? Yes. The same fetch code works in a web page. A key in a page is visible to anyone who opens it, so use a browser key there, which works only from the origins you allow, and keep server keys on your server.