Metadata-Version: 2.4
Name: plannexus
Version: 0.1.0
Summary: Official Python client for the PlanNexus planning data API.
Project-URL: Homepage, https://plannexus.io
Project-URL: Documentation, https://plannexus.io/docs
Project-URL: Repository, https://github.com/jbaeda/PlanNexus
Project-URL: Issues, https://github.com/jbaeda/PlanNexus/issues
Author-email: PlanNexus <support@plannexus.io>
License-Expression: MIT
Keywords: api,local-government,planning,planning-applications,uk
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.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Description-Content-Type: text/markdown

# PlanNexus Python SDK

Official Python client for the [PlanNexus](https://plannexus.io) planning
data API — access live planning applications from 100+ UK local authorities
through one consistent interface.

## Install

```bash
pip install plannexus
```

Requires Python 3.10+. Depends only on `httpx`.

## Usage

### Synchronous

```python
from plannexus import PlanNexus

with PlanNexus(api_key="pn_live_...") as client:
    # Full-text + structured search
    result = client.applications.search(q="extension", postcode="SW1", per_page=10)
    for app in result["data"]:
        print(app["reference"], "-", app["address"])

    # Nearby (radius in metres)
    nearby = client.applications.nearby(lat=51.509, lng=-0.118, radius=500)

    # Single record with full detail
    full = client.applications.get(app["id"])
```

### Asynchronous

```python
import asyncio
from plannexus import AsyncPlanNexus

async def main():
    async with AsyncPlanNexus(api_key="pn_live_...") as client:
        result = await client.applications.search(postcode="SW1")
        print(f"Matched {result['meta']['total']} applications")

asyncio.run(main())
```

## Rate limits & retries

The client automatically retries `429 Too Many Requests` up to 3 times,
honouring the `Retry-After` header when present. After retries are
exhausted a `RateLimitError` is raised with the `.retry_after` attribute.

```python
from plannexus import PlanNexus, RateLimitError

try:
    client.applications.search(...)
except RateLimitError as e:
    print(f"Still rate-limited; hint was {e.retry_after}s")
```

## Error handling

Any non-2xx (other than handled 429s) raises `PlanNexusError` with
`.status_code`, `.detail`, and `.body` populated from the response.

## Links

- [API reference](https://plannexus.io/docs)
- [Coverage](https://plannexus.io/coverage)
- [Source](https://github.com/jbaeda/PlanNexus)
