permisapi-client 0.8.0__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {permisapi_client-0.8.0 → permisapi_client-0.9.0}/.gitignore +6 -0
- permisapi_client-0.9.0/PKG-INFO +220 -0
- permisapi_client-0.9.0/README.md +192 -0
- {permisapi_client-0.8.0 → permisapi_client-0.9.0}/permisapi_client/__init__.py +4 -3
- {permisapi_client-0.8.0 → permisapi_client-0.9.0}/permisapi_client/client.py +320 -129
- {permisapi_client-0.8.0 → permisapi_client-0.9.0}/permisapi_client/exceptions.py +2 -1
- {permisapi_client-0.8.0 → permisapi_client-0.9.0}/pyproject.toml +2 -2
- {permisapi_client-0.8.0 → permisapi_client-0.9.0}/tests/test_client.py +155 -21
- permisapi_client-0.8.0/PKG-INFO +0 -188
- permisapi_client-0.8.0/README.md +0 -160
|
@@ -36,6 +36,8 @@ SECRETS_INVENTORY.md
|
|
|
36
36
|
data/raw/*.csv
|
|
37
37
|
data/raw/*.zip
|
|
38
38
|
data/processed/*.parquet
|
|
39
|
+
data/cerema_dvfplus/
|
|
40
|
+
data/cadastre_geojson/
|
|
39
41
|
|
|
40
42
|
# Database
|
|
41
43
|
*.sqlite
|
|
@@ -66,3 +68,7 @@ scrape-downloads/
|
|
|
66
68
|
|
|
67
69
|
# Local handoff bundle (assembled then zipped, not committed)
|
|
68
70
|
handoff-bundle/
|
|
71
|
+
|
|
72
|
+
# Exports prospects locaux (donnees derivees, regenerables via scripts/export_prospects.py)
|
|
73
|
+
prospects_export.csv
|
|
74
|
+
prospects_*.csv
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: permisapi-client
|
|
3
|
+
Version: 0.9.0
|
|
4
|
+
Summary: Client Python officiel pour PermisAPI : permis de construire de France en API REST.
|
|
5
|
+
Project-URL: Homepage, https://permisapi.fr
|
|
6
|
+
Project-URL: Documentation, https://permisapi.fr/docs
|
|
7
|
+
Project-URL: Changelog, https://permisapi.fr/changelog
|
|
8
|
+
Author-email: Evan Caroux <evan@permisapi.fr>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: api,construire,france,geocoding,permis,proptech,real-estate,sitadel
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Natural Language :: French
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: httpx>=0.24.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
26
|
+
Requires-Dist: respx>=0.20; extra == 'dev'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# permisapi-client
|
|
30
|
+
|
|
31
|
+
Client Python officiel de [PermisAPI](https://permisapi.fr) : 1,3 million de permis de
|
|
32
|
+
construire en France (base Sitadel, permis autorisés depuis 2013, 101 départements) dans
|
|
33
|
+
une API REST.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install permisapi-client
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Démarrage rapide
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from permisapi_client import PermisAPI
|
|
45
|
+
|
|
46
|
+
client = PermisAPI(api_key="pk_live_...") # clé obtenue sur https://permisapi.fr/#pricing
|
|
47
|
+
|
|
48
|
+
# Liste paginée : {"data": [...], "pagination": {page, limit, total, has_next, next_url}}
|
|
49
|
+
page = client.permits.list(dep_code="75", limit=10)
|
|
50
|
+
for permit in page["data"]:
|
|
51
|
+
print(permit["num_pa"], permit["full_address"])
|
|
52
|
+
print(page["pagination"]["total"], "permis au total")
|
|
53
|
+
|
|
54
|
+
# Recherche géographique (PostGIS) : liste de permis, chacun avec distance_m
|
|
55
|
+
nearby = client.permits.near(lat=48.8599, lng=2.3423, radius_m=300)
|
|
56
|
+
|
|
57
|
+
# Détail d'un permis
|
|
58
|
+
permit = client.permits.get("07510125V0025")
|
|
59
|
+
|
|
60
|
+
# Un même numéro peut porter plusieurs autorisations (un PC et un PD, par
|
|
61
|
+
# exemple) : la fiche liste les autres types dans autres_types, et type=
|
|
62
|
+
# en désigne une. permit["identifiant"] (type et numéro accolés) est la
|
|
63
|
+
# forme à conserver ; elle est acceptée partout à la place du numéro.
|
|
64
|
+
if permit.get("autres_types"):
|
|
65
|
+
autre = client.permits.get(permit["num_pa"], type=permit["autres_types"][0])
|
|
66
|
+
|
|
67
|
+
# Statistiques d'une commune (Paris entier est rangé sous le code 75056 dans Sitadel)
|
|
68
|
+
stats = client.stats.commune("75056")
|
|
69
|
+
print(stats["total_permits"], stats["granted"], stats["trend_yoy_pct"])
|
|
70
|
+
|
|
71
|
+
# Toutes les pages, jusqu'à pagination.has_next == False
|
|
72
|
+
for p in client.permits.iter_all(dep_code="75", permit_type="PC_LOGEMENT"):
|
|
73
|
+
print(p["num_pa"])
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Coût : une recherche (`list`, `iter_all`, `near`, `search`) décompte 1 requête par permis
|
|
77
|
+
renvoyé. `iter_all` sur un département entier peut donc épuiser le quota mensuel de la
|
|
78
|
+
formule Gratuit (500 requêtes), Découverte (2 000) ou Explorateur (5 000).
|
|
79
|
+
|
|
80
|
+
Formules : **Gratuit** (1 département, les nouveaux permis des 2 dernières publications
|
|
81
|
+
Sitadel), **Découverte** (1 département, 6 mois), **Explorateur** (5 départements, 12 mois),
|
|
82
|
+
**Régional** (5 départements, historique complet et données avancées), **Pro** (les mêmes
|
|
83
|
+
données avancées sur la France entière), **Business** et **Entreprise**. Les
|
|
84
|
+
exemples de cette page portent sur Paris (75).
|
|
85
|
+
|
|
86
|
+
## Données avancées (formules Régional, Pro, Business et Entreprise)
|
|
87
|
+
|
|
88
|
+
Les formules Gratuit, Découverte et Explorateur reçoivent une erreur 402 sur ces routes.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
num_pa = "07510125V0025"
|
|
92
|
+
|
|
93
|
+
# Ventes immobilières DVF voisines (ventes de 2021 à 2025)
|
|
94
|
+
dvf = client.permits.dvf(num_pa, limit=5)
|
|
95
|
+
for m in dvf["matches"]:
|
|
96
|
+
print(m["date_mutation"], m["valeur_fonciere"], "EUR à", m["distance_m"], "m")
|
|
97
|
+
print("Prix médian au m² :", dvf["dvf_median_price_per_m2"])
|
|
98
|
+
|
|
99
|
+
# Score d'opportunité marchand de biens v0.3 (note 0-100 et palier)
|
|
100
|
+
score = client.permits.score_mdb(num_pa)
|
|
101
|
+
print(f"Score : {score['score']}/100 ({score['tier']})")
|
|
102
|
+
# score["components"] : note 0-100 de chacun des 11 signaux ; une explication
|
|
103
|
+
# en français de chaque signal : route /v1/permits/{num_pa}/score/explain.
|
|
104
|
+
|
|
105
|
+
# Zonage PLU (constructible, à urbaniser, agricole, naturel)
|
|
106
|
+
plu = client.permits.plu(num_pa)
|
|
107
|
+
if plu["has_plu"]:
|
|
108
|
+
z = plu["zonage"]
|
|
109
|
+
print(f"Zone {z['code']}, constructible : {z['constructible']}")
|
|
110
|
+
|
|
111
|
+
# Risques Géorisques (inondation, séisme, sites Seveso proches...)
|
|
112
|
+
risks = client.permits.risks(num_pa)
|
|
113
|
+
print(risks["risk_summary"]) # niveau, et ce qu'il mesure
|
|
114
|
+
for raison in risks["risk_reasons"]: # d'où viennent les points
|
|
115
|
+
print("-", raison)
|
|
116
|
+
|
|
117
|
+
# Votre avis sur un score
|
|
118
|
+
client.permits.score_feedback(
|
|
119
|
+
num_pa=num_pa,
|
|
120
|
+
score_received=72,
|
|
121
|
+
tier_received="high",
|
|
122
|
+
useful=True,
|
|
123
|
+
accuracy="correct",
|
|
124
|
+
comment="Score cohérent avec notre analyse du quartier.",
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Export CSV (Business et Entreprise)
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
# Plafond : 100 000 lignes en Business, 1 000 000 en Entreprise
|
|
132
|
+
csv_bytes = client.permits.export(
|
|
133
|
+
dep_code="75",
|
|
134
|
+
permit_type="PC_LOGEMENT",
|
|
135
|
+
date_from="2024-01-01",
|
|
136
|
+
max_rows=5000,
|
|
137
|
+
)
|
|
138
|
+
with open("paris.csv", "wb") as f:
|
|
139
|
+
f.write(csv_bytes)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Serveur MCP (Claude, ChatGPT, Cursor)
|
|
143
|
+
|
|
144
|
+
Le serveur MCP officiel (paquet séparé `permisapi-mcp`) permet à Claude, ChatGPT, Claude
|
|
145
|
+
Desktop, Cursor et Windsurf d'interroger PermisAPI en langage naturel :
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
pip install permisapi-mcp
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Configurez votre clé dans le client MCP, puis demandez directement :
|
|
152
|
+
> *« Liste les permis de logement autorisés à Paris ce mois-ci »*
|
|
153
|
+
|
|
154
|
+
Mise en place complète : [permisapi.fr/mcp](https://permisapi.fr/mcp).
|
|
155
|
+
|
|
156
|
+
## Async
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
import asyncio
|
|
160
|
+
from permisapi_client import AsyncPermisAPI
|
|
161
|
+
|
|
162
|
+
async def main():
|
|
163
|
+
async with AsyncPermisAPI(api_key="pk_live_...") as client:
|
|
164
|
+
page = await client.permits.list(dep_code="75")
|
|
165
|
+
print(page["pagination"]["total"])
|
|
166
|
+
async for p in client.permits.iter_all(dep_code="75", permit_type="PC_LOGEMENT"):
|
|
167
|
+
print(p["num_pa"])
|
|
168
|
+
|
|
169
|
+
asyncio.run(main())
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Alertes par webhook
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
alert = client.alerts.create(
|
|
176
|
+
name="Paris 18e rénovations",
|
|
177
|
+
center_lat=48.88,
|
|
178
|
+
center_lng=2.35,
|
|
179
|
+
radius_km=3,
|
|
180
|
+
permit_types=["PC_LOGEMENT"],
|
|
181
|
+
webhook_url="https://votre-application.fr/permisapi-hook",
|
|
182
|
+
)
|
|
183
|
+
# Le webhook_secret n'est renvoyé qu'ICI : conservez-le pour vérifier les signatures HMAC.
|
|
184
|
+
print(alert["webhook_secret"])
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Gestion des erreurs
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from permisapi_client import (
|
|
191
|
+
PermisAPI,
|
|
192
|
+
PermisAPIRateLimited,
|
|
193
|
+
PermisAPIQuotaExceeded,
|
|
194
|
+
PermisAPIUnauthorized,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
client.permits.list(dep_code="75")
|
|
199
|
+
except PermisAPIUnauthorized:
|
|
200
|
+
print("Clé API invalide")
|
|
201
|
+
except PermisAPIQuotaExceeded:
|
|
202
|
+
print("Quota mensuel atteint : il se renouvelle le 1er du mois (UTC)")
|
|
203
|
+
except PermisAPIRateLimited as e:
|
|
204
|
+
print(f"Trop de requêtes par minute, nouvel essai possible dans {e.retry_after} s")
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Journal des versions
|
|
208
|
+
|
|
209
|
+
- **0.9.0** (12/11/2026, bascule de la clé type et numéro, C24) : argument facultatif `type` (`PC`, `DP`, `PA` ou `PD`) sur les méthodes qui prennent un `num_pa` ; `identifiant` (`PC0330632600176`) accepté partout à la place du numéro ; l'export CSV gagne `type_autorisation` et `identifiant` en fin de ligne. Version mineure : ajouts seulement, aucun appel existant ne change.
|
|
210
|
+
- Versions précédentes : historique du dépôt (`git log -- sdk-python/`).
|
|
211
|
+
|
|
212
|
+
## Licence
|
|
213
|
+
|
|
214
|
+
MIT. Source des permis : SDES, base Sitadel, data.gouv.fr (Licence Ouverte Etalab).
|
|
215
|
+
|
|
216
|
+
## Liens
|
|
217
|
+
|
|
218
|
+
- Documentation : https://permisapi.fr/docs
|
|
219
|
+
- Référence complète de l'API : https://api.permisapi.fr/docs
|
|
220
|
+
- Nouveautés : https://permisapi.fr/changelog
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# permisapi-client
|
|
2
|
+
|
|
3
|
+
Client Python officiel de [PermisAPI](https://permisapi.fr) : 1,3 million de permis de
|
|
4
|
+
construire en France (base Sitadel, permis autorisés depuis 2013, 101 départements) dans
|
|
5
|
+
une API REST.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install permisapi-client
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Démarrage rapide
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from permisapi_client import PermisAPI
|
|
17
|
+
|
|
18
|
+
client = PermisAPI(api_key="pk_live_...") # clé obtenue sur https://permisapi.fr/#pricing
|
|
19
|
+
|
|
20
|
+
# Liste paginée : {"data": [...], "pagination": {page, limit, total, has_next, next_url}}
|
|
21
|
+
page = client.permits.list(dep_code="75", limit=10)
|
|
22
|
+
for permit in page["data"]:
|
|
23
|
+
print(permit["num_pa"], permit["full_address"])
|
|
24
|
+
print(page["pagination"]["total"], "permis au total")
|
|
25
|
+
|
|
26
|
+
# Recherche géographique (PostGIS) : liste de permis, chacun avec distance_m
|
|
27
|
+
nearby = client.permits.near(lat=48.8599, lng=2.3423, radius_m=300)
|
|
28
|
+
|
|
29
|
+
# Détail d'un permis
|
|
30
|
+
permit = client.permits.get("07510125V0025")
|
|
31
|
+
|
|
32
|
+
# Un même numéro peut porter plusieurs autorisations (un PC et un PD, par
|
|
33
|
+
# exemple) : la fiche liste les autres types dans autres_types, et type=
|
|
34
|
+
# en désigne une. permit["identifiant"] (type et numéro accolés) est la
|
|
35
|
+
# forme à conserver ; elle est acceptée partout à la place du numéro.
|
|
36
|
+
if permit.get("autres_types"):
|
|
37
|
+
autre = client.permits.get(permit["num_pa"], type=permit["autres_types"][0])
|
|
38
|
+
|
|
39
|
+
# Statistiques d'une commune (Paris entier est rangé sous le code 75056 dans Sitadel)
|
|
40
|
+
stats = client.stats.commune("75056")
|
|
41
|
+
print(stats["total_permits"], stats["granted"], stats["trend_yoy_pct"])
|
|
42
|
+
|
|
43
|
+
# Toutes les pages, jusqu'à pagination.has_next == False
|
|
44
|
+
for p in client.permits.iter_all(dep_code="75", permit_type="PC_LOGEMENT"):
|
|
45
|
+
print(p["num_pa"])
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Coût : une recherche (`list`, `iter_all`, `near`, `search`) décompte 1 requête par permis
|
|
49
|
+
renvoyé. `iter_all` sur un département entier peut donc épuiser le quota mensuel de la
|
|
50
|
+
formule Gratuit (500 requêtes), Découverte (2 000) ou Explorateur (5 000).
|
|
51
|
+
|
|
52
|
+
Formules : **Gratuit** (1 département, les nouveaux permis des 2 dernières publications
|
|
53
|
+
Sitadel), **Découverte** (1 département, 6 mois), **Explorateur** (5 départements, 12 mois),
|
|
54
|
+
**Régional** (5 départements, historique complet et données avancées), **Pro** (les mêmes
|
|
55
|
+
données avancées sur la France entière), **Business** et **Entreprise**. Les
|
|
56
|
+
exemples de cette page portent sur Paris (75).
|
|
57
|
+
|
|
58
|
+
## Données avancées (formules Régional, Pro, Business et Entreprise)
|
|
59
|
+
|
|
60
|
+
Les formules Gratuit, Découverte et Explorateur reçoivent une erreur 402 sur ces routes.
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
num_pa = "07510125V0025"
|
|
64
|
+
|
|
65
|
+
# Ventes immobilières DVF voisines (ventes de 2021 à 2025)
|
|
66
|
+
dvf = client.permits.dvf(num_pa, limit=5)
|
|
67
|
+
for m in dvf["matches"]:
|
|
68
|
+
print(m["date_mutation"], m["valeur_fonciere"], "EUR à", m["distance_m"], "m")
|
|
69
|
+
print("Prix médian au m² :", dvf["dvf_median_price_per_m2"])
|
|
70
|
+
|
|
71
|
+
# Score d'opportunité marchand de biens v0.3 (note 0-100 et palier)
|
|
72
|
+
score = client.permits.score_mdb(num_pa)
|
|
73
|
+
print(f"Score : {score['score']}/100 ({score['tier']})")
|
|
74
|
+
# score["components"] : note 0-100 de chacun des 11 signaux ; une explication
|
|
75
|
+
# en français de chaque signal : route /v1/permits/{num_pa}/score/explain.
|
|
76
|
+
|
|
77
|
+
# Zonage PLU (constructible, à urbaniser, agricole, naturel)
|
|
78
|
+
plu = client.permits.plu(num_pa)
|
|
79
|
+
if plu["has_plu"]:
|
|
80
|
+
z = plu["zonage"]
|
|
81
|
+
print(f"Zone {z['code']}, constructible : {z['constructible']}")
|
|
82
|
+
|
|
83
|
+
# Risques Géorisques (inondation, séisme, sites Seveso proches...)
|
|
84
|
+
risks = client.permits.risks(num_pa)
|
|
85
|
+
print(risks["risk_summary"]) # niveau, et ce qu'il mesure
|
|
86
|
+
for raison in risks["risk_reasons"]: # d'où viennent les points
|
|
87
|
+
print("-", raison)
|
|
88
|
+
|
|
89
|
+
# Votre avis sur un score
|
|
90
|
+
client.permits.score_feedback(
|
|
91
|
+
num_pa=num_pa,
|
|
92
|
+
score_received=72,
|
|
93
|
+
tier_received="high",
|
|
94
|
+
useful=True,
|
|
95
|
+
accuracy="correct",
|
|
96
|
+
comment="Score cohérent avec notre analyse du quartier.",
|
|
97
|
+
)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Export CSV (Business et Entreprise)
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
# Plafond : 100 000 lignes en Business, 1 000 000 en Entreprise
|
|
104
|
+
csv_bytes = client.permits.export(
|
|
105
|
+
dep_code="75",
|
|
106
|
+
permit_type="PC_LOGEMENT",
|
|
107
|
+
date_from="2024-01-01",
|
|
108
|
+
max_rows=5000,
|
|
109
|
+
)
|
|
110
|
+
with open("paris.csv", "wb") as f:
|
|
111
|
+
f.write(csv_bytes)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Serveur MCP (Claude, ChatGPT, Cursor)
|
|
115
|
+
|
|
116
|
+
Le serveur MCP officiel (paquet séparé `permisapi-mcp`) permet à Claude, ChatGPT, Claude
|
|
117
|
+
Desktop, Cursor et Windsurf d'interroger PermisAPI en langage naturel :
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
pip install permisapi-mcp
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Configurez votre clé dans le client MCP, puis demandez directement :
|
|
124
|
+
> *« Liste les permis de logement autorisés à Paris ce mois-ci »*
|
|
125
|
+
|
|
126
|
+
Mise en place complète : [permisapi.fr/mcp](https://permisapi.fr/mcp).
|
|
127
|
+
|
|
128
|
+
## Async
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
import asyncio
|
|
132
|
+
from permisapi_client import AsyncPermisAPI
|
|
133
|
+
|
|
134
|
+
async def main():
|
|
135
|
+
async with AsyncPermisAPI(api_key="pk_live_...") as client:
|
|
136
|
+
page = await client.permits.list(dep_code="75")
|
|
137
|
+
print(page["pagination"]["total"])
|
|
138
|
+
async for p in client.permits.iter_all(dep_code="75", permit_type="PC_LOGEMENT"):
|
|
139
|
+
print(p["num_pa"])
|
|
140
|
+
|
|
141
|
+
asyncio.run(main())
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Alertes par webhook
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
alert = client.alerts.create(
|
|
148
|
+
name="Paris 18e rénovations",
|
|
149
|
+
center_lat=48.88,
|
|
150
|
+
center_lng=2.35,
|
|
151
|
+
radius_km=3,
|
|
152
|
+
permit_types=["PC_LOGEMENT"],
|
|
153
|
+
webhook_url="https://votre-application.fr/permisapi-hook",
|
|
154
|
+
)
|
|
155
|
+
# Le webhook_secret n'est renvoyé qu'ICI : conservez-le pour vérifier les signatures HMAC.
|
|
156
|
+
print(alert["webhook_secret"])
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Gestion des erreurs
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from permisapi_client import (
|
|
163
|
+
PermisAPI,
|
|
164
|
+
PermisAPIRateLimited,
|
|
165
|
+
PermisAPIQuotaExceeded,
|
|
166
|
+
PermisAPIUnauthorized,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
client.permits.list(dep_code="75")
|
|
171
|
+
except PermisAPIUnauthorized:
|
|
172
|
+
print("Clé API invalide")
|
|
173
|
+
except PermisAPIQuotaExceeded:
|
|
174
|
+
print("Quota mensuel atteint : il se renouvelle le 1er du mois (UTC)")
|
|
175
|
+
except PermisAPIRateLimited as e:
|
|
176
|
+
print(f"Trop de requêtes par minute, nouvel essai possible dans {e.retry_after} s")
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Journal des versions
|
|
180
|
+
|
|
181
|
+
- **0.9.0** (12/11/2026, bascule de la clé type et numéro, C24) : argument facultatif `type` (`PC`, `DP`, `PA` ou `PD`) sur les méthodes qui prennent un `num_pa` ; `identifiant` (`PC0330632600176`) accepté partout à la place du numéro ; l'export CSV gagne `type_autorisation` et `identifiant` en fin de ligne. Version mineure : ajouts seulement, aucun appel existant ne change.
|
|
182
|
+
- Versions précédentes : historique du dépôt (`git log -- sdk-python/`).
|
|
183
|
+
|
|
184
|
+
## Licence
|
|
185
|
+
|
|
186
|
+
MIT. Source des permis : SDES, base Sitadel, data.gouv.fr (Licence Ouverte Etalab).
|
|
187
|
+
|
|
188
|
+
## Liens
|
|
189
|
+
|
|
190
|
+
- Documentation : https://permisapi.fr/docs
|
|
191
|
+
- Référence complète de l'API : https://api.permisapi.fr/docs
|
|
192
|
+
- Nouveautés : https://permisapi.fr/changelog
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
Usage sync :
|
|
4
4
|
from permisapi_client import PermisAPI
|
|
5
5
|
client = PermisAPI(api_key="pk_live_...")
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
page = client.permits.list(dep_code="75", limit=10)
|
|
7
|
+
for permit in page["data"]:
|
|
8
|
+
print(permit["num_pa"], permit["full_address"])
|
|
8
9
|
|
|
9
10
|
Usage async :
|
|
10
11
|
from permisapi_client import AsyncPermisAPI
|
|
@@ -30,4 +31,4 @@ __all__ = [
|
|
|
30
31
|
"PermisAPIValidationError",
|
|
31
32
|
]
|
|
32
33
|
|
|
33
|
-
__version__ = "0.
|
|
34
|
+
__version__ = "0.9.0"
|