Places API
The Places API provides hybrid text and geographic search across the Geog places database. Built on a geo-composite cell file architecture using H3 cell partitioning for efficient spatial queries.
Base URL
https://api.geog.dev/v1/places
Authentication
Places API requests require authentication via Bearer token:
curl -X POST "https://api.geog.dev/v1/places/search" \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{"location": {"lat": 37.7749, "lon": -122.4194}, "radius": 5000}'
Endpoints
1. Search Places
Search for places using text queries, geographic constraints, and category filters.
/v1/places/searchRequest Body
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
location | object | Yes | - | Geographic center point: { "lat": number, "lon": number } |
radius | number | No | 5000 | Search radius in meters (1-100,000) |
query | string | No | - | Text search query (e.g., "coffee shops") |
categories | string[] | No | - | Filter by categories: ["restaurant", "cafe"] |
limit | integer | No | 20 | Results per page (1-1,000) |
offset | integer | No | 0 | Pagination offset |
weights | object | No | - | Hybrid scoring weights: { "text": 0.6, "geo": 0.4 } |
Location constraints:
lat: Latitude (-90 to 90)lon: Longitude (-180 to 180)
Weights object:
text: Weight for text relevance scoring (0-1)geo: Weight for geographic proximity scoring (0-1)
Example Request
curl -X POST "https://api.geog.dev/v1/places/search" \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"query": "coffee shop",
"location": { "lat": 37.7749, "lon": -122.4194 },
"radius": 5000,
"limit": 5
}'
Example Response
{
"results": [
{
"id": "8529a1003ffffff_n456789012",
"score": 0.95,
"name": "Blue Bottle Coffee",
"category": "cafe",
"categories": ["cafe", "coffee_shop"],
"location": {
"lat": 37.7849,
"lon": -122.4088
},
"distance": 1250,
"address": "66 Mint St, San Francisco, CA 94103",
"city": "San Francisco",
"region": "California",
"postcode": "94103",
"phone": "+14158438220",
"website": "https://bluebottlecoffee.com",
"brand": "Blue Bottle Coffee",
"sourceId": "way/123456789",
"confidence": 0.98,
"updatedAt": "2025-11-20T08:00:00Z",
"scores": {
"text": 0.92,
"geo": 0.98
}
}
],
"total": 245,
"executionTime": 42,
"queryStats": {
"pruningEfficiency": 0.85,
"cellsSearched": 3
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
results | array | Array of matching places |
results[].id | string | Unique place identifier |
results[].score | number | Combined relevance score (0-1) |
results[].name | string | Place name |
results[].description | string | Place description (when available) |
results[].category | string | Primary place category |
results[].categories | array | All categories for this place |
results[].tags | array | Tags associated with this place |
results[].location | object | Geographic coordinates (lat, lon) |
results[].distance | number | Distance from search center (meters) |
results[].address | string | Street address |
results[].city | string | City name |
results[].region | string | State or region name |
results[].postcode | string | Postal code |
results[].phone | string | Phone number |
results[].website | string | Website URL |
results[].hours | object | Operating hours (raw, is24_7, timezone) |
results[].brand | string | Brand name |
results[].brandWikidata | string | Wikidata ID for the brand |
results[].contact | object | Contact info (phone, email, website) |
results[].attributes | object | Additional place attributes |
results[].status | string | Place status (e.g., active) |
results[].sourceId | string | Source data identifier |
results[].confidence | number | Data confidence score (0-1) |
results[].updatedAt | string | Last updated timestamp (ISO 8601) |
results[].scores | object | Individual scoring components |
results[].scores.text | number | Text relevance score |
results[].scores.geo | number | Geographic proximity score |
total | integer | Total matching results |
executionTime | integer | Query execution time (milliseconds) |
queryStats.pruningEfficiency | number | Cell pruning efficiency ratio |
queryStats.cellsSearched | integer | Number of H3 cells searched |
2. Get Place by ID
Retrieve detailed information about a specific place.
/v1/places/{placeId}Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
placeId | string | Yes | Place ID (format: {h3_r5}_{type}{osm_id}) |
Example Request
curl -H "Authorization: Bearer your-api-key" \
"https://api.geog.dev/v1/places/8529a1003ffffff_n456789012"
Example Response
{
"id": "8529a1003ffffff_n456789012",
"score": 1.0,
"name": "Blue Bottle Coffee",
"description": "Specialty coffee roaster and retailer",
"category": "cafe",
"categories": ["cafe", "coffee_shop"],
"location": {
"lat": 37.7849,
"lon": -122.4088
},
"address": "66 Mint St, San Francisco, CA 94103",
"city": "San Francisco",
"region": "California",
"postcode": "94103",
"phone": "+14158438220",
"website": "https://bluebottlecoffee.com",
"brand": "Blue Bottle Coffee",
"sourceId": "way/123456789",
"confidence": 0.98,
"updatedAt": "2025-11-20T08:00:00Z"
}
Error Handling
Error Response Format
{
"error": "error_type",
"message": "Human-readable description",
"code": "MACHINE_READABLE_CODE"
}
Error Codes
| Code | Status | Description |
|---|---|---|
GEO_CONSTRAINT_REQUIRED | 400 | Missing required location field |
INVALID_LOCATION | 400 | Invalid latitude or longitude values |
INVALID_RADIUS | 400 | Radius must be a positive number |
RADIUS_EXCEEDED | 400 | Radius exceeds 100,000m maximum |
INVALID_JSON | 400 | Request body is not valid JSON |
VALIDATION_ERROR | 400 | Request parameters failed validation |
INVALID_ID | 400 | Place ID format is invalid |
NOT_FOUND | 404 | Place not found |
SERVICE_UNAVAILABLE | 503 | Search service is not configured |
INDEX_NOT_AVAILABLE | 503 | Search index is not available |
Best Practices
Search Optimization
Always provide a location. The
locationfield is required for all searches. The Places API uses H3 cell partitioning for efficient spatial queries.Use appropriate radius. Smaller radii return faster results. Start with the default 5,000m and increase only if needed.
Combine text and category filters. Use
queryfor free-text search andcategoriesfor structured filtering together for best results.Paginate large result sets. Use
limitandoffsetfor pagination instead of requesting all results at once.
Scoring Weights
Adjust the weights parameter to tune result ranking:
{
"weights": { "text": 0.7, "geo": 0.3 }
}
- Higher
textweight prioritizes name/description matches - Higher
geoweight prioritizes proximity to the search center - When omitted, the API uses balanced default weights
Caching
- Place details (
GET /v1/places/{id}) are stable and suitable for client-side caching - Search results may change as the index updates; cache with shorter TTLs