Skip to main content

A Python wrapper for the NYC OTI Geoclient 2.0 API

Project description

Python Geoclient MOO

  • Author @SteveScott

A comprehensive Python wrapper for the NYC OTI Geoclient 2.0 API, providing easy access to New York City's official geocoding service.

Python 3.8+ License: MIT

Overview

The NYC Geoclient API provides developer-friendly access to the Department of City Planning's official geocoding service (Geosupport). This Python wrapper makes it easy to geocode NYC addresses, intersections, places, and more.

Features

  • 🏢 Address Geocoding: Convert NYC addresses to coordinates and property information
  • 🔢 BBL/BIN Lookups: Get property details by Borough-Block-Lot or Building Identification Number
  • 🛣️ Street Operations: Geocode intersections and blockfaces
  • 🏛️ Place Names: Find coordinates for well-known NYC locations
  • 🔍 Single-Field Search: Parse natural language location queries
  • 🛡️ Type Safety: Full type hints and response models
  • Robust Error Handling: Comprehensive exception handling with retries
  • 🧪 Well Tested: Extensive unit and integration test coverage

Installation

pip install python-geoclient-moo

Quick Start

from geoclient import GeoClient

# Initialize the client with your API credentials
client = GeoClient(
    app_id="your_app_id",
    app_key="your_app_key"
)

# Geocode an address
result = client.address("314", "west 100 st", "manhattan")
print(f"Coordinates: {result.latitude}, {result.longitude}")
print(f"BBL: {result.bbl}")

# Use as a context manager (recommended)
with GeoClient("your_app_id", "your_app_key") as client:
    result = client.search("empire state building")
    print(f"Found: {result.results[0].response}")

Getting API Credentials

To use the Geoclient API, you need to register for free API credentials:

  1. Visit the NYC Developer Portal
  2. Create an account and register a new application
  3. Subscribe to the Geoclient API
  4. There are two keys, a primary and secondary key. Take the first key and save it as the enviroment variable GEOCLIENT_SUBSCRIPTION_KEY.

API Methods

Address Geocoding

# Geocode with borough
result = client.address("314", "west 100 st", "manhattan")

# Geocode with ZIP code
result = client.address("314", "west 100 st", zip_code="10025")

# Access response data
print(f"Address: {result.house_number} {result.street_name}")
print(f"Borough: {result.borough_name}")
print(f"Coordinates: {result.latitude}, {result.longitude}")
print(f"BBL: {result.bbl}, BIN: {result.bin}")

Property Lookups

# Look up by Borough-Block-Lot (BBL)
result = client.bbl("manhattan", "1889", "1")
print(f"Address: {result.house_number} {result.street_name}")

# Look up by Building Identification Number (BIN)
result = client.bin_("1079043")
print(f"BBL: {result.bbl}")

Street Operations

# Geocode intersection
result = client.intersection("broadway", "west 100 st", "manhattan")
print(f"Intersection: {result.latitude}, {result.longitude}")

# Get blockface information
result = client.blockface(
    on_street="amsterdam ave",
    cross_street_one="west 110 st", 
    cross_street_two="west 111 st",
    borough="manhattan"
)
print(f"Segment length: {result.segment_length} feet")

Place Names

# Geocode well-known places
result = client.place("empire state building", "manhattan")
print(f"Place: {result.place_name}")
print(f"Address: {result.house_number} {result.street_name}")

Single-Field Search

The search endpoint can parse various input formats:

# Address search
result = client.search("314 west 100 st manhattan")

# Intersection search  
result = client.search("broadway and west 100 st")

# BBL search
result = client.search("1018890001")

# BIN search
result = client.search("1079043")

# Blockface search
result = client.search("broadway between west 100 st and west 101 st")

# Place search
result = client.search("empire state building")

# Process results
for search_result in result.results:
    if search_result.status == "EXACT_MATCH":
        print(f"Found exact match: {search_result.response}")

Search Options

Customize search behavior with optional parameters:

result = client.search(
    "314 west 100 st",
    exact_match_for_single_success=True,  # Treat single result as exact match
    exact_match_max_level=3,              # Maximum search levels
    return_tokens=True,                   # Return parsed input tokens
    return_rejections=True,               # Include rejected results
    similar_names_distance=8              # Levenshtein distance for similar names
)

Response Models

All API methods return typed response objects with parsed data:

result = client.address("314", "west 100 st", "manhattan")

# Basic address info
print(result.house_number)      # "314"
print(result.street_name)       # "WEST 100 STREET" 
print(result.borough_name)      # "MANHATTAN"
print(result.zip_code)          # "10025"

# Geographic coordinates
print(result.latitude)          # 40.79582646522169
print(result.longitude)         # -73.96790007529056
print(result.x_coordinate)      # State plane coordinate
print(result.y_coordinate)      # State plane coordinate

# Property identifiers
print(result.bbl)               # "1018890001"
print(result.bin)               # "1079043"

# Administrative areas
print(result.community_district)  # "109"
print(result.census_tract)        # "243"

# Processing status
print(result.geosupport.return_code)  # "00" (success)
print(result.geosupport.message)      # Status message

# Raw API response (if needed)
print(result.raw_data)          # Complete API response dict

Error Handling

The library provides comprehensive error handling:

from geoclient.exceptions import (
    GeoClientError,           # Base exception
    GeoClientHTTPError,       # HTTP errors (4xx, 5xx)
    GeoClientAuthError,       # Authentication errors (401, 403)
    GeoClientAPIError,        # Geosupport API errors
)

try:
    result = client.address("999999", "invalid street", "manhattan")
except GeoClientAuthError:
    print("Check your API credentials")
except GeoClientAPIError as e:
    print(f"Geosupport error: {e.geosupport_return_code} - {e.message}")
except GeoClientHTTPError as e:
    print(f"HTTP error {e.status_code}: {e.message}")
except GeoClientError as e:
    print(f"General error: {e.message}")

Configuration

Customize client behavior:

client = GeoClient(
    app_id="your_app_id",
    app_key="your_app_key",
    base_url="https://api.nyc.gov/geo/geoclient/v2/",  # Custom base URL
    timeout=30.0,           # Request timeout in seconds  
    retries=3,              # Number of retries for failed requests
    retry_delay=1.0,        # Delay between retries in seconds  
)

Advanced Usage

Batch Geocoding

The library includes a batch geocoding module for processing many addresses at once, with built-in rate limiting and error handling.

CSV-to-CSV (simplest)

Geocode an entire CSV file in one call:

from geoclient import geocode_csv

# Input CSV must have columns: house_number, street, borough
geocode_csv("addresses.csv", "results.csv", delay=0.1)

Programmatic batch

For more control, use batch_geocode_addresses with your own client:

from geoclient import GeoClient, batch_geocode_addresses

addresses = [
    {"house_number": "314", "street": "west 100 st", "borough": "manhattan"},
    {"house_number": "350", "street": "5th ave", "borough": "manhattan"},
]

with GeoClient() as client:
    results = batch_geocode_addresses(addresses, client, delay=0.1)

for r in results:
    if r["success"]:
        print(f"{r['normalized_address']}: {r['latitude']}, {r['longitude']}")
    else:
        print(f"FAILED {r['input_house_number']} {r['input_street']}: {r['error_message']}")

Load addresses from CSV

from geoclient import load_addresses_from_csv

addresses = load_addresses_from_csv("addresses.csv")
# Returns: [{"house_number": "314", "street": "west 100 st", "borough": "manhattan"}, ...]

Column names are case-insensitive and accept spaces or underscores (e.g., House Number or house_number).

Save results to CSV

from geoclient import save_results_to_csv

save_results_to_csv(results, "output.csv")

Result fields

Each result dict contains:

Field Success Failure
input_house_number, input_street, input_borough Original input Original input
success True False
latitude, longitude Coordinates None
normalized_address, normalized_borough Geosupport-normalized None
zip_code, bbl, bin, community_district Property info None
cross_street_one, cross_street_two Cross streets None
error_message None Error description
geosupport_return_code "00" Error code or None

Borough Validation

# The client accepts various borough formats:
client.address("314", "west 100 st", "manhattan")    # Full name
client.address("314", "west 100 st", "Manhattan")    # Title case
client.address("314", "west 100 st", "MANHATTAN")    # Upper case
client.address("314", "west 100 st", "1")            # Borough code
client.address("314", "west 100 st", "si")           # Staten Island abbreviation

# Valid borough names/codes:
# Manhattan: "manhattan", "1"
# Bronx: "bronx", "2" 
# Brooklyn: "brooklyn", "3"
# Queens: "queens", "4"
# Staten Island: "staten island", "si", "5"

Testing

Run the test suite:

# Install development dependencies
pip install -e ".[dev]"

# Run unit tests
pytest tests/ -v

# Run with coverage
pytest tests/ --cov=geoclient --cov-report=html

# Run integration tests (requires API credentials)
export GEOCLIENT_APP_ID="your_app_id"
export GEOCLIENT_APP_KEY="your_app_key"
pytest tests/test_integration.py -v

API Reference

For complete API documentation, see the official Geoclient documentation.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

Changelog

v1.0.0 (2026-03-19)

  • Initial release
  • Full support for all Geoclient 2.0 API endpoints
  • Comprehensive error handling and type safety
  • Extensive test coverage
  • Complete documentation and examples

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

python_geoclient_moo-1.0.5.1.tar.gz (42.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

python_geoclient_moo-1.0.5.1-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file python_geoclient_moo-1.0.5.1.tar.gz.

File metadata

  • Download URL: python_geoclient_moo-1.0.5.1.tar.gz
  • Upload date:
  • Size: 42.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for python_geoclient_moo-1.0.5.1.tar.gz
Algorithm Hash digest
SHA256 dbe5cd03ba8a6b859154a70635d141a94678f011d96adfc53fac3428fa156744
MD5 0d2f5e53934c864c35c161beffdfc092
BLAKE2b-256 16ce10d3809642c310573b4102823fe9aee3a95c3cc881f9d464d2413617e565

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_geoclient_moo-1.0.5.1.tar.gz:

Publisher: publish.yml on NYCMayorOps/python-geoclient-moo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file python_geoclient_moo-1.0.5.1-py3-none-any.whl.

File metadata

File hashes

Hashes for python_geoclient_moo-1.0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 739f8e0e9bd4f25b10fe261558ce9994192bef8a8558ddc164181b42b0f535bc
MD5 c099091ef5596ae5415d82692850872a
BLAKE2b-256 a12e09adf6ed772ffbf43f1a9c1a26436baced91c60612c68391d6d0dbadb82d

See more details on using hashes here.

Provenance

The following attestation bundles were made for python_geoclient_moo-1.0.5.1-py3-none-any.whl:

Publisher: publish.yml on NYCMayorOps/python-geoclient-moo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page