JavaScript in the browser
The mapricot package calls each Mapricot API from a web page: geocoding, places, routing, and IP geolocation. It's the same package as on Node.js, and uses the browser's own fetch, with no dependencies. Load it from a CDN in a plain HTML page, or install it from npm in an app with a bundler. This page shows each API, and a map of the results on the free map tiles. Its source is on GitHub.
Create a browser key
Code in a web page is visible to anyone who opens it, and so is any key in it. Use a browser key there, which starts with mapricot_pk_ and works only from the sites you allow, and never a server key. Create one in the dashboard with the products the page uses: Places for geocoding and places, Routing, and IP geolocation. Under Allowed origins, list your site, such as https://example.com, and http://localhost:*, so it also works from your computer while you build. Cap its requests per minute and per 30 days, so a copied key can't use up your free requests or credit. API keys explains each setting, and how to rotate a key.
Load the client
From a CDN
A page with no build step imports the client from jsDelivr, which serves any npm package. Save this as index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Mapricot</title>
</head>
<body>
<script type="module">
import { Mapricot } from "https://cdn.jsdelivr.net/npm/mapricot@latest/+esm";
const client = new Mapricot({ apiKey: "mapricot_pk_…" });
</script>
</body>
</html>
Put your browser key in place of mapricot_pk_…. type="module" lets the script import, and use await outside a function. +esm asks jsDelivr for the package as a module, and @latest loads the newest release. To keep the page from changing when a new version comes out, put the version in the URL instead, such as mapricot@0.3.0.
Serve the folder with a local web server, and open the address it prints: run npx serve (with Node.js) or python3 -m http.server in the folder. A page opened straight from a file has no origin, so the API refuses the key with 403 and "Key is not allowed from this origin."
With a bundler
In an app built with a bundler, such as Vite, install the package from npm:
npm install mapricot
Under Vite, put the key in a file called .env.local, which new Vite apps leave out of git:
VITE_MAPRICOT_KEY=mapricot_pk_…
Then import the client, and make it with the key:
import { Mapricot } from "mapricot";
const client = new Mapricot({ apiKey: import.meta.env.VITE_MAPRICOT_KEY });
Vite puts variables that start with VITE_ into the built page, where anyone can read them. That's fine for a browser key, so never give a server key that prefix. The package includes TypeScript types. MapLibre React builds a whole Vite app with it.
The examples below go in the module script, after client, and print to the browser's console. Open your browser's developer tools to see them.
Geocode an address
search turns an address or place name into coordinates:
const { results } = await client.search("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
reverse turns coordinates into the nearest address:
const { results: [nearest] } = await client.reverse(45.5217, -122.6733, { limit: 1 });
console.log(nearest.label, nearest.distanceMeters);
128 SW 3rd Avenue, Portland, OR, USA 4
Suggest places as someone types
autocomplete takes the text typed so far. focus ranks results near a point first:
const { results: [suggestion] } = await client.autocomplete("stumpt", { focus: { lat: 45.52, lon: -122.68 }, limit: 1 });
console.log(suggestion.label, suggestion.id);
Stumptown Coffee Roasters, Portland, OR, USA mp_b3BlbnN0cmVldG1hcDp2ZW51ZTpub2RlLzExOTg2MjI2NjYw
In a search field, answers can arrive out of order, and a slow answer for "1005 W" can replace the newer one for "1005 W Burnside". Every method takes a signal, so cancel the last request when the text changes:
const input = document.querySelector("input");
let controller;
input.addEventListener("input", async () => {
controller?.abort();
controller = new AbortController();
try {
const { results } = await client.autocomplete(input.value, { limit: 5, signal: controller.signal });
console.log(results.map((result) => result.label));
} catch (error) {
if (error.name !== "AbortError") throw error;
}
});
A cancelled request throws an AbortError, which the listener ignores, since a newer request is already on its way. Address autocomplete builds a whole field from this, with a list of suggestions, the keyboard, and screen readers.
Find places nearby
nearby lists places of a category around a point, nearest first:
const nearby = await client.nearby(45.52, -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 place fetches that place again later:
const { result: place } = await client.place(suggestion.id);
console.log(place.label);
Stumptown Coffee Roasters, Portland, OR, USA
Get a route
route takes two to 20 waypoints and a mode of drive, walk, or bicycle:
const { routes } = await client.route([{ 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, decode it with precision 6, as decoding a route polyline explains:
import polyline from "https://cdn.jsdelivr.net/npm/@mapbox/polyline@1.2.1/+esm";
const points = polyline.decode(routes[0].polyline, 6);
With a bundler, npm install @mapbox/polyline and import from @mapbox/polyline instead. Drawing a route on a MapLibre map draws the line, with stops to drag.
Look up an IP address
ip gives the approximate location of an IPv4 or IPv6 address:
const location = await client.ip("8.8.8.8");
console.log(location.city, location.countryCode);
Mountain View US
A page doesn't know its visitor's IP address, but your server sees it on each request. Have the server put it in the page, or look it up on the server with a server key, as Node.js shows.
Show a map
Map tiles are free and need no key. MapLibre GL JS draws them. This page finds an address and puts a marker on it. Save it as index.html, and put your key in place of mapricot_pk_…:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>128 SW 3rd Ave</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@6.15.0/dist/maplibre-gl.css">
</head>
<body style="margin: 0">
<div id="map" style="height: 100vh"></div>
<script type="module">
import * as maplibregl from "https://cdn.jsdelivr.net/npm/maplibre-gl@6.15.0/+esm";
import { Mapricot } from "https://cdn.jsdelivr.net/npm/mapricot@latest/+esm";
const client = new Mapricot({ apiKey: "mapricot_pk_…" });
const { results: [result], attribution } = await client.search("128 SW 3rd Ave, Portland", { limit: 1 });
const map = new maplibregl.Map({
container: "map",
style: "https://tiles.mapricot.com/styles/light.json",
center: [result.lon, result.lat],
zoom: 16,
attributionControl: { customAttribution: attribution.map((credit) => credit.text) },
});
new maplibregl.Marker().setLngLat([result.lon, result.lat]).addTo(map);
</script>
</body>
</html>
MapLibre takes coordinates as [lon, lat], the reverse of the results' lat and lon. The style credits OpenStreetMap, and customAttribution adds the credits for the sources in the results, which you should show whenever you show them. Map tiles explains the rest of the page, and the guides build more: markers and popups, a search box, and clustering points.
Errors and retries
The client throws a MapricotError for any error response, with the HTTP status, the API's error code, its message, and the requestId:
import { MapricotError } from "https://cdn.jsdelivr.net/npm/mapricot@latest/+esm";
try {
await client.search("");
} catch (error) {
if (!(error instanceof MapricotError)) throw error;
console.log(error.status, error.code, error.message);
}
400 invalid_request The service rejected the request.
A key used from a site that isn't in its allowed origins gets 403 with "Key is not allowed from this origin." Add the page's origin to the key in the dashboard.
The client retries 503 responses, and 429 responses that say too many requests are in progress, up to twice, waiting longer each time. Errors aren't billed, so retrying them costs nothing. Other 429 codes, such as credit_exhausted, or rate_limited and period_cap_reached when the key's caps are used up, don't clear up by waiting a few seconds, so the client throws them right away. Set maxRetries and timeout, in milliseconds, in the options passed to new Mapricot to change this. The API reference lists every status and code.