API reference
Address autocomplete, place search, reverse and batch geocoding over 477 million addresses and 67 million places in 198 countries. Eight endpoints, one credential, billed by the address, not the keystroke.
Quickstart
One key, one endpoint, one session token reused while the user types. This is the same
/autocomplete example documented in full below -- it is repeated here because
it is the single most-used part of this page and the one thing worth reading before
anything else: session is the billing unit.
# `session` groups every keystroke of ONE address lookup into a SINGLE
# billable session -- reuse the same token on each call, drop it once the
# user picks a result. See "Sessions and what you are billed for" below.
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://wayfindnow.com/autocomplete?q=Damrak+1+Amsterdam&session=9f2e1c-a-user-picked-uuid"// `session` is the billing unit: reuse the SAME token on every keystroke of one
// address lookup and the whole lookup is billed ONCE, not once per request.
const res = await fetch(
"https://wayfindnow.com/autocomplete?q=Damrak+1+Amsterdam&session=" + sessionToken,
{ headers: { Authorization: "Bearer " + apiKey } }
);
const rows = await res.json(); // [] on no matches -- that is not an errorimport requests
# `session` groups every keystroke of ONE address lookup into a SINGLE billable session.
# Mint one token (any UUID) per lookup, reuse it on every call, then start a new one.
r = requests.get(
"https://wayfindnow.com/autocomplete",
params={"q": "Damrak 1 Amsterdam", "session": session_token},
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
rows = r.json()Your key is on your account page under "Your API key"; named keys with their own limits live under API keys. Full reference for every endpoint is in the left nav: /autocomplete, /search, /nearby, /place/{id}, /chose, /reverse, /parse and /batch.
Authentication
Send your key as a bearer token: Authorization: Bearer YOUR_API_KEY. If you
cannot set headers, ?key=YOUR_API_KEY in the query string works the same way --
prefer the header where you can, since query strings end up in access logs.
Every endpoint on this page requires a valid key. A missing or unrecognised one gets a
plain 401 (not JSON -- see Errors below). /health
is the one route that stays open, for load balancers that cannot hold a credential.
Sessions and what you are billed for
The unit that is billed is a session, not a request.
Someone typing "Rotterdam" fires an /autocomplete call after almost every
keystroke -- that is normal and expected, and it is not eight charges. Generate one token
per address lookup (a UUID you make up client-side is fine) and send it as
&session= on every call that belongs to that lookup: every keystroke's
autocomplete request, and the request that resolves whichever suggestion got picked. All of
it counts as one session.
The token is opaque to us on purpose -- send a UUID, not something that identifies a person. It is hashed the moment it arrives and only the hash is kept; the raw value is never logged or returned.
A session is not open-ended: one session covers up to 30 requests, which is roughly four times what a real address lookup takes. Anything past that is billed per request, so reusing a single token for unrelated lookups costs the same as not sending one at all. Use a fresh token per address and you will never meet this limit.
Your account page shows sessions (the billable count) alongside
requests and requests_per_session -- the last one is "how many
keystrokes does one address cost you", which is the number worth watching if you are
migrating pricing expectations from another provider.
session is not exclusive to /autocomplete: it is read once,
before routing, so any request to any endpoint that carries the same token is folded into
the same session. If a lookup happens to span /autocomplete and a
/search or /nearby call, pass the same token to all of them and it
is still one session.
The session is a discount you earn by finishing the lookup
One rule, three cases. It is the same rule Google Places applies to an abandoned session, so if you are porting an integration the behaviour will already be familiar:
| What happened | What is billed |
|---|---|
session sent, and a suggestion was resolved
(/chose or /place/) |
one session, however many keystrokes it took |
session sent, nothing ever resolved |
every request, individually |
no session at all |
every request, individually |
So sending the token is worth doing, and closing the loop with
/chose when the user picks is what actually converts the discount. Omitting
the token still works -- nothing breaks, you simply pay per request.
Choosing what comes back: fields
By default a suggestion carries what a dropdown needs: id,
main_text, secondary_text, text, type,
kind, the matched spans, and cc -- the two-letter
country of the row. Keep cc: passing it back as country= on
/place/{id} is the difference between a 0.06s lookup and a 56s one.
Coordinates and the structured address are opt-in:
| fields | adds |
|---|---|
| (omitted) | the display set above |
fields=geometry | location — lat/lon |
fields=address | components — the address in parts, see below |
fields=geometry,address | both |
fields=* | the whole record |
A name we have not shipped yet is ignored rather than rejected, so adding a
tier to your client is always safe. id is always returned — it is the one
field you may store indefinitely (see below).
The address in parts: components
A bill of lading has one box for the street and a different box for the state. These are document fields, copied — never sliced back out of the rendered label, which would break the day we change a separator.
| key | what it is | example |
|---|---|---|
street | the street or road name | Damrak |
house_number | as written locally, ranges and letters included | 1-5, 12A |
sublocality | a part of a town — quarter, hamlet | — |
locality | the town or city | Amsterdam |
subregion | the tier between town and region — county, département, provincia. Not yet returned, see below | — |
region | the top subdivision — the "State / Province" box | Florida, Île-de-France, 東京都 |
postcode | postal code | 1012LG |
country | country name | Netherlands |
country_code | ISO 3166-1 alpha-2 | NL |
A key is absent when we do not hold that tier, never guessed. The
absence is information: no house_number means the row is a street rather than
an address, and a missing region means we could not establish one for this
address — not that the country has no states. Nothing is ever back-filled from the
tier above, because a guessed province on a customs form is worse than a blank one.
subregion is named here and does not yet appear in any
response. We hold 47,593 of those areas as records, but they are not yet linked to the
towns inside them, so no address can reach one. The name is reserved so it can start
appearing without a breaking change; write your client to tolerate its absence, which is
the same rule every key here follows.
admin_area is deprecated. It is the old name
for region and still carries the identical value. Move to
region; admin_area will be removed in a future version.
Coming from Google
The mapping is mechanical. We name each part by the role it plays rather than by
its depth in the hierarchy, because depth is the one thing you cannot act on:
administrative_area_level_1 is a state in the US, a région in France and
a constituent country in the UK, so reading it still means knowing the country first.
| here | |
|---|---|
street_number | house_number |
route | street |
sublocality | sublocality |
locality | locality |
administrative_area_level_2 | subregion |
administrative_area_level_1 | region |
postal_code | postcode |
country | country + country_code |
If you need the depth as a number rather than a name — walking the
hierarchy generically rather than filling a form — /place/{id}
returns admin_chain, where every rung carries both level (the name
above) and level_number (1 country, 2 region, 3 subregion, 4 locality,
5 sublocality).
What you may store
Store the id, refresh the record. Our id is stable and
yours to keep for as long as you like — put it in your orders table and re-resolve it
whenever you need the address. The rest of a response may be cached for up to 30 days.
Building a persistent copy of the database is not permitted.
This is better for you as well as for us: an address that is re-resolved stays correct when a street is renamed or a postcode is reallocated.
Locking a key down
A browser key is published by design — it ships in your page source. Every key can therefore be restricted, and an unrestricted key behaves exactly as it always has:
| restriction | refuses |
|---|---|
| allowed origins | requests from any site but yours
(https://shop.example.com, https://*.example.com) |
| allowed IPs | server-side calls from anywhere but your machines (single addresses or CIDR) |
| allowed endpoints | a widget key reaching /batch |
A refusal is 403, not 401, and the message names which list
refused — the common case is that you have just locked a key down and need to know
which one to widen.
Be clear on what an origin restriction is worth: browsers send
Origin, and anything that is not a browser can forge it. It stops your key
working on somebody else's website. It does not stop a determined attacker with a shell.
Language: lang and Accept-Language
Two independent things share the word "language" here, and it is worth keeping them apart:
- What you typed decides what matches. Untouched by anything below.
- What name is shown back to you -- a Munich/München/מינכן/慕尼黑 pick --
is decided, in order: an explicit
?lang=xx(a two-letter code) if you send one; otherwise yourAccept-Languageheader, parsed per RFC 9110 with its;q=weights, first match wins; and for/autocompleteand/searchonly, if neither is present, the script of what you typed decides it (a Hebrew query gets a Hebrew label) before falling back to English.
/nearby has no query text to detect a script from, so with no
lang given it defaults to plain en -- Accept-Language
still applies if you send it, only the query-script fallback is unavailable there.
The country filter
An optional two-letter code (NL, BG, ...) that scopes the
search to one country's index instead of all 198 -- faster, and it removes cross-country
ambiguity for short names. All three endpoints accept it. On /autocomplete,
if you do not pass it, a country named inside the query itself ("Sofia Bulgaria") can still
scope the search the same way, on top of everything you would get for free otherwise.
Rate limits
Each key is a token bucket refilled continuously at your configured requests/second
(20 req/s is the default for a key with no custom rate configured), with
a burst allowance of 3 seconds' worth of tokens -- typing five characters
in a second is normal and will not be throttled. Once the bucket is empty you get
429 with a Retry-After: 1 header and no body;
wait a second and retry.
Errors
Error responses are plain HTML error pages (the Python stdlib's default), not JSON --
with one exception, 402, which is JSON. Do not .json() a response
blindly. Check the status code first.
| status | when | what to do |
|---|---|---|
400 |
a required parameter is missing or malformed (q on /search,
lat/lon on /nearby, a non-integer limit), or a
kind you asked for is not one we recognise. |
fix the parameter named in the response body and retry. For an unknown kind
the body lists every valid value, so it is worth validating client-side before the call
rather than round-tripping to find out. |
401 |
missing or unrecognised API key. | check that Authorization: Bearer ... (or ?key=) carries a
real key from your account page. Retrying with the same key will not succeed --
that is not a transient failure. |
404 |
the path itself does not exist. | check the endpoint path and method. A query that legitimately finds nothing is not
this -- that is 200 with an empty result, see below. |
402 |
this account's prepaid credit is used up. The one error that is JSON:
{"error": "credit exhausted", "detail": ...}. |
add credit on your account page. Waiting does not help and retrying does not either --
an empty balance does not refill on its own, which is why this is not a
429. |
403 |
the key is genuine but a restriction on it refused this call -- see Locking a key down above. The body names which list (origins, IPs or endpoints) refused. | widen that list on the API keys page. Do not go looking for a different key; the credential is fine. |
413 |
a /batch call carried more than 500 addresses. |
split the list. |
429 |
two different things, told apart by Retry-After. 1: this
key's token bucket is empty -- see Rate limits above. 3600: a hard-capped
plan is over its cap for the billing period. |
wait for the interval in the header and retry once. Retrying immediately in a tight loop just keeps the bucket empty; for the hourly value, nothing you send before then will be served. |
503 |
/place/{id} could not reach the database in time. Sent with
Retry-After: 2 and no body. |
retry once after two seconds. If you called without country=, add it
(see /place/{id}) -- the unhinted lookup is the one that times out. |
A search that simply finds nothing is not an error -- it is
200 with an empty list (or, for /search, a results: [] with a
reason).
GET /autocomplete
Address, street and place suggestions as someone types. Examples below use the public host, https://wayfindnow.com. The try-it panel does not hard-code that -- it calls relative to wherever this page is served, so it keeps working if the host changes. If you have an older integration pointed at https://wayfindnow.com, move it: that form is retired, and it sends your key over plain HTTP.
# `session` groups every keystroke of ONE address lookup into a SINGLE
# billable session -- reuse the same token on each call, drop it once the
# user picks a result. See "Sessions and what you are billed for" below.
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://wayfindnow.com/autocomplete?q=Damrak+1+Amsterdam&session=9f2e1c-a-user-picked-uuid"// `session` is the billing unit: reuse the SAME token on every keystroke of one
// address lookup and the whole lookup is billed ONCE, not once per request.
const res = await fetch(
"https://wayfindnow.com/autocomplete?q=Damrak+1+Amsterdam&session=" + sessionToken,
{ headers: { Authorization: "Bearer " + apiKey } }
);
const rows = await res.json(); // [] on no matches -- that is not an errorimport requests
# `session` groups every keystroke of ONE address lookup into a SINGLE billable session.
# Mint one token (any UUID) per lookup, reuse it on every call, then start a new one.
r = requests.get(
"https://wayfindnow.com/autocomplete",
params={"q": "Damrak 1 Amsterdam", "session": session_token},
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
rows = r.json()| param | required | default | meaning |
|---|---|---|---|
q | yes | — | the text typed so far. |
session | no | none | groups this call into a billable session, see above. |
country | no | auto | a two-letter code to scope the search to one country. |
lang | no | auto | two-letter code; see Language above. |
lat, lon | no | none | send both to bias ranking toward this point (a Gauss decay, not a filter) -- a distant match still ranks, just lower. This is different from /nearby's lat/lon, which is a hard search radius. |
There is no limit parameter on this endpoint -- it always
returns up to 10 rows. (Verified live: adding &limit=2 to a query that
otherwise returns 10 rows still returns 10.) If you need a different count, that is what
/search and /nearby are for.
Response
A JSON array, best match first. Each row:
| param | required | default | meaning |
|---|---|---|---|
id | the entity's id. Stable; safe to use as a React key. | ||
type | address, street, locality or place. | ||
kind | a category (port, airport, ...) for place rows, null otherwise. | ||
text, main_text, secondary_text | ready-to-render label, split the way Google's autocomplete widget splits it (bold part / grey part). | ||
matched | [[start, length], ...] spans into main_text -- the part that justifies the match, for highlighting it. | ||
cc | the row's two-letter country. Always present. Pass it back as country= when you call /place/{{id}}. | ||
components | <em>opt-in</em> | only with fields=address: the address broken into parts -- street, house_number, locality, region, postcode, country, country_code. Each key is present only when that entity actually has it, so its absence is itself information. Full list and the mapping from Google's names: Address components. | |
location | <em>opt-in</em> | only with fields=geometry: {{"lat":..,"lon":..}}, when known. |
Real response, captured live and trimmed to two rows:
[
{"id": "a604088905", "type": "address", "kind": null,
"text": "Damrak 1-5, 1012LG Amsterdam, Netherlands",
"main_text": "Damrak 1-5", "secondary_text": "1012LG Amsterdam, Netherlands",
"matched": [[0, 6]], "cc": "NL"},
{"id": "a130895906", "type": "address", "kind": null,
"text": "Damrak 1, 1012LG Amsterdam, Netherlands",
"main_text": "Damrak 1", "secondary_text": "1012LG Amsterdam, Netherlands",
"matched": [[0, 6]], "cc": "NL"}
]GET /search
A named place plus an optional category, when you have a place name but not
coordinates -- "ports near Rotterdam" without knowing Rotterdam is 51.9244, 4.4777. Under
the hood this resolves q with the same logic as /autocomplete,
then runs /nearby around whatever it resolved to.
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://wayfindnow.com/search?q=Rotterdam&kind=port&kind=pier&limit=5"const res = await fetch(
"https://wayfindnow.com/search?q=Rotterdam&kind=port&kind=pier&limit=5",
{ headers: { Authorization: "Bearer " + apiKey } }
);
const { query, near, results } = await res.json();import requests
r = requests.get(
"https://wayfindnow.com/search",
params=[("q", "Rotterdam"), ("kind", "port"), ("kind", "pier"), ("limit", 5)],
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
data = r.json() # {"query", "near", "results"}| param | required | default | meaning |
|---|---|---|---|
q | yes | — | the place name to resolve, e.g. Rotterdam. |
kind | no | any | repeat the parameter per kind you want, e.g. &kind=port&kind=pier -- a single comma-joined value will not match anything and returns 400. Valid values: port, pier, marina, airport, airport_terminal, rail_terminal, road_terminal, multimodal_terminal, border_crossing, warehouses, shipping_center, freight_and_cargo_service, freight_forwarding_agency, transportation, wholesaler, business, business_manufacturing_and_supply, commercial_industrial, company, corporate_office, professional_services, industrial_company, industrial_equipment, location. |
radius | no | 25km | an Elasticsearch distance string, e.g. 10km. |
limit | no | 20 | capped at 100; non-integer values are a 400. |
country | no | none | see the country filter above. |
rank_by | no | prominence | prominence (importance, then distance) or distance (nearest first). |
lang | no | auto | see Language above. |
session | no | none | also honored here, not just on /autocomplete -- see Sessions & billing below. A call carrying the same token as an earlier /autocomplete call in the same lookup still counts toward that one session. |
Response
An object, not an array:
| param | required | default | meaning |
|---|---|---|---|
query | your q, echoed back. | ||
near | the row q resolved to -- same shape as an /autocomplete row (it comes from that endpoint), or null if nothing could be resolved. | ||
results | an array in /nearby's shape (below) -- note these rows carry distance_m but not matched. | ||
reason | present only when near is null, explaining why. |
Real response, captured live and trimmed to two results:
{
"query": "Rotterdam",
"near": {"id": "l75957", "type": "locality", "kind": null,
"text": "Rotterdam, Netherlands", "main_text": "Rotterdam",
"secondary_text": "Netherlands", "matched": [[0, 9]],
"components": {"locality": "Rotterdam", "country_code": "NL"},
"location": {"lat": 51.9244424, "lon": 4.47775}},
"results": [
{"id": "p224789", "type": "place", "kind": "port",
"text": "Rotterdam \u2014 Rotterdam, Netherlands", "main_text": "Rotterdam",
"secondary_text": "Rotterdam, Netherlands",
"components": {"locality": "Rotterdam", "country_code": "NL"},
"distance_m": 2744.7, "location": {"lat": 51.9, "lon": 4.483333}},
{"id": "p224823", "type": "place", "kind": "port",
"text": "Schiedam \u2014 Pernis Rotterdam, Netherlands", "main_text": "Schiedam",
"secondary_text": "Pernis Rotterdam, Netherlands",
"components": {"locality": "Pernis Rotterdam", "country_code": "NL"},
"distance_m": 5985.7, "location": {"lat": 51.9, "lon": 4.4}}
]
}GET /nearby
What is within a radius of a point you already have coordinates for -- a driver's GPS
ping, a warehouse's stored lat/lon. Restricted to type=place rows: addresses
and streets carry no kind, so a plain radius search over them would bury the
two ports you wanted under ten thousand houses.
curl -H "Authorization: Bearer YOUR_API_KEY" \
"https://wayfindnow.com/nearby?lat=51.9244&lon=4.4777&radius=5km&kind=port"const res = await fetch(
"https://wayfindnow.com/nearby?lat=51.9244&lon=4.4777&radius=5km&kind=port",
{ headers: { Authorization: "Bearer " + apiKey } }
);
const rows = await res.json();import requests
r = requests.get(
"https://wayfindnow.com/nearby",
params={"lat": 51.9244, "lon": 4.4777, "radius": "5km", "kind": "port"},
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
rows = r.json()| param | required | default | meaning |
|---|---|---|---|
lat, lon | yes | — | the search centre. Missing or non-numeric is a 400 -- unlike /autocomplete's lat/lon, this is a hard filter, not a ranking bias. |
kind | no | any | same repeated-parameter rule as /search's kind. Valid values: port, pier, marina, airport, airport_terminal, rail_terminal, road_terminal, multimodal_terminal, border_crossing, warehouses, shipping_center, freight_and_cargo_service, freight_forwarding_agency, transportation, wholesaler, business, business_manufacturing_and_supply, commercial_industrial, company, corporate_office, professional_services, industrial_company, industrial_equipment, location. |
radius | no | 5km | an Elasticsearch distance string. |
limit | no | 20 | capped at 100. |
country | no | none | see the country filter above. |
rank_by | no | distance | distance (nearest first) or prominence (importance, then distance). |
lang | no | en | no query text here to detect a script from, so this defaults to English rather than auto-detecting -- see Language above. |
session | no | none | also honored here, not just on /autocomplete -- see Sessions & billing below. A call carrying the same token as an earlier /autocomplete call in the same lookup still counts toward that one session. |
Response
A JSON array, same row shape as /autocomplete's minus matched,
plus distance_m (metres, from your lat/lon):
Real response, captured live:
[
{"id": "p224789", "type": "place", "kind": "port",
"text": "Rotterdam \u2014 Rotterdam, Netherlands", "main_text": "Rotterdam",
"secondary_text": "Rotterdam, Netherlands",
"components": {"locality": "Rotterdam", "country_code": "NL"},
"distance_m": 2740.5, "location": {"lat": 51.9, "lon": 4.483333}}
]GET /place/{id}
The full record behind a suggestion: coordinates, postcode, the admin chain with
populations, and the locality's name in every language we hold. This is the second half of
every lookup -- /autocomplete gives you a thin row to draw a dropdown from,
and the pick fetches this. It is also what converts a session (see Sessions above):
a call here carrying the lookup's session token is what turns eight billed
keystrokes into one billed address.
curl -H "Authorization: Bearer YOUR_API_KEY" \\\n "https://wayfindnow.com/place/a604088905?country=NL&session=9f2e1c-a-user-picked-uuid"| param | required | default | meaning |
|---|---|---|---|
id | yes | in the path. The id from any suggestion row -- a… address, s… street, l… locality, p… place. | |
country | no | send it. The cc from the suggestion you are opening. The database is partitioned by country and the primary key leads with it: with this hint the lookup is one index read (~0.06s); without it every partition is scanned (measured 56s, and the 503 below). A wrong value costs nothing -- it falls back to the slow path -- so it is a hint, never trusted for correctness. | |
session | no | the same token you sent on the keystrokes. This is the call that converts them into one billed session. |
Response
One object. type is the entity kind; admin_chain is innermost
first; locality_names is keyed by language code. Address rows carry
street/house_number/postcode; place rows carry a
place object with the name and category instead. A missing id is
404; a lookup that cannot reach the database in time is 503 with
Retry-After: 2.
Real response, captured live and trimmed:
{
"id": 604088905, "type": "address", "country": "NL",
"street": "Damrak", "house_number": "1-5", "postcode": "1012LG",
"location": {"lat": 52.3772801, "lon": 4.8976771},
"admin_chain": [
{"level": "locality", "level_number": 4, "id": 71786, "ext_id": "NL-l-amsterdam",
"name": "Amsterdam", "population": 881933},
{"level": "region", "level_number": 2, "id": 75087, "ext_id": "NL-r-noord-holland",
"name": "Noord-Holland", "population": 2952000}
],
"locality_names": {"names": {"en": ["Amsterdam"], "nl": ["Amsterdam"],
"ar": ["آمستردام"], "ja": ["アムステルダム"]}},
"street_names": ["Damrak"],
"updated_at": "2026-08-30T23:32:18+02:00"
}GET /chose
Tell us which suggestion the user picked. Two things happen: the lookup's session
converts (the discount in Sessions above -- this and /place/ are the
only two calls that do that), and the choice becomes ranking feedback. The second is why
rank matters more than id: "picked the third row" says the first
two were worse for this query, and that is the signal we train on.
curl -H "Authorization: Bearer YOUR_API_KEY" \\\n "https://wayfindnow.com/chose?q=Damrak+1&id=a604088905&rank=2&session=9f2e1c-a-user-picked-uuid"| param | required | default | meaning |
|---|---|---|---|
session | no | the lookup's token. Without it nothing converts -- the call is still accepted, it just changes nothing on your bill. | |
id | no | the chosen row's id. | |
rank | no | its 1-based position in the list the user saw. | |
q | no | what the user had typed when they picked. | |
country | no | the country filter in force, if any. |
Response
Always 200 with the same body -- there is nothing to look up:
{"ok": true}Every parameter is optional and none is validated, so a typo here fails
silently rather than loudly. If the session is not converting, check the session
token matches the one on your /autocomplete calls exactly.
GET /reverse
What is at a coordinate -- a GPS ping, a map click. Returns the places nearest
the point, nearest first, in /nearby's row shape. Unlike /nearby
it takes no kind: it answers "what is here", not "which ports are here".
curl -H "Authorization: Bearer YOUR_API_KEY" \\\n "https://wayfindnow.com/reverse?lat=52.3771&lon=4.898"| param | required | default | meaning |
|---|---|---|---|
lat, lon | yes | the point. Missing or non-numeric is a 400. | |
radius | no | 500m | an Elasticsearch distance string. |
country | no | see the country filter above. | |
lang | no | en | no query text to detect a script from, so this defaults to English -- see Language above. |
Response
A JSON array, nearest first, with distance_m from your point.
components and location are always present here -- this endpoint
does not take fields=.
Real response, captured live and trimmed to one row:
[
{"id": "p23530308", "type": "place", "kind": "shopping",
"text": "2020 — Damrak 1, 1012LG Amsterdam, Netherlands",
"main_text": "2020", "secondary_text": "Damrak 1, 1012LG Amsterdam, Netherlands",
"components": {"street": "Damrak", "house_number": "1", "locality": "Amsterdam",
"postcode": "1012LG", "country_code": "NL"},
"distance_m": 1.8, "location": {"lat": 52.37709, "lon": 4.89798}}
]GET /parse
Resolve a whole pasted address rather than a typed prefix. The difference from
/autocomplete is the first step: a country named at either end of the text
("…, Netherlands", "Italy, …") is peeled off and used as the filter, instead of being treated
as a word that has to match something. After that it is the same matcher, so a query that
works on one works on the other.
curl -H "Authorization: Bearer YOUR_API_KEY" \\\n "https://wayfindnow.com/parse?q=Damrak+1+Amsterdam,+Netherlands"| param | required | default | meaning |
|---|---|---|---|
q | yes | the full address text. | |
country | no | auto | overrides whatever was detected in the text. |
lang | no | auto | see Language above. |
Response
An object: country is what was peeled off (or null),
query is the text that remained, results is a list in
/autocomplete's row shape, best first, with components and
location always present. Nothing found is results: [], not an
error.
Real response, captured live and trimmed to one result:
{
"country": null,
"query": "Damrak 1 Amsterdam",
"results": [
{"id": "a604088905", "type": "address", "kind": null,
"text": "Damrak 1-5, 1012LG Amsterdam, Netherlands",
"main_text": "Damrak 1-5", "secondary_text": "1012LG Amsterdam, Netherlands",
"matched": [[0, 6]],
"components": {"street": "Damrak", "house_number": "1-5", "locality": "Amsterdam",
"postcode": "1012LG", "country_code": "NL"},
"location": {"lat": 52.3772801, "lon": 4.8976771}, "cc": "NL"}
]
}GET /batch
A spreadsheet of consignees, resolved in one call instead of five hundred round trips.
Repeat q once per line; up to 500 per call (413 past that). Lines
are resolved concurrently, so 500 of them take seconds, not minutes.
curl -H "Authorization: Bearer YOUR_API_KEY" \\\n "https://wayfindnow.com/batch?q=Damrak+1+Amsterdam&q=Blaak+31+Rotterdam&country=NL"| param | required | default | meaning |
|---|---|---|---|
q | yes | repeat per address, e.g. &q=…&q=…. At most 500. | |
country | no | applied to every line. Send it when you know it -- it is the cheapest way to keep a Dutch spreadsheet from matching a street in Texas. | |
lang | no | en | see Language above. |
Response
An array, one object per input line in the same order: query echoed,
country as detected or given, and match -- the single best row in
/parse's shape -- or null with a reason when nothing
matched at all.
Read match as the top suggestion, not a verified resolution.
There is no confidence score. ambiguous: true means only that a second candidate
existed; it does not say the first is right. Measured on a nonsense line
("Nowhere Street 999999 Zzz"): the best match was a café in New York named NOWHERE, with
ambiguous: true -- fuzzy search will find something for almost any
text. If a wrong fill is worse than an empty cell for you, treat ambiguous: true
as "review this row" and consider country= mandatory.
Real response, captured live -- the second line is that nonsense query, left in on purpose:
[
{"query": "Damrak 1 Amsterdam", "country": null, "ambiguous": true,
"match": {"id": "a604088905", "type": "address",
"text": "Damrak 1-5, 1012LG Amsterdam, Netherlands",
"components": {"street": "Damrak", "house_number": "1-5",
"locality": "Amsterdam", "postcode": "1012LG",
"country_code": "NL"},
"location": {"lat": 52.3772801, "lon": 4.8976771}}},
{"query": "Nowhere Street 999999 Zzz", "country": null, "ambiguous": true,
"match": {"id": "p158899463", "type": "place",
"text": "NOWHERE — Wooster Street 40, 10013 New York, USA"}}
]Try it
Runs for real, from your browser, against wherever this page is hosted. Your key is never sent anywhere but the API itself, and it is not saved anywhere -- reload the page and it is gone.
Prefilled examples -- one click loads a worked query and runs it (Latin and native-script addresses, a typo, a radius search, a named place):
