🔑 Authentication
All API endpoints under /api/* require an API key. Pass it in the X-API-Key header.
curl -H "X-API-Key: your-api-key" \
http://localhost:8088/api/search/autocomplete?q=yaounde
API keys belong to applications. Your admin creates an application (e.g. "My Mobile App") and generates a key. The application uses the key to search places, then creates contributors who submit and validate data.
🔍 Searching Places
The autocomplete endpoint supports partial input — type as you go. Results merge three sources with contributor data boosted highest.
curl -H "X-API-Key: your-key" \
"http://localhost:8088/api/search/autocomplete?q=carrefour+biyem&lat=3.848&lon=11.502&limit=5"
# Response
{
"results": [
{ "name": "Carrefour Biyem-Assi", "lat": 3.848, "lon": 11.502,
"rank_score": 0.85, "source": "contributor" }
],
"query": "carrefour biyem",
"took_ms": 4
}
Typo tolerance: The search engine tolerates 1 typo on 4-7 char queries and 2 typos on 8+ char queries, with infix matching so partial words match as you type. emobmo finds Emombo, yaounde finds Yaoundé.
Local names (OSM loc_name): every OSM place is also searchable by its loc_name — the name people actually use on the street. dispensaire messassi and Centre Médical d'Arrondissement Yaoundé I Olembe return the same place (its loc_name is "Dispensaire Messassi"). The local name is also available as a response field:
curl -H "X-API-Key: your-key" \
"http://localhost:8088/api/search/autocomplete?q=dispensaire+messassi&lat=3.9464&lon=11.5221&fields=name,loc_name,lat,lon"
# → {"results":[{"name":"Centre Médical d'Arrondissement Yaoundé I Olembe","loc_name":"Dispensaire Messassi","lat":3.9464,"lon":11.5221}]}
Location biasing: Results are re-ranked by Gaussian decay from the focus point (σ=10km). Closer places get boosted.
Viewport restriction: Pass sw_lat, sw_lon, ne_lat, ne_lon to limit results to a geographic zone (e.g. a delivery area):
curl -H "X-API-Key: your-key" \
"http://localhost:8088/api/search/autocomplete?q=pharmacie&sw_lat=3.81&sw_lon=11.46&ne_lat=3.90&ne_lon=11.55"
Blacklist filtering: GIDs in contrib_blacklist are excluded from results automatically.
📍 Submitting a New Point
curl -X POST http://localhost:8088/api/contributor/points \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"name": "Carrefour Biyem-Assi",
"lat": 3.8480,
"lon": 11.5021,
"accuracy_meters": 15,
"city": "Yaoundé",
"display_name": "Jean",
}'
Duplicate detection: If a point with the same normalized name exists within 500m, the existing point is returned instead. No fragmentation.
Auto-activation: Points from contributors with reputation > 0.3 are automatically set to active.
✅ Validating a Location
The core self-improving loop. When a driver arrives at a location, they can confirm, correct, or reject.
curl -X POST http://localhost:8088/api/contributor/validate \
-H "X-API-Key: your-key" \
-H "Content-Type: application/json" \
-d '{
"point_id": "uuid",
"action": "confirm",
"lat": 3.8481,
"lon": 11.5020,
"reported_name": "Carrefour Biyem-Assi",
"display_name": "Jean",
"accuracy_meters": 5
}'
Actions:
- confirm — Updates position (weighted average), increments rank
- correct — Creates alias with corrected name, penalizes old point
- reject — Decrements rank, flags as disputed if below 0.1
🗺️ Geocoding
Structured (address → coordinates):
curl -X POST http://localhost:8088/api/geocode/structured \
-H "X-API-Key: your-key" \
-d '{"address": "Yaoundé", "country": "CM"}'
Reverse (coordinates → place):
curl "http://localhost:8088/api/geocode/reverse?lat=3.848&lon=11.502" \
-H "X-API-Key: your-key"
📋 Place Details
Get full details including dynamic parent hierarchy and 360° nearby places of the same type.
curl "http://localhost:8088/api/places/6684796032" \
-H "X-API-Key: your-key"
# Response includes:
# - parents: dynamic hierarchy (city → district → region)
# - nearby: 8 sectors of same-type places
# - tags: OSM metadata
# - loc_name: OSM local name (add ?fields=name,loc_name to request just it)
🚫 Blacklisting Fake Places
Pelias-inspired GID-based blacklist. Add fake places by their GID to exclude them from search results.
# Blacklisted GIDs are filtered at query time.
# Manage via the admin dashboard: http://localhost:8088/admin/blacklist