Python SDK Quick Start — PLSS Coordinate Conversion in Python

Install the townshipamerica Python package and convert PLSS legal land descriptions to GPS coordinates in four lines of code. Typed responses, sync and async clients.

Convert PLSS legal land descriptions to GPS coordinates in Python with the official townshipamerica package. MIT license, typed Pydantic v2 models, sync and async clients.

Installation

Install from PyPI with pip:

pip install townshipamerica

Requires Python 3.9 or later. Dependencies (httpx, pydantic) install automatically.

Your first conversion

Get an API key

Sign up at townshipamerica.com/app and subscribe to the Search API from the Developer Portal.

Set your API key

Store your key as an environment variable — never hardcode it in source files.

export TOWNSHIPAMERICA_API_KEY="your_api_key"

Convert a PLSS description

import os
from townshipamerica import TownshipAmerica

client = TownshipAmerica(api_key=os.environ["TOWNSHIPAMERICA_API_KEY"])
result = client.search("NENE 12 4N 5E Indian Meridian")

feature = result.features[0]
print(feature.properties)           # Section metadata
print(feature.geometry.coordinates) # Boundary polygon

That's it — result is a typed FeatureCollection with GeoJSON geometry and PLSS metadata.

Reverse geocoding

Find the PLSS legal land description at a GPS coordinate:

result = client.reverse(-97.5, 36.1)

feature = result.features[0]
print(feature.properties.descriptor)  # "NWSE 15 4N 7W Indian Meridian"

The unit parameter controls precision — "Township", "Section", "Quarter Section", or "all":

result = client.reverse(-97.5, 36.1, unit="Quarter Section")

Autocomplete

Add type-ahead search for PLSS descriptions:

suggestions = client.autocomplete("T4N R5E", limit=5)
for item in suggestions.results:
    print(item.text, item.location)

The proximity parameter biases results toward a GPS coordinate:

suggestions = client.autocomplete(
    "T4N",
    limit=5,
    proximity=(-97.5, 36.1)
)

Batch conversion

Convert up to 100 PLSS descriptions in a single request:

locations = [
    "NENE 12 4N 5E Indian Meridian",
    "NESW 25 5N 30E Mount Diablo Meridian",
    "SWNE 22 3N 7E Montana Meridian",
]

results = client.batch_search(locations)
for fc in results:
    feature = fc.features[0]
    print(feature.properties.descriptor, feature.geometry.coordinates[0][0][:2])

Batch reverse geocoding works the same way:

coordinates = [(-97.5, 36.1), (-110.0, 45.5), (-104.8, 39.7)]
results = client.batch_reverse(coordinates, unit="Section")

Error handling

The SDK raises typed exceptions mapped to HTTP status codes:

from townshipamerica.exceptions import (
    AuthenticationError,
    NotFoundError,
    RateLimitError,
    ValidationError,
)

try:
    result = client.search("invalid input")
except ValidationError as e:
    print(f"Bad input: {e}")
except AuthenticationError:
    print("Check your API key")
except RateLimitError as e:
    print(f"Rate limited — retry after {e.retry_after} seconds")
except NotFoundError:
    print("No PLSS data at that location")

Context manager

The client supports with statements for automatic cleanup:

with TownshipAmerica(api_key=os.environ["TOWNSHIPAMERICA_API_KEY"]) as client:
    result = client.search("NENE 12 4N 5E Indian Meridian")
    print(result.features[0].properties)
# Connection closed automatically

Need help? Contact us or open an issue on GitHub.