Metadata-Version: 2.4
Name: permisapi-client
Version: 0.6.0
Summary: Client Python officiel pour PermisAPI : permis de construire France en API REST.
Project-URL: Homepage, https://permisapi.fr
Project-URL: Documentation, https://permisapi.fr/docs
Project-URL: Changelog, https://permisapi.fr/changelog
Author-email: Evan Caroux <evan@permisapi.fr>
License: MIT
Keywords: api,construire,france,geocoding,permis,proptech,real-estate,sitadel
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Natural Language :: French
Classifier: Programming Language :: Python :: 3
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 :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Description-Content-Type: text/markdown

# permisapi-client

Client Python officiel pour [PermisAPI](https://permisapi.fr) : accede aux 311k+ permis de construire France via une API REST moderne.

## Installation

```bash
pip install permisapi-client
```

## Quickstart

```python
from permisapi_client import PermisAPI

client = PermisAPI(api_key="pk_live_...")  # obtenue sur https://permisapi.fr/#pricing

# Liste
data = client.permits.list(dep_code="75", limit=10)
for permit in data["items"]:
    print(permit["num_pa"], permit["adr_localite_ter"])

# Recherche geographique (PostGIS)
nearby = client.permits.near(lat=48.85, lng=2.35, radius_m=500)

# Detail
permit = client.permits.get("PC 075 115 24 B0001")

# Stats par commune
stats = client.stats.commune("75115")
print(stats["total"], stats["granted"])

# Itere sur toutes les pages
for p in client.permits.iter_all(dep_code="75"):
    print(p["num_pa"])
```

## Cross-ref Pro+ (DVF, Score MDB, PLU, Géorisques)

```python
# Top 5 transactions DVF voisines (5 ans glissants)
dvf = client.permits.dvf("PC07404021K1", limit=5)
for m in dvf["matches"]:
    print(m["valeur_fonciere"], "EUR a", m["distance_m"], "m")

# Score Opportunite Marchand de Biens v0.1 (note 0-100 + tier)
score = client.permits.score_mdb("PC07404021K1")
print(f"Score: {score['score']}/100 ({score['tier']})")

# Zonage urbanisme PLU (constructible vs agricole vs naturelle)
plu = client.permits.plu("PC07404021K1")
if plu["has_plu"]:
    z = plu["zonage"]
    print(f"Zone {z['code']} - constructible: {z['constructible']}")

# Georisques BRGM (inondation, seisme, argile, ICPE proches)
risks = client.permits.risks("PC07404021K1")
print(f"Risk score: {risks['risk_score']}/100 ({risks['risk_tier']})")
for risk in risks["risks"]:
    print(f"- {risk['label']} (PPR: {risk['has_ppr']})")

# Feedback sur le Score MDB (alimente le training V1.0 ML)
client.permits.score_feedback(
    num_pa="PC07404021K1",
    score_received=78,
    tier_received="high",
    useful=False,
    accuracy="too_high",
    comment="SCI familiale, pas un MDB. Le signal SCI=90 est trop generique.",
    recommendation="Distinguer SCI familiale via volume de permis.",
)
```

## Export bulk CSV (Business+)

```python
# Cap par defaut : 100k Business / 1M Enterprise
csv_bytes = client.permits.export(
    dep_code="33",
    permit_type="PC_LOGEMENT",
    date_from="2024-01-01",
    max_rows=5000,
)
with open("bordeaux.csv", "wb") as f:
    f.write(csv_bytes)
```

## MCP server (Claude / ChatGPT / Cursor)

Le serveur MCP officiel (package separe `permisapi-mcp`) permet a Claude
Desktop, Cursor et Windsurf d'appeler PermisAPI en langage naturel :

```bash
pip install permisapi-mcp
```

Configure ta cle dans le client MCP, puis demande directement :
> *"Liste les permis de logement deposes a Bordeaux ce mois avec un score MDB > 70"*

Voir [permisapi.fr/mcp](https://permisapi.fr/mcp) pour le setup complet.

## Async

```python
import asyncio
from permisapi_client import AsyncPermisAPI

async def main():
    async with AsyncPermisAPI(api_key="pk_live_...") as client:
        data = await client.permits.list(dep_code="75")
        async for p in client.permits.iter_all(dep_code="75"):
            print(p["num_pa"])

asyncio.run(main())
```

## Alertes webhook

```python
alert = client.alerts.create(
    name="Paris 18e renovations",
    center_lat=48.88,
    center_lng=2.35,
    radius_km=3,
    permit_types=["PC_LOGEMENT"],
    webhook_url="https://tonapp.fr/permisapi-hook",
)
# Le webhook_secret n'est renvoye QU'ICI. Stocke-le pour verifier les HMAC.
print(alert["webhook_secret"])
```

## Gestion des erreurs

```python
from permisapi_client import (
    PermisAPI,
    PermisAPIRateLimited,
    PermisAPIQuotaExceeded,
    PermisAPIUnauthorized,
)

try:
    client.permits.list()
except PermisAPIUnauthorized:
    print("Cle API invalide")
except PermisAPIQuotaExceeded as e:
    print(f"Quota mensuel atteint, retry dans {e.retry_after}s")
except PermisAPIRateLimited as e:
    print(f"Burst limit, retry dans {e.retry_after}s")
```

## Licence

MIT. Source : SDES, base Sitadel, data.gouv.fr (Licence Ouverte Etalab).

## Liens

- Documentation : https://permisapi.fr/docs
- Reference API complete : https://api.permisapi.fr/docs
- Changelog : https://permisapi.fr/changelog
