Places API

Preview — This API is unstable and will change. Do not depend on the current request/response shape in production integrations.

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 a Bearer token with places:read scope. For frontend applications, exchange a long-lived API token for a short-lived access token to avoid exposing credentials in client-side code:

# 1. Exchange your API token for a short-lived access token (run on your backend)
curl -X POST "https://api.geog.dev/v1/auth/token" \
  -H "Authorization: Bearer your-long-lived-api-token" \
  -H "Content-Type: application/json" \
  -d '{"ttl": 3600, "scope": "places:read"}'

# Response: { "access_token": "eyJhbGc...", "expires_in": 3600, ... }

# 2. Use the short-lived token for places requests
curl -X POST "https://api.geog.dev/v1/places/search" \
  -H "Authorization: Bearer eyJhbGc..." \
  -H "Content-Type: application/json" \
  -d '{"location": {"lat": 44.9778, "lon": -93.2650}, "radius": 5000}'

See Token Exchange for the full endpoint specification.

Endpoints

1. Search Places

Search for places using text queries, geographic constraints, and category filters.

POST/v1/places/search

Request Body

ParameterTypeRequiredDefaultDescription
locationobjectYesGeographic center point: { "lat": number, "lon": number }
radiusnumberNo5000Search radius in meters (1–100,000)
querystringNoText search query (e.g., "coffee shops")
categoriesstring[]NoFilter by categories: ["restaurant", "cafe"]
limitintegerNo20Results per page (1–1,000)
offsetintegerNo0Pagination offset
weightsobjectNoHybrid 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-token" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "museum",
    "location": { "lat": 44.9778, "lon": -93.2650 },
    "radius": 5000,
    "limit": 5
  }'

Example Response

{
	"results": [
		{
			"id": "8527526bfffffff_w157035918",
			"name": "Mill City Museum",
			"categories": ["museum", "arts_and_entertainment"],
			"location": { "lat": 44.9790262, "lon": -93.2568813 },
			"address": {
				"housenumber": "704",
				"street": "South 2nd Street",
				"city": "Minneapolis",
				"state": "MN",
				"postcode": "55401"
			},
			"contact": {
				"email": "mcm@mnhs.org",
				"phone": "+1 612 341 7555",
				"website": "https://www.mnhs.org/millcity"
			},
			"hours": "Th,Fr 10:00-16:00; Sa,Su 10:00-17:00",
			"access": { "wheelchair": "yes" },
			"meta": {
				"building": "yes",
				"check_date:opening_hours": "2023-04-20",
				"fee": "yes",
				"tourism": "museum"
			},
			"score": 1,
			"distance": 0,
			"confidence": 1
		}
	],
	"total": 12,
	"executionTime": 38,
	"metadata": { "shardsSearched": 1, "cacheHit": false },
	"queryStats": { "pruningEfficiency": 0.92, "cellsSearched": 1 }
}

Response Fields

FieldTypeDescription
resultsarrayArray of matching places
results[].idstringPlace ID ({h3_r5}_{type}{osm_id})
results[].namestringPlace name
results[].categoriesarrayCategories computed from primary OSM tag
results[].locationobjectGeographic coordinates (lat, lon)
results[].addressobjectAddress fields: housenumber, street, city, state, postcode, country, unit
results[].contactobjectContact info: phone, website, email, fax, facebook, instagram, etc.
results[].hoursstringRaw opening_hours value
results[].cuisinearrayCuisine types (from OSM cuisine tag)
results[].paymentobjectPayment methods: cash, credit_cards, contactless, etc.
results[].accessobjectAccessibility: wheelchair, wheelchairToilets
results[].brandobjectBrand info: name, wikidata, wikipedia
results[].operatorstringOperator name
results[].dietobjectDietary options: vegetarian, vegan, halal, kosher, etc.
results[].amenitiesobjectAmenities: outdoor_seating, internet_access, smoking, etc.
results[].metaobjectAdditional OSM tags not in other groups
results[].scorenumberCombined relevance score (0–1)
results[].distancenumberDistance from search center (meters)
results[].confidencenumberData confidence score (0–1)
results[].scoresobjectIndividual scoring components
results[].scores.textnumberText relevance score
results[].scores.geonumberGeographic proximity score
totalintegerTotal matching results
executionTimenumberQuery execution time (milliseconds)
metadata.shardsSearchedintegerNumber of shards searched
metadata.cacheHitbooleanWhether results were served from cache
queryStats.pruningEfficiencynumberCell pruning efficiency ratio
queryStats.cellsSearchedintegerNumber of H3 cells searched

2. Get Place by ID

Retrieve detailed information about a specific place.

GET/v1/places/{placeId}

Parameters

ParameterTypeRequiredDescription
placeIdstringYesPlace ID (format: {h3_r5}_{type}{osm_id})

Example Request

curl -H "Authorization: Bearer your-api-token" \
  "https://api.geog.dev/v1/places/8527526bfffffff_w157035918"

Example Response

Returns a single PlaceResult object with all available fields (same schema as search results). Response is cached for 1 hour (Cache-Control: public, max-age=3600).

Error Handling

Error Response Format

{
	"error": "error_type",
	"message": "Human-readable description",
	"code": "MACHINE_READABLE_CODE"
}

Error Codes

CodeStatusDescription
GEO_CONSTRAINT_REQUIRED400Missing required location field
INVALID_LOCATION400Invalid latitude or longitude values
INVALID_RADIUS400Radius must be a positive number
RADIUS_EXCEEDED400Radius exceeds 100,000m maximum
INVALID_JSON400Request body is not valid JSON
VALIDATION_ERROR400Request parameters failed validation
INVALID_ID400Place ID format is invalid
NOT_FOUND404Place not found
SERVICE_UNAVAILABLE503Search service is not configured
INDEX_NOT_AVAILABLE503Search index is not available

Best Practices

Search Optimization

  1. Always provide a location. The location field is required for all searches. The Places API uses H3 cell partitioning for efficient spatial queries.

  2. Use appropriate radius. Smaller radii return faster results. Start with the default 5,000m and increase only if needed.

  3. Combine text and category filters. Use query for free-text search and categories for structured filtering together for best results.

  4. Paginate large result sets. Use limit and offset for 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 text weight prioritizes name matches
  • Higher geo weight 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

See Also