Metadata-Version: 2.4
Name: latlng
Version: 0.1.0
Summary: Python SDK for the latlng.work geocoding and places API
Project-URL: Homepage, https://latlng.work
Project-URL: Website, https://latlng.work
Project-URL: Documentation, https://latlng.work/docs
Project-URL: Get API Key, https://dash.latlng.work/
Project-URL: Repository, https://github.com/latlng-work/latlng-python
Project-URL: Bug Tracker, https://github.com/latlng-work/latlng-python/issues
Author: latlng.work
License: MIT
Keywords: geocoding,latlng,maps,places,reverse-geocoding
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: GIS
Requires-Python: >=3.8
Requires-Dist: httpx>=0.24.0
Provides-Extra: dev
Requires-Dist: hatch; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Description-Content-Type: text/markdown

# latlng · Python SDK

[![Website](https://img.shields.io/badge/latlng.work-visit-4f46e5?style=flat&logo=globe)](https://latlng.work)
[![PyPI](https://img.shields.io/pypi/v/latlng)](https://pypi.org/project/latlng/)
[![Python](https://img.shields.io/pypi/pyversions/latlng)](https://pypi.org/project/latlng/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

The official Python SDK for [latlng.work](https://latlng.work) — fast, open-data geocoding and places API powered by OpenStreetMap + Overture Maps.

- **Forward geocoding** — address/place name → coordinates
- **Reverse geocoding** — coordinates → address
- **Places nearby** — POIs around a lat/lon
- **Places search** — full-text place search
- **Autosuggest** — typeahead suggestions
- **Categories** — browse available place types

Both **sync** and **async** clients included. `httpx` is the only dependency.

---

## Installation

```bash
pip install latlng
```

> **Get an API key** → [dash.latlng.work](https://dash.latlng.work/)  
> Free tier: 3,000 geocodes/day. No credit card required.

---

## Quick Start

```python
from latlng import LatlngClient

# Get a free API key at https://dash.latlng.work/
client = LatlngClient(api_key="latlng_xxxxx")  # or omit for anonymous (rate-limited)

# Forward geocode
result = client.geocode("Eiffel Tower, Paris")
print(result.first.lat, result.first.lon)
# 48.8584 2.2945

# Reverse geocode
result = client.reverse(48.8584, 2.2945)
print(result.first.name, result.first.city)
# Eiffel Tower Paris

# Places nearby
places = client.places.nearby(lat=48.8584, lon=2.2945, radius=500)
for place in places:
    print(place.name, place.category, f"{place.distance_m}m")

# Full-text place search
results = client.places.search("Louvre", lat=48.8584, lon=2.2945)

# Autosuggest (typeahead)
suggestions = client.places.autosuggest("Eiff", lat=48.858, lon=2.294)

# All place categories
categories = client.places.categories()
for cat in categories:
    print(cat.category, cat.count)
```

---

## Async Usage

```python
import asyncio
from latlng import AsyncLatlngClient

async def main():
    async with AsyncLatlngClient(api_key="latlng_xxxxx") as client:
        result = await client.geocode("Tokyo")
        print(result.first.lat, result.first.lon)

        places = await client.places.nearby(lat=35.6762, lon=139.6503, radius=1000)
        for place in places:
            print(place.name)

asyncio.run(main())
```

---

## API Reference

### `LatlngClient` / `AsyncLatlngClient`

| Constructor Arg | Type | Default | Description |
|---|---|---|---|
| `api_key` | `str \| None` | `None` | API key (`latlng_xxxxx`). Anonymous if omitted. |
| `base_url` | `str` | `https://api.latlng.work` | Override API base URL. |
| `timeout` | `float` | `10.0` | HTTP timeout in seconds. |

### Geocoding

#### `client.geocode(query, *, limit=5, lang=None, lat=None, lon=None)`

Forward geocode a query string. Returns `GeocodingResponse`.

| Param | Type | Description |
|---|---|---|
| `query` | `str` | Address or place name |
| `limit` | `int` | Max results (default 5) |
| `lang` | `str` | Language code (e.g. `"en"`) |
| `lat`, `lon` | `float` | Bias results near this location |

#### `client.reverse(lat, lon, *, limit=1, lang=None)`

Reverse geocode coordinates. Returns `GeocodingResponse`.

### Places

#### `client.places.nearby(lat, lon, *, radius=1000, type=None, limit=20)`

POIs near a location. Returns `NearbyResponse`.

#### `client.places.search(q, *, lat=None, lon=None, type=None, country=None, limit=20)`

Full-text place search. Returns `SearchResponse`.

#### `client.places.autosuggest(q, *, lat=None, lon=None, limit=10)`

Typeahead suggestions. Returns `AutosuggestResponse`.

#### `client.places.categories()`

All available place categories. Returns `CategoriesResponse`.

### Response Models

| Model | Key Fields |
|---|---|
| `GeocodingResult` | `lat`, `lon`, `name`, `city`, `country`, `postcode`, `state` |
| `Place` | `id`, `name`, `lat`, `lon`, `category`, `distance_m`, `country`, `region`, `locality` |
| `Category` | `category`, `count` |

All collection responses (`GeocodingResponse`, `NearbyResponse`, etc.) support iteration with `for item in response`.

### Exceptions

| Exception | When |
|---|---|
| `LatlngRateLimitError` | HTTP 429 — rate limit hit |
| `LatlngAuthError` | HTTP 401/403 — bad API key |
| `LatlngUnavailableError` | HTTP 503 — upstream down |
| `LatlngAPIError` | Any other non-2xx error |

```python
from latlng import LatlngClient
from latlng.exceptions import LatlngRateLimitError

client = LatlngClient()
try:
    result = client.geocode("Berlin")
except LatlngRateLimitError:
    print("Rate limit hit — get a free key at https://dash.latlng.work/")
```

---

## Rate Limits

| Plan | Geocode | Reverse | Places | Period |
|---|---|---|---|---|
| Anonymous | 30 | 10 | 10 | /day |
| Free | 3,000 | 300 | 500 | /day |
| Pro | 1,000,000 | 100,000 | 500,000 | /month |
| Enterprise | Unlimited | Unlimited | Unlimited | /month |

**[→ Get a free API key at dash.latlng.work](https://dash.latlng.work/)**

---

## License

MIT
