Geocode an address in JavaScript and Node.js
This tutorial uses only the built-in fetch of Node 18 or later (or a browser). It geocodes one address, handles the rate limit politely, then geocodes a list with a small queue. A typed client exists too: see the TypeScript SDK.
The base URL https://api.addrova.eu is not live yet. Everything below follows the API specification. [À VÉRIFIER : service ouvert, URL de base définitive.]
1. One address
const resp = await fetch("https://api.addrova.eu/v1/geocode", {
method: "POST",
headers: { "X-API-Key": process.env.ADDROVA_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ q: "4 place d'Armes, Belfort", country: "FR", limit: 1 }),
});
if (!resp.ok) throw new Error((await resp.json()).code);
const { results, attribution } = await resp.json();
const best = results[0];
console.log(best.label, best.location, best.precision, best.confidence_level);
console.log(attribution.map((a) => a.text));
The address travels in the body of a POST, not in the URL, so it never appears in a URL or query string (the API logs only the route template, ZL-01). Keep the attribution with the result if you store it.
2. Wrap it, and retry on a rate limit
When you exceed your rate, the API answers 429 with a Retry-After header. Honour it instead of hammering.
async function geocode(q, country = "FR", attempts = 3) {
for (let i = 0; i < attempts; i++) {
const resp = await fetch("https://api.addrova.eu/v1/geocode", {
method: "POST",
headers: { "X-API-Key": process.env.ADDROVA_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ q, country, limit: 1 }),
});
if (resp.status === 429 && i < attempts - 1) {
const wait = Number(resp.headers.get("retry-after") ?? 1);
await new Promise((r) => setTimeout(r, wait * 1000));
continue;
}
if (!resp.ok) throw new Error(`${resp.status} ${(await resp.json()).code}`);
return (await resp.json()).results[0] ?? null;
}
}
const hit = await geocode("4 place d'Armes, Belfort");
console.log(hit?.label ?? "not found");
3. A list, with a small queue
A short list does not need a batch job. Run a few requests at a time rather than all at once.
async function geocode(q) {
const resp = await fetch("https://api.addrova.eu/v1/geocode", {
method: "POST",
headers: { "X-API-Key": process.env.ADDROVA_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({ q, country: "FR", limit: 1 }),
});
if (!resp.ok) throw new Error(`${resp.status}`);
return (await resp.json()).results[0] ?? null;
}
async function geocodeAll(addresses, concurrency = 4) {
const out = new Array(addresses.length);
let next = 0;
const worker = async () => {
while (next < addresses.length) {
const i = next++;
out[i] = await geocode(addresses[i]);
}
};
await Promise.all(Array.from({ length: Math.min(concurrency, addresses.length) }, worker));
return out;
}
const rows = await geocodeAll(["4 place d'Armes, Belfort", "10 rue de Rivoli, Paris"]);
console.log(rows.map((r) => r?.label ?? null));
For thousands of rows, use the batch endpoint or the CSV geocoder instead of a loop.
Good habits
- Do not put a live key in a public page. In a browser, use a key restricted by origin or relay the call from your server.
- Check
confidence_levelandprecisionbefore using a point: see precision levels and confidence thresholds. - Never log the address you send. The service writes no address to its logs (ZL-10); your own code should not either.