A Python client library for the NYC GeoSearch API v2, providing geocoding and autocomplete functionality for New York City addresses using authoritative data from the Property Address Directory (PAD).
- Simple and intuitive API - Easy-to-use Python interface
- Both sync and async support - Use with regular requests or async/await
- Comprehensive geocoding - Search for addresses, landmarks, and places
- Real-time autocomplete - Fast typeahead suggestions for UIs
- Geographic filtering - Focus searches by location or boundary
- Structured responses - Clean data models with type hints
- Error handling - Proper exception handling with detailed error messages
- Rate limiting - Built-in respectful rate limiting
- Session management - Connection pooling and context managers
pip install nyc-geosearch
# For async support:
pip install nyc-geosearch[async]
# or install aiohttp separately:
pip install aiohttpfrom geosearch import GeoSearchClient
# Basic search
with GeoSearchClient() as client:
result = client.search("120 Broadway, Manhattan")
if result:
feature = result[0]
print(f"Found: {feature.properties.label}")
print(f"Coordinates: {feature.geometry.coordinates}")
# Autocomplete for UIs
with GeoSearchClient() as client:
suggestions = client.autocomplete("120 Broad")
for suggestion in suggestions:
print(suggestion.properties.label)The main synchronous client for the NYC GeoSearch API.
GeoSearchClient(
base_url: str = "https://geosearch.planninglabs.nyc",
version: str = "v2",
timeout: int = 30,
session: requests.Session = None
)Search for addresses and places. Use this for complete addresses or when users paste full addresses.
Parameters:
text(str): Address or place name to search forsize(int, optional): Number of results (1-40, default: 10)focus_lat,focus_lon(float, optional): Geographic focus pointboundary_rect_*(float, optional): Rectangular boundary limitsboundary_circle_*(float, optional): Circular boundary limits
Example:
# Basic search
result = client.search("Empire State Building")
# Search with geographic focus
result = client.search(
"Main Street",
focus_lat=40.6782,
focus_lon=-73.9442 # Focus on Brooklyn
)
# Search within boundary
result = client.search(
"Park Avenue",
boundary_rect_min_lat=40.700,
boundary_rect_min_lon=-74.020,
boundary_rect_max_lat=40.790,
boundary_rect_max_lon=-73.930
)Get autocomplete suggestions. Use this for real-time typeahead in UIs.
Parameters:
text(str): Partial address or place namefocus_lat,focus_lon(float, optional): Geographic focus pointboundary_rect_*(float, optional): Rectangular boundary limits
Example:
# Real-time autocomplete
suggestions = client.autocomplete("120 Br")
for suggestion in suggestions[:5]: # Top 5 suggestions
print(suggestion.properties.label)Async version for concurrent requests.
import asyncio
from geosearch import AsyncGeoSearchClient
async def main():
async with AsyncGeoSearchClient() as client:
result = await client.search("Brooklyn Bridge")
print(result[0].properties.label)
asyncio.run(main())Container for API responses with list-like interface.
result = client.search("Broadway")
# Access features
print(len(result)) # Number of results
print(result[0]) # First feature
for feature in result: # Iterate features
print(feature.properties.label)
# Metadata
print(result.geocoding.version) # API version
print(result.bbox) # Bounding boxIndividual search result with GeoJSON structure.
feature = result[0]
# Properties (address info)
props = feature.properties
print(props.label) # Full formatted address
print(props.name) # Short name
print(props.housenumber) # House number
print(props.street) # Street name
print(props.borough) # NYC borough
print(props.confidence) # Match confidence score
# Geometry (coordinates)
coords = feature.geometry.coordinates # [longitude, latitude]
lat, lon = coords[1], coords[0]Detailed address information.
Common fields:
label- Full formatted addressname- Short name/descriptionconfidence- Match quality score (0-1)housenumber- House/building numberstreet- Street nameborough- NYC borough (Manhattan, Brooklyn, etc.)neighbourhood- Neighborhood namepostalcode- ZIP codepad_bin- NYC Building Identification Numberpad_bbl- NYC Borough, Block, and Lot number
Prioritize results near a specific location:
# Focus search around user's location
result = client.search(
"Coffee Shop",
focus_lat=40.7589, # Near Times Square
focus_lon=-73.9851
)Limit results to specific geographic areas:
# Search only within Manhattan
result = client.search(
"Park Avenue",
boundary_rect_min_lat=40.700,
boundary_rect_min_lon=-74.020,
boundary_rect_max_lat=40.790,
boundary_rect_max_lon=-73.930
)
# Search within 2km radius
result = client.search(
"Pizza",
boundary_circle_lat=40.7589,
boundary_circle_lon=-73.9851,
boundary_circle_radius=2.0
)Use async client for better performance:
import asyncio
from geosearch import AsyncGeoSearchClient
async def geocode_addresses(addresses):
async with AsyncGeoSearchClient() as client:
tasks = [client.search(addr) for addr in addresses]
results = await asyncio.gather(*tasks)
return results
# Geocode multiple addresses concurrently
addresses = ["120 Broadway", "1 Wall Street", "Times Square"]
results = asyncio.run(geocode_addresses(addresses))import requests
from geosearch import GeoSearchClient
# Custom session with connection pooling
session = requests.Session()
session.headers.update({'User-Agent': 'MyApp/1.0'})
# Custom timeout and session
client = GeoSearchClient(
timeout=60,
session=session
)The library provides specific exceptions for different error types:
from geosearch import (
GeoSearchClient,
GeoSearchException,
GeoSearchAPIException,
GeoSearchTimeoutException,
GeoSearchValidationException
)
with GeoSearchClient() as client:
try:
result = client.search("Some address")
except GeoSearchValidationException as e:
print(f"Invalid parameters: {e}")
except GeoSearchTimeoutException as e:
print(f"Request timed out: {e}")
except GeoSearchAPIException as e:
print(f"API error {e.status_code}: {e}")
except GeoSearchException as e:
print(f"General error: {e}")The client includes built-in rate limiting to be respectful to the API:
- Minimum 100ms between requests
- Automatic throttling
- Can be customized by subclassing
client = GeoSearchClient()
client._min_request_interval = 0.2 # 200ms between requestsSee the examples/ directory for comprehensive usage examples:
basic_usage.py- Common use cases and featuresasync_usage.py- Async/concurrent examplesui_integration.py- Integrating with web UIsbatch_geocoding.py- Processing multiple addresses
This wrapper is based on the NYC GeoSearch API v2, which uses the Pelias geocoding engine with NYC-specific data from the Property Address Directory (PAD).
Official API Documentation:
Endpoints:
/v2/search- Precise geocoding for complete addresses/v2/autocomplete- Fast autocomplete suggestions
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
MIT License - see LICENSE file for details.
- Initial release
- Sync and async clients
- Full API coverage
- Comprehensive error handling
- Type hints and documentation