paravane 0.2.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.
- paravane-0.2.0/LICENSE +21 -0
- paravane-0.2.0/PKG-INFO +697 -0
- paravane-0.2.0/README.md +637 -0
- paravane-0.2.0/pyproject.toml +68 -0
- paravane-0.2.0/setup.cfg +4 -0
- paravane-0.2.0/src/paravane/__init__.py +39 -0
- paravane-0.2.0/src/paravane/_version.py +1 -0
- paravane-0.2.0/src/paravane/client.py +49 -0
- paravane-0.2.0/src/paravane/errors.py +67 -0
- paravane-0.2.0/src/paravane/http.py +166 -0
- paravane-0.2.0/src/paravane/py.typed +1 -0
- paravane-0.2.0/src/paravane/smtprs.py +68 -0
- paravane-0.2.0/src/paravane/types.py +172 -0
- paravane-0.2.0/src/paravane.egg-info/PKG-INFO +697 -0
- paravane-0.2.0/src/paravane.egg-info/SOURCES.txt +19 -0
- paravane-0.2.0/src/paravane.egg-info/dependency_links.txt +1 -0
- paravane-0.2.0/src/paravane.egg-info/requires.txt +12 -0
- paravane-0.2.0/src/paravane.egg-info/top_level.txt +1 -0
- paravane-0.2.0/tests/test_client.py +27 -0
- paravane-0.2.0/tests/test_errors.py +45 -0
- paravane-0.2.0/tests/test_smtprs.py +170 -0
paravane-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Paravane Labs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
paravane-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: paravane
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for the Paravane API.
|
|
5
|
+
Author-email: Paravane Labs <contact@paravane.io>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2026 Paravane Labs
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://paravane.io
|
|
29
|
+
Project-URL: Documentation, https://paravane.io/pages/docs/index.html
|
|
30
|
+
Project-URL: Changelog, https://github.com/paravaneai/paravane-python/blob/main/CHANGELOG.md
|
|
31
|
+
Project-URL: Source, https://github.com/paravaneai/paravane-python
|
|
32
|
+
Project-URL: Issues, https://github.com/paravaneai/paravane-python/issues
|
|
33
|
+
Keywords: paravane,smtprs,email risk,api,sdk
|
|
34
|
+
Classifier: Development Status :: 3 - Alpha
|
|
35
|
+
Classifier: Intended Audience :: Developers
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Operating System :: OS Independent
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
45
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
46
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
47
|
+
Requires-Python: >=3.8
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
49
|
+
License-File: LICENSE
|
|
50
|
+
Requires-Dist: requests>=2.31.0
|
|
51
|
+
Provides-Extra: dev
|
|
52
|
+
Requires-Dist: build>=1.2.1; extra == "dev"
|
|
53
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
54
|
+
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
55
|
+
Requires-Dist: setuptools>=61.0; extra == "dev"
|
|
56
|
+
Requires-Dist: tomli>=2.0.0; python_version < "3.11" and extra == "dev"
|
|
57
|
+
Requires-Dist: twine>=5.1.0; extra == "dev"
|
|
58
|
+
Requires-Dist: wheel; extra == "dev"
|
|
59
|
+
Dynamic: license-file
|
|
60
|
+
|
|
61
|
+
<p align="center">
|
|
62
|
+
<picture>
|
|
63
|
+
<source
|
|
64
|
+
media="(prefers-color-scheme: light)"
|
|
65
|
+
srcset="https://raw.githubusercontent.com/paravaneai/paravane-python/main/doc/images/readme/paravane-smtprs-sdk-readme-banner-logo-matched-orange-dark-developer-api-upscaled-3x.png" />
|
|
66
|
+
<img
|
|
67
|
+
src="https://raw.githubusercontent.com/paravaneai/paravane-python/main/doc/images/readme/paravane-smtprs-sdk-readme-banner-logo-matched-orange-dark-developer-api-upscaled-3x.png"
|
|
68
|
+
alt="Paravane" />
|
|
69
|
+
</picture>
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
<h1 align="center">
|
|
73
|
+
Paravane Python Library
|
|
74
|
+
</h1>
|
|
75
|
+
|
|
76
|
+
<p align="center">
|
|
77
|
+
The Paravane Python library provides convenient access to the Paravane API from applications written in Python.
|
|
78
|
+
<br /><br />
|
|
79
|
+
The first supported product API is <strong>smtpRS</strong>, Paravane's email risk intelligence API.
|
|
80
|
+
smtpRS helps score email addresses for onboarding, trust, review, abuse-prevention, and operations workflows.
|
|
81
|
+
<br /><br />
|
|
82
|
+
This SDK is intentionally small at the start. It provides a stable client entrypoint,
|
|
83
|
+
request serialization, response helpers, structured errors, examples, tests, and packaging
|
|
84
|
+
conventions that can grow as Paravane adds more APIs.
|
|
85
|
+
</p>
|
|
86
|
+
|
|
87
|
+
<p align="center">
|
|
88
|
+
<a href="https://pypi.org/project/paravane/">
|
|
89
|
+
<img
|
|
90
|
+
src="https://img.shields.io/pypi/v/paravane.svg"
|
|
91
|
+
alt="PyPI" />
|
|
92
|
+
</a>
|
|
93
|
+
<a href="https://pypi.org/project/paravane/">
|
|
94
|
+
<img
|
|
95
|
+
src="https://img.shields.io/pypi/pyversions/paravane.svg"
|
|
96
|
+
alt="Supported Python versions" />
|
|
97
|
+
</a>
|
|
98
|
+
<a href="https://github.com/paravaneai/paravane-python/actions/workflows/tests.yml">
|
|
99
|
+
<img
|
|
100
|
+
src="https://github.com/paravaneai/paravane-python/actions/workflows/tests.yml/badge.svg"
|
|
101
|
+
alt="Tests" />
|
|
102
|
+
</a>
|
|
103
|
+
</p>
|
|
104
|
+
|
|
105
|
+
<h3 align="center">
|
|
106
|
+
<a href="#documentation">Documentation</a>
|
|
107
|
+
<span> · </span>
|
|
108
|
+
<a href="#installation">Installation</a>
|
|
109
|
+
<span> · </span>
|
|
110
|
+
<a href="#quickstart">Quickstart</a>
|
|
111
|
+
<span> · </span>
|
|
112
|
+
<a href="#examples">Examples</a>
|
|
113
|
+
<span> · </span>
|
|
114
|
+
<a href="#development">Development</a>
|
|
115
|
+
</h3>
|
|
116
|
+
|
|
117
|
+
## Contents
|
|
118
|
+
|
|
119
|
+
- [Documentation](#documentation)
|
|
120
|
+
- [Installation](#installation)
|
|
121
|
+
- [Requirements](#requirements)
|
|
122
|
+
- [Quickstart](#quickstart)
|
|
123
|
+
- [Usage](#usage)
|
|
124
|
+
- [smtpRS](#smtprs)
|
|
125
|
+
- [Configuration](#configuration)
|
|
126
|
+
- [Per-request options](#per-request-options)
|
|
127
|
+
- [Custom HTTP sessions](#custom-http-sessions)
|
|
128
|
+
- [Retries](#retries)
|
|
129
|
+
- [Idempotency](#idempotency)
|
|
130
|
+
- [Errors](#errors)
|
|
131
|
+
- [Responses and raw data](#responses-and-raw-data)
|
|
132
|
+
- [Types](#types)
|
|
133
|
+
- [Logging](#logging)
|
|
134
|
+
- [Examples](#examples)
|
|
135
|
+
- [Development](#development)
|
|
136
|
+
- [Repository layout](#repository-layout)
|
|
137
|
+
- [Versioning](#versioning)
|
|
138
|
+
- [Security](#security)
|
|
139
|
+
- [Support](#support)
|
|
140
|
+
|
|
141
|
+
## Documentation
|
|
142
|
+
|
|
143
|
+
- Product docs: https://paravane.io/pages/docs/index.html
|
|
144
|
+
- smtpRS docs: https://paravane.io/pages/docs/smtprs/index.html
|
|
145
|
+
- Analyse endpoint: https://paravane.io/pages/docs/smtprs/analyse.html
|
|
146
|
+
- Plans and limits: https://paravane.io/pages/docs/smtprs/plans-limits.html
|
|
147
|
+
|
|
148
|
+
## Installation
|
|
149
|
+
|
|
150
|
+
Install the latest release from PyPI:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
python -m pip install --upgrade paravane
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
To install the current development version directly from GitHub:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
python -m pip install "git+https://github.com/paravaneai/paravane-python.git"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
For local development:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
git clone git@github.com:paravaneai/paravane-python.git
|
|
166
|
+
cd paravane-python
|
|
167
|
+
python -m pip install -e ".[dev]"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Requirements
|
|
171
|
+
|
|
172
|
+
Python 3.8 or newer.
|
|
173
|
+
|
|
174
|
+
Runtime dependency:
|
|
175
|
+
|
|
176
|
+
- `requests>=2.31.0`
|
|
177
|
+
|
|
178
|
+
Development dependencies are installed with:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
python -m pip install -e ".[dev]"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Quickstart
|
|
185
|
+
|
|
186
|
+
Set your API key:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
export PARAVANE_API_KEY="pvn_live_..."
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
On Windows PowerShell:
|
|
193
|
+
|
|
194
|
+
```powershell
|
|
195
|
+
$env:PARAVANE_API_KEY = "pvn_live_..."
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Call smtpRS:
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from paravane import ParavaneClient
|
|
202
|
+
|
|
203
|
+
client = ParavaneClient()
|
|
204
|
+
|
|
205
|
+
result = client.smtprs.analyze("alice@example.com")
|
|
206
|
+
|
|
207
|
+
print(result.decision)
|
|
208
|
+
print(result.overall_risk)
|
|
209
|
+
print(result.credits_charged)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
The SDK method is named `analyze(...)` for Python readability. The HTTP API endpoint remains:
|
|
213
|
+
|
|
214
|
+
```text
|
|
215
|
+
POST /v1/analyse
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Usage
|
|
219
|
+
|
|
220
|
+
Create a client with an API key:
|
|
221
|
+
|
|
222
|
+
```python
|
|
223
|
+
from paravane import ParavaneClient
|
|
224
|
+
|
|
225
|
+
client = ParavaneClient(api_key="pvn_live_...")
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Or use `PARAVANE_API_KEY`:
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from paravane import ParavaneClient
|
|
232
|
+
|
|
233
|
+
client = ParavaneClient()
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Run an email risk analysis:
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
result = client.smtprs.analyze("person@example.com")
|
|
240
|
+
|
|
241
|
+
if result.decision == "allow":
|
|
242
|
+
print("Continue")
|
|
243
|
+
else:
|
|
244
|
+
print("Review or block")
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Inspect the normalized response:
|
|
248
|
+
|
|
249
|
+
```python
|
|
250
|
+
print(result.email)
|
|
251
|
+
print(result.decision)
|
|
252
|
+
print(result.overall_risk)
|
|
253
|
+
print(result.analysis_profile)
|
|
254
|
+
print(result.credit_cost)
|
|
255
|
+
print(result.usage)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Access the original API response:
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
raw = result.to_dict()
|
|
262
|
+
print(raw)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## smtpRS
|
|
266
|
+
|
|
267
|
+
smtpRS is available through:
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
client.smtprs
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Analysis profiles
|
|
274
|
+
|
|
275
|
+
smtpRS provides named profiles so applications can choose the appropriate balance of
|
|
276
|
+
coverage, latency, and credit usage without configuring individual checks.
|
|
277
|
+
|
|
278
|
+
| Profile | Credits | Availability | Intended use |
|
|
279
|
+
| --- | ---: | --- | --- |
|
|
280
|
+
| `quick` | 1 | All plans | Lightweight screening; this is the default. |
|
|
281
|
+
| `standard` | 3 | Basic and above | Broader passive analysis. |
|
|
282
|
+
| `adaptive` | 5 | Pro and above | Adds checks when the initial result needs more context. |
|
|
283
|
+
| `deep` | 5 | Pro and above | Comprehensive analysis without catch-all probing. |
|
|
284
|
+
| `catch_all` | 20 | Enterprise or entitled accounts | Deep analysis with catch-all probing. |
|
|
285
|
+
|
|
286
|
+
Use the default quick profile:
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
result = client.smtprs.analyze("person@example.com")
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Or request another profile available to your account:
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
result = client.smtprs.analyze(
|
|
296
|
+
"person@example.com",
|
|
297
|
+
profile="standard",
|
|
298
|
+
)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
Profile availability is enforced by the API. Requesting a profile that is not included
|
|
302
|
+
with the current plan raises `PermissionDeniedError`.
|
|
303
|
+
|
|
304
|
+
### Legacy mode flags
|
|
305
|
+
|
|
306
|
+
The older `disposable_only`, `strict_disposable`, `guess`, and `run_catch_all` arguments
|
|
307
|
+
remain available for compatibility. New integrations should use `profile` instead. Do not
|
|
308
|
+
combine `profile` with an enabled legacy mode flag.
|
|
309
|
+
|
|
310
|
+
For example, replace:
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
result = client.smtprs.analyze(
|
|
314
|
+
"person@example.com",
|
|
315
|
+
strict_disposable=True,
|
|
316
|
+
)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
with:
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
result = client.smtprs.analyze(
|
|
323
|
+
"person@example.com",
|
|
324
|
+
profile="deep",
|
|
325
|
+
)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Low-latency posture
|
|
329
|
+
|
|
330
|
+
Use the `fast` flag when your workflow favors lower latency:
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
result = client.smtprs.analyze(
|
|
334
|
+
"person@example.com",
|
|
335
|
+
fast=True,
|
|
336
|
+
)
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Company Validity Beta
|
|
340
|
+
|
|
341
|
+
Paid smtpRS callers can explicitly request the optional Company Validity Beta:
|
|
342
|
+
|
|
343
|
+
```python
|
|
344
|
+
result = client.smtprs.analyze(
|
|
345
|
+
"person@example.com",
|
|
346
|
+
profile="standard",
|
|
347
|
+
company_validity_beta=True,
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
beta = result.company_validity_beta
|
|
351
|
+
if beta is not None:
|
|
352
|
+
print(beta.status) # "beta"
|
|
353
|
+
print(beta.requested) # True
|
|
354
|
+
print(beta.enabled) # True when the feature was active for this request
|
|
355
|
+
print(beta.notes)
|
|
356
|
+
|
|
357
|
+
signal = result.domain_signal
|
|
358
|
+
if signal is not None:
|
|
359
|
+
print(signal.domain_status)
|
|
360
|
+
print(signal.mail_status)
|
|
361
|
+
print(signal.company_valid)
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
The option is omitted by default, so existing calls retain their current
|
|
365
|
+
behavior. It is available only to paid smtpRS plans and provides additive
|
|
366
|
+
company-domain context rather than an allowlist. It currently adds no credits
|
|
367
|
+
beyond the selected profile. Free-plan requests that explicitly enable it
|
|
368
|
+
receive `PermissionDeniedError`. Because the response contract is beta,
|
|
369
|
+
applications can use `result.to_dict()` to retain access to newly added fields.
|
|
370
|
+
|
|
371
|
+
## Configuration
|
|
372
|
+
|
|
373
|
+
```python
|
|
374
|
+
from paravane import ParavaneClient
|
|
375
|
+
|
|
376
|
+
client = ParavaneClient(
|
|
377
|
+
api_key="pvn_live_...",
|
|
378
|
+
base_url="https://api.paravane.io",
|
|
379
|
+
timeout=20.0,
|
|
380
|
+
max_network_retries=1,
|
|
381
|
+
)
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
Environment variables:
|
|
385
|
+
|
|
386
|
+
| Name | Purpose | Default |
|
|
387
|
+
| --- | --- | --- |
|
|
388
|
+
| `PARAVANE_API_KEY` | API key used for requests. | None |
|
|
389
|
+
| `PARAVANE_BASE_URL` | API base URL. | `https://api.paravane.io` |
|
|
390
|
+
|
|
391
|
+
## Per-request options
|
|
392
|
+
|
|
393
|
+
You can set a timeout for one request:
|
|
394
|
+
|
|
395
|
+
```python
|
|
396
|
+
result = client.smtprs.analyze(
|
|
397
|
+
"person@example.com",
|
|
398
|
+
timeout=5.0,
|
|
399
|
+
)
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
You can pass an idempotency key:
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
result = client.smtprs.analyze(
|
|
406
|
+
"person@example.com",
|
|
407
|
+
idempotency_key="signup-check-123",
|
|
408
|
+
)
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
You can pass future or preview query parameters without waiting for a new SDK release:
|
|
412
|
+
|
|
413
|
+
```python
|
|
414
|
+
result = client.smtprs.analyze(
|
|
415
|
+
"person@example.com",
|
|
416
|
+
extra_params={"preview_flag": "enabled"},
|
|
417
|
+
)
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Custom HTTP sessions
|
|
421
|
+
|
|
422
|
+
The SDK uses `requests` by default. If your environment needs custom connection pooling, proxies, certificates, or adapters, pass a configured `requests.Session`:
|
|
423
|
+
|
|
424
|
+
```python
|
|
425
|
+
import requests
|
|
426
|
+
from paravane import ParavaneClient
|
|
427
|
+
|
|
428
|
+
session = requests.Session()
|
|
429
|
+
session.proxies.update({
|
|
430
|
+
"https": "https://proxy.example.com:8443",
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
client = ParavaneClient(
|
|
434
|
+
api_key="pvn_live_...",
|
|
435
|
+
session=session,
|
|
436
|
+
)
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## Retries
|
|
440
|
+
|
|
441
|
+
By default, the SDK does not retry network calls:
|
|
442
|
+
|
|
443
|
+
```python
|
|
444
|
+
client = ParavaneClient(max_network_retries=0)
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
Enable limited retries for transient network failures, `429` rate limits, and `5xx` API responses:
|
|
448
|
+
|
|
449
|
+
```python
|
|
450
|
+
client = ParavaneClient(max_network_retries=2)
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
When enabling retries for write-like workflows, pass an idempotency key from your application so repeated attempts can be treated safely by the API:
|
|
454
|
+
|
|
455
|
+
```python
|
|
456
|
+
result = client.smtprs.analyze(
|
|
457
|
+
"person@example.com",
|
|
458
|
+
idempotency_key="customer-signup-456",
|
|
459
|
+
)
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## Idempotency
|
|
463
|
+
|
|
464
|
+
The SDK accepts an `idempotency_key` and sends it as:
|
|
465
|
+
|
|
466
|
+
```text
|
|
467
|
+
Idempotency-Key: your-key
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Use stable keys for requests that your application may retry after timeouts or transient failures.
|
|
471
|
+
|
|
472
|
+
## Errors
|
|
473
|
+
|
|
474
|
+
Unsuccessful requests raise structured exceptions from `paravane.errors`.
|
|
475
|
+
|
|
476
|
+
```python
|
|
477
|
+
from paravane import (
|
|
478
|
+
AuthenticationError,
|
|
479
|
+
ParavaneClient,
|
|
480
|
+
QuotaExceededError,
|
|
481
|
+
RateLimitError,
|
|
482
|
+
ValidationError,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
client = ParavaneClient()
|
|
486
|
+
|
|
487
|
+
try:
|
|
488
|
+
result = client.smtprs.analyze("person@example.com")
|
|
489
|
+
except AuthenticationError:
|
|
490
|
+
print("Check your API key.")
|
|
491
|
+
except QuotaExceededError:
|
|
492
|
+
print("The workspace has exhausted its available credits.")
|
|
493
|
+
except RateLimitError:
|
|
494
|
+
print("Slow down and retry later.")
|
|
495
|
+
except ValidationError as exc:
|
|
496
|
+
print("Request was invalid:", exc)
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
Exception classes:
|
|
500
|
+
|
|
501
|
+
| Class | Typical cause |
|
|
502
|
+
| --- | --- |
|
|
503
|
+
| `ConfigurationError` | Missing API key or invalid client setup. |
|
|
504
|
+
| `APIConnectionError` | Network failure, timeout, DNS failure, or connection error. |
|
|
505
|
+
| `APIError` | Generic non-success API response. |
|
|
506
|
+
| `AuthenticationError` | Missing, invalid, or revoked API key. |
|
|
507
|
+
| `PermissionDeniedError` | API key lacks access to the requested resource. |
|
|
508
|
+
| `ValidationError` | Invalid request payload or parameters. |
|
|
509
|
+
| `QuotaExceededError` | Plan or credit quota has been exhausted. |
|
|
510
|
+
| `RateLimitError` | Too many requests. |
|
|
511
|
+
|
|
512
|
+
API errors include useful details when available:
|
|
513
|
+
|
|
514
|
+
```python
|
|
515
|
+
try:
|
|
516
|
+
client.smtprs.analyze("not-an-email")
|
|
517
|
+
except ValidationError as exc:
|
|
518
|
+
print(exc.status_code)
|
|
519
|
+
print(exc.code)
|
|
520
|
+
print(exc.request_id)
|
|
521
|
+
print(exc.response)
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
## Responses and raw data
|
|
525
|
+
|
|
526
|
+
The SDK returns `SmtpRsAnalysis` for smtpRS analysis requests.
|
|
527
|
+
|
|
528
|
+
```python
|
|
529
|
+
result = client.smtprs.analyze("person@example.com")
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
Common fields:
|
|
533
|
+
|
|
534
|
+
| Field | Description |
|
|
535
|
+
| --- | --- |
|
|
536
|
+
| `email` | Email address represented by the response, when returned by the API. |
|
|
537
|
+
| `decision` | Customer-facing decision or recommendation, when returned by the API. |
|
|
538
|
+
| `overall_risk` | Overall risk score, when returned by the API. |
|
|
539
|
+
| `tier` | Workspace/API-key tier reflected by the response. |
|
|
540
|
+
| `analysis_profile` | Analysis path used by the API. |
|
|
541
|
+
| `response_profile` | Response shape, such as summary/full. |
|
|
542
|
+
| `credit_cost` | Planned credit cost for the selected profile. |
|
|
543
|
+
| `credits_charged` | Credits recorded for the request, when returned. |
|
|
544
|
+
| `company_validity_beta` | Typed beta status, opt-in state, availability, credit cost, and API notes. |
|
|
545
|
+
| `domain_signal` | Typed domain, mail, and company-context facts when returned. |
|
|
546
|
+
| `reasons` | Human-readable reason strings, when returned. |
|
|
547
|
+
| `usage` | Usage/quota snapshot, when returned. |
|
|
548
|
+
| `raw` | Original API payload. |
|
|
549
|
+
|
|
550
|
+
To avoid losing fields added by the API before the SDK is updated, the full payload is always preserved:
|
|
551
|
+
|
|
552
|
+
```python
|
|
553
|
+
raw = result.to_dict()
|
|
554
|
+
print(raw["usage"])
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
## Types
|
|
558
|
+
|
|
559
|
+
This package includes inline type hints and ships a `py.typed` marker.
|
|
560
|
+
|
|
561
|
+
The response helper is a dataclass:
|
|
562
|
+
|
|
563
|
+
```python
|
|
564
|
+
from paravane import SmtpRsAnalysis
|
|
565
|
+
|
|
566
|
+
def handle_result(result: SmtpRsAnalysis) -> None:
|
|
567
|
+
print(result.decision)
|
|
568
|
+
signal = result.domain_signal
|
|
569
|
+
if signal is not None:
|
|
570
|
+
print(signal.mail_capable)
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
Type hints are intended to describe stable SDK behavior. The raw API response may include additional fields that are not represented as first-class dataclass attributes yet.
|
|
574
|
+
|
|
575
|
+
## Logging
|
|
576
|
+
|
|
577
|
+
The SDK does not install or configure logging handlers. Applications should configure logging at the application boundary.
|
|
578
|
+
|
|
579
|
+
For now, request failures are surfaced through exceptions. If you need detailed HTTP logging during development, configure your own `requests.Session` or enable logging in your HTTP stack.
|
|
580
|
+
|
|
581
|
+
## Examples
|
|
582
|
+
|
|
583
|
+
This repository includes small examples:
|
|
584
|
+
|
|
585
|
+
```text
|
|
586
|
+
examples/basic_analyze.py
|
|
587
|
+
examples/strict_analyze.py
|
|
588
|
+
examples/batch_csv.py
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
Run one with:
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
PARAVANE_API_KEY="pvn_live_..." python examples/basic_analyze.py
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Windows PowerShell:
|
|
598
|
+
|
|
599
|
+
```powershell
|
|
600
|
+
$env:PARAVANE_API_KEY = "pvn_live_..."
|
|
601
|
+
python examples/basic_analyze.py
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
## Development
|
|
605
|
+
|
|
606
|
+
Create an environment:
|
|
607
|
+
|
|
608
|
+
```bash
|
|
609
|
+
python -m venv .venv
|
|
610
|
+
source .venv/bin/activate
|
|
611
|
+
python -m pip install -e ".[dev]"
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
Windows PowerShell:
|
|
615
|
+
|
|
616
|
+
```powershell
|
|
617
|
+
python -m venv .venv
|
|
618
|
+
.\.venv\Scripts\Activate.ps1
|
|
619
|
+
python -m pip install -e ".[dev]"
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
Run tests:
|
|
623
|
+
|
|
624
|
+
```bash
|
|
625
|
+
pytest
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
Run lint:
|
|
629
|
+
|
|
630
|
+
```bash
|
|
631
|
+
ruff check .
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
Build the package:
|
|
635
|
+
|
|
636
|
+
```bash
|
|
637
|
+
python -m build
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
Recommended pre-commit check:
|
|
641
|
+
|
|
642
|
+
```bash
|
|
643
|
+
pytest && ruff check . && python -m build
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
## Repository layout
|
|
647
|
+
|
|
648
|
+
```text
|
|
649
|
+
paravane-python/
|
|
650
|
+
.github/workflows/ GitHub Actions checks
|
|
651
|
+
examples/ Small runnable examples
|
|
652
|
+
src/paravane/ SDK package source
|
|
653
|
+
tests/ Unit tests
|
|
654
|
+
pyproject.toml Packaging metadata and tool config
|
|
655
|
+
RELEASING.md Maintainer release process
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
## Versioning
|
|
659
|
+
|
|
660
|
+
Current package version:
|
|
661
|
+
|
|
662
|
+
```text
|
|
663
|
+
0.2.0
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
The SDK is in an early release phase. Until `1.0.0`, minor versions may refine naming, response helpers, or packaging behavior as the public API settles. Runtime-breaking changes should still be called out clearly in `CHANGELOG.md`.
|
|
667
|
+
|
|
668
|
+
## Security
|
|
669
|
+
|
|
670
|
+
Do not put API keys in source code, client-side apps, mobile apps, screenshots, or public repositories.
|
|
671
|
+
|
|
672
|
+
Recommended handling:
|
|
673
|
+
|
|
674
|
+
- load API keys from environment variables or a secret manager
|
|
675
|
+
- rotate keys if they are exposed
|
|
676
|
+
- create separate keys for development, staging, and production
|
|
677
|
+
- revoke keys that are no longer needed
|
|
678
|
+
|
|
679
|
+
Report suspected vulnerabilities privately:
|
|
680
|
+
|
|
681
|
+
```text
|
|
682
|
+
security@paravane.io
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
## Support
|
|
686
|
+
|
|
687
|
+
For product or account support:
|
|
688
|
+
|
|
689
|
+
```text
|
|
690
|
+
contact@paravane.io
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
For security reports:
|
|
694
|
+
|
|
695
|
+
```text
|
|
696
|
+
security@paravane.io
|
|
697
|
+
```
|