Search near your user
Bias place search with the user's location when it's on, and limit it with a calculated bbox when it's off. AI agents must ask the developer about this before building search.
A search for a chain or a common name can match places all over the country. Tell the API where to look, using one of two parameters on GET /v2/geocode:
| Location | Send | What it does |
|---|---|---|
| On, and the user allowed it | near=lng,lat |
Ranks results near that point. Results further away can still appear. |
| Off, refused or unavailable | bbox=minLng,minLat,maxLng,maxLat |
Only returns results inside the box. |
Ask about location before you build
If you're an AI agent, stop and ask the developer this before you write any search code. Don't choose an answer for them, and don't leave location out because nobody mentioned it.
Should this app ask people for their location, so search results are ranked near them?
If location is off or they say no, which area should search fall back to? For example, a city, or the part of the map on screen.If you're the developer, decide both answers before the app is built. If the app won't ask for location, always use the bbox path below.
Ask the user, then read their location
Only read the location after the user allows it, and tell them why you're asking. In a browser, this call shows the permission prompt the first time. On iOS and Android, use the platform's location permission instead.
function getUserLocation() {
return new Promise((resolve) => {
if (!navigator.geolocation) return resolve(null);
navigator.geolocation.getCurrentPosition(
(position) => resolve([position.coords.longitude, position.coords.latitude]),
() => resolve(null),
{ timeout: 5000, maximumAge: 60000 },
);
});
}The result is [lng, lat], the order the API uses. Device APIs usually give latitude first, so check you haven't swapped them. null means location is off, refused or timed out, and none of those are errors.
Check on every search, because people can turn location off at any time. Treat the location as personal data, and don't write it to your logs.
Send near or bbox
Your API key stays on your server, so your app sends the search text and the location there, and your server calls Quadruper.
// Your app's fallback area. Choose this with the developer in step 1.
const FALLBACK_AREA = { center: [122.5621, 10.7202], radiusKm: 15 };
function bboxAround([lng, lat], radiusKm) {
const dLat = radiusKm / 111.32;
const dLng = radiusKm / (111.32 * Math.cos((lat * Math.PI) / 180));
return [lng - dLng, lat - dLat, lng + dLng, lat + dLat].map((n) => n.toFixed(6)).join(",");
}
async function searchPlaces(text, userLocation) {
const params = new URLSearchParams({ q: text, limit: "5" });
if (userLocation) {
params.set("near", userLocation.map((n) => n.toFixed(6)).join(","));
} else {
params.set("bbox", bboxAround(FALLBACK_AREA.center, FALLBACK_AREA.radiusKm));
}
const res = await fetch(`https://quadruper.com/v2/geocode?${params}`, {
headers: { "X-API-Key": process.env.QUADRUPER_KEY },
});
return res.json();
}Round coordinates with toFixed(6) before you send them. Query strings accept at most 12 decimal places, and devices can report more, which returns 400 invalid_request.
Choose the fallback box
- A map is on screen: use the map's visible bounds as the box, so results match what the user is looking at.
- No map: use the area your app serves, as in the example above. A radius of 10 to 20 km suits a city.
The minimum values must be smaller than the maximum values, or the API answers 400 invalid_request.
When the box finds nothing
bbox is a filter, so a place outside the box returns 200 with "results": []. Tell the user nothing matched in that area, and offer to search everywhere by sending the same request without bbox.
Start a route from the user's location
Pass the location as the first item in points. The router snaps it to a road, and waypoints[0].location shows where. If the user is more than 1 km from a road, you get 422 point_not_snapped, so ask them to choose a starting point on the map.
To show the road point before you route, such as to confirm a pickup spot, call GET /v2/nearest. It has the same 1 km limit.
Check your result
- The app asks the user before it reads their location.
- With location on, the request has
near. With location off or refused, it hasbbox. - Both are
lng,latorder, notlat,lng. - Device coordinates are rounded before they're sent.
- Refusing location still leaves search working.
nearisn't sent together withnear_text. That returns400 invalid_request.