http://localhost:2000
The Who's On First API provides geocoding services to resolve geographic coordinates into administrative hierarchies using the Who's On First (WOF) gazetteer dataset. The API currently contains 258,937 places across the United States.
FastAPI provides interactive API documentation:
- Swagger UI: http://localhost:2000/docs
- ReDoc: http://localhost:2000/redoc
- OpenAPI Schema: http://localhost:2000/openapi.json
All /api/v1/* endpoints require a Bearer token in the Authorization header:
curl -H "Authorization: Bearer wof_<your-token>" \
"http://localhost:2000/api/v1/hierarchy?lat=37.7749&lon=-122.4194"Tokens are issued via the admin page at /admin (HTTP Basic auth, configured
with ADMIN_USERNAME / ADMIN_PASSWORD env vars). The admin page lists all
tokens with usage counts and lets you create or revoke tokens. A token's
plaintext value is shown only once at creation time — only its sha256 hash is
stored.
The /health and / endpoints remain public.
Auth failures return 401 Unauthorized with WWW-Authenticate: Bearer.
Resolve latitude/longitude coordinates to their corresponding administrative hierarchy.
Endpoint: GET /api/v1/hierarchy
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
lat |
float | Yes | Latitude (-90 to 90) |
lon |
float | Yes | Longitude (-180 to 180) |
Response Schema:
{
"continent": {
"id": integer,
"name": "string",
"placetype": "string"
} | null,
"country": { ... } | null,
"region": { ... } | null,
"county": { ... } | null,
"locality": { ... } | null,
"neighbourhood": { ... } | null
}Example Request:
# San Francisco, CA
curl "http://localhost:2000/api/v1/hierarchy?lat=37.7749&lon=-122.4194"
# New York City
curl "http://localhost:2000/api/v1/hierarchy?lat=40.7128&lon=-74.0060"
# Vinton, OH
curl "http://localhost:2000/api/v1/hierarchy?lat=38.977570&lon=-82.337409"Example Response (San Francisco):
{
"continent": null,
"country": null,
"region": null,
"county": null,
"locality": {
"id": 85922583,
"name": "San Francisco",
"placetype": "locality"
},
"neighbourhood": null
}HTTP Status Codes:
200 OK- Success400 Bad Request- Invalid coordinates500 Internal Server Error- Server error503 Service Unavailable- Database unavailable
Retrieve detailed information about a specific Who's On First place by its ID.
Endpoint: GET /api/v1/place/{wof_id}
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
wof_id |
integer | Yes | Who's On First place ID |
Response Schema:
{
"id": integer,
"name": "string",
"placetype": "string",
"parent_id": integer | null,
"properties": {
// Full WOF properties including:
// - Hierarchy information
// - Geometric data (bbox, centroid)
// - Names in multiple languages
// - Population statistics
// - External concordances (Wikidata, GeoNames, etc.)
// - And much more...
}
}Example Request:
# Get San Francisco
curl "http://localhost:2000/api/v1/place/85922583"
# Get Alaska Congressional District
curl "http://localhost:2000/api/v1/place/1108737357"
# Get Vinton, OH
curl "http://localhost:2000/api/v1/place/101711873"Example Response (Vinton, OH - truncated):
{
"id": 101711873,
"name": "Vinton",
"placetype": "locality",
"parent_id": 404524595,
"properties": {
"wof:id": 101711873,
"wof:name": "Vinton",
"wof:placetype": "locality",
"wof:country": "US",
"wof:hierarchy": [
{
"continent_id": 102191575,
"country_id": 85633793,
"region_id": 85688485,
"county_id": 102083675,
"localadmin_id": 404524595,
"locality_id": 101711873
}
],
"wof:parent_id": 404524595,
"wof:population": 222,
"geom:latitude": 38.97757,
"geom:longitude": -82.337409,
"geom:bbox": "-82.350061,38.969332,-82.326371,38.98621",
"iso:country": "US",
"lbl:latitude": 38.977868,
"lbl:longitude": -82.337402,
"name:eng_x_preferred": ["Vinton"],
"qs:a0": "United States",
"qs:a1": "*Ohio",
"wof:concordances": {
"gn:id": 4527247,
"wd:id": "Q2186359",
"uscensus:geoid": "3980178"
}
}
}HTTP Status Codes:
200 OK- Success404 Not Found- Place ID not found500 Internal Server Error- Server error503 Service Unavailable- Database unavailable
Check the health status of the API and database connection.
Endpoint: GET /health
Response Schema:
{
"status": "ok" | "degraded",
"service": "wof-api",
"database": "healthy" | "unhealthy" | "unavailable",
"error": "string" // Optional, only present if there's an error
}Example Request:
curl "http://localhost:2000/health"Example Response:
{
"status": "ok",
"service": "wof-api",
"database": "healthy"
}HTTP Status Codes:
200 OK- Always returns 200, checkstatusfield for actual health
The database contains the following place types for the United States:
| Place Type | Count | Description |
|---|---|---|
| Locality | 179,570 | Cities, towns, villages |
| Neighbourhood | 40,263 | Neighborhoods and districts |
| Localadmin | 19,769 | Townships, local administrative areas |
| Campus | 8,073 | University campuses, facilities |
| Constituency | 7,194 | Congressional & state legislative districts |
| County | 3,143 | County boundaries |
| Microhood | 263 | Micro-neighborhoods |
| Marketarea | 210 | Designated market areas |
| Macrohood | 24 | Macro-neighborhoods |
| Borough | 4 | NYC boroughs and similar |
- Region: United States
- Total Places: 258,937
- Data Source: Who's On First (WOF) via geocode.earth
- Last Updated: 2024
Convert coordinates to place names:
# Where am I?
curl "http://localhost:2000/api/v1/hierarchy?lat=40.7589&lon=-73.9851"Given a coordinate from an address geocoder, get the full administrative hierarchy:
# Get full hierarchy for a delivery address
curl "http://localhost:2000/api/v1/hierarchy?lat=37.7749&lon=-122.4194"Find which congressional district a coordinate falls within:
# Get place details including constituency
curl "http://localhost:2000/api/v1/hierarchy?lat=37.7749&lon=-122.4194"
# Then lookup constituency by exploring the databaseIdentify if coordinates are within a university campus:
# Check if coordinates are on a campus
curl "http://localhost:2000/api/v1/hierarchy?lat=37.8719&lon=-122.2585"Enrich real estate listings with neighborhood and administrative data:
# Get neighborhood and locality for a property
curl "http://localhost:2000/api/v1/hierarchy?lat=40.7489&lon=-73.9680"Currently, there are no rate limits on the local API. If deploying to production, consider implementing:
- Rate limiting per IP address
- API key authentication
- Request throttling
Invalid Coordinates (400)
{
"detail": "Invalid latitude or longitude."
}Place Not Found (404)
{
"detail": "Place with ID 999999999 not found"
}Database Connection Error (503)
{
"detail": "Database connection pool not available"
}Internal Server Error (500)
{
"detail": "An internal error occurred: [error details]"
}- Hierarchy lookup: 50-200ms (depends on complexity)
- Place by ID: 10-50ms (indexed query)
- Health check: <10ms
- Use connection pooling - Already configured (1-10 connections)
- Enable query caching - Can be added with Redis
- Add read replicas - For high-traffic scenarios
- Use CDN caching - For static lookups
# Test hierarchy endpoint
curl -X GET "http://localhost:2000/api/v1/hierarchy?lat=37.7749&lon=-122.4194" \
-H "accept: application/json"
# Test place lookup
curl -X GET "http://localhost:2000/api/v1/place/85922583" \
-H "accept: application/json"
# Test health check
curl -X GET "http://localhost:2000/health" \
-H "accept: application/json"import requests
# Get hierarchy
response = requests.get(
"http://localhost:2000/api/v1/hierarchy",
params={"lat": 37.7749, "lon": -122.4194}
)
print(response.json())
# Get place by ID
response = requests.get("http://localhost:2000/api/v1/place/85922583")
print(response.json())// Get hierarchy
fetch('http://localhost:2000/api/v1/hierarchy?lat=37.7749&lon=-122.4194')
.then(response => response.json())
.then(data => console.log(data));
// Get place by ID
fetch('http://localhost:2000/api/v1/place/85922583')
.then(response => response.json())
.then(data => console.log(data));| Column | Type | Description |
|---|---|---|
id |
BIGINT | WOF place ID (primary key) |
parent_id |
BIGINT | Parent place ID |
name |
TEXT | Place name |
placetype |
VARCHAR(50) | Type of place |
country_code |
VARCHAR(2) | ISO country code |
properties |
JSONB | Full WOF properties |
geom |
GEOMETRY | PostGIS geometry (polygon/multipolygon) |
- Primary key on
id - GIST spatial index on
geom(for fast point-in-polygon queries) - B-tree index on
placetype - B-tree index on
parent_id
DB_HOST=localhost # Database host
DB_PORT=5432 # Database port
DB_NAME=wof # Database name
DB_USER=user # Database user
DB_PASS=password # Database password
DB_MIN_CONNECTIONS=1 # Min connection pool size
DB_MAX_CONNECTIONS=10 # Max connection pool size# Build and start services
docker-compose up -d
# View logs
docker-compose logs -f wof-api
# Stop services
docker-compose downSee DEPLOYMENT_GUIDE.md for AWS deployment instructions.
- FastAPI Documentation: https://fastapi.tiangolo.com/
- Who's On First: https://whosonfirst.org/
- PostGIS Documentation: https://postgis.net/documentation/
- OpenAPI Specification: http://localhost:2000/openapi.json
- Initial release
- 258,937 US places
- 3 endpoints (hierarchy, place lookup, health)
- PostgreSQL + PostGIS backend
- Connection pooling
- Comprehensive error handling
This API uses Who's On First data, which is licensed under Creative Commons Zero.