billionverify 1.0.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.
@@ -0,0 +1,26 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - uses: actions/checkout@v4
12
+
13
+ - uses: actions/setup-python@v5
14
+ with:
15
+ python-version: "3.12"
16
+
17
+ - name: Install build tools
18
+ run: pip install build
19
+
20
+ - name: Build package
21
+ run: python -m build
22
+
23
+ - name: Publish to PyPI
24
+ uses: pypa/gh-action-pypi-publish@release/v1
25
+ with:
26
+ password: ${{ secrets.PYPI_TOKEN }}
@@ -0,0 +1,39 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Virtual environments
7
+ .venv/
8
+ venv/
9
+ ENV/
10
+
11
+ # Distribution / packaging
12
+ dist/
13
+ build/
14
+ *.egg-info/
15
+ *.egg
16
+
17
+ # Test / coverage
18
+ .pytest_cache/
19
+ .coverage
20
+ htmlcov/
21
+ .tox/
22
+ .mypy_cache/
23
+
24
+ # IDE
25
+ .idea/
26
+ .vscode/
27
+ *.swp
28
+ *.swo
29
+
30
+ # OS
31
+ .DS_Store
32
+ Thumbs.db
33
+
34
+ # Environment
35
+ .env
36
+ .env.local
37
+
38
+ # Logs
39
+ *.log
@@ -0,0 +1,310 @@
1
+ Metadata-Version: 2.4
2
+ Name: billionverify
3
+ Version: 1.0.0
4
+ Summary: Official BillionVerify Python SDK for email verification
5
+ Project-URL: Homepage, https://billionverify.com
6
+ Project-URL: Documentation, https://billionverify.com/docs
7
+ Project-URL: Repository, https://github.com/BillionVerify/billionverify-python
8
+ Author-email: BillionVerify <support@billionverify.com>
9
+ License-Expression: MIT
10
+ Keywords: billionverify,email,email-verification,validation,verification
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.8
23
+ Requires-Dist: httpx>=0.24.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: mypy>=1.0; extra == 'dev'
26
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
27
+ Requires-Dist: pytest>=7.0; extra == 'dev'
28
+ Requires-Dist: ruff>=0.1; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # billionverify
32
+
33
+ Official BillionVerify Python SDK for email verification.
34
+
35
+ **Documentation:** https://billionverify.com/docs
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install billionverify
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from billionverify import BillionVerify
47
+
48
+ client = BillionVerify(api_key="your-api-key")
49
+
50
+ # Verify a single email
51
+ result = client.verify("user@example.com")
52
+ print(result.status) # 'valid', 'invalid', 'unknown', 'risky', 'disposable', 'catchall', 'role'
53
+ print(result.is_deliverable) # True or False
54
+ ```
55
+
56
+ ## Configuration
57
+
58
+ ```python
59
+ client = BillionVerify(
60
+ api_key="your-api-key", # Required
61
+ base_url="https://api.billionverify.com/v1", # Optional
62
+ timeout=30.0, # Optional: Request timeout in seconds (default: 30)
63
+ retries=3, # Optional: Number of retries (default: 3)
64
+ )
65
+ ```
66
+
67
+ ## Single Email Verification
68
+
69
+ Uses the `/verify/single` endpoint:
70
+
71
+ ```python
72
+ result = client.verify(
73
+ email="user@example.com",
74
+ check_smtp=True, # Optional: Perform SMTP verification (default: True)
75
+ )
76
+
77
+ # Flat response structure
78
+ print(result.email) # 'user@example.com'
79
+ print(result.status) # 'valid', 'invalid', 'unknown', 'risky', 'disposable', 'catchall', 'role'
80
+ print(result.score) # 0.95
81
+ print(result.is_deliverable) # True
82
+ print(result.is_disposable) # False
83
+ print(result.is_catchall) # False
84
+ print(result.is_role) # False
85
+ print(result.is_free) # True
86
+ print(result.domain) # 'example.com'
87
+ print(result.reason) # 'Valid email address'
88
+ print(result.smtp_check) # True (whether SMTP was performed)
89
+ print(result.credits_used) # 1
90
+ ```
91
+
92
+ ## Bulk Email Verification (Synchronous)
93
+
94
+ Verify up to 50 emails synchronously using `verify_bulk()`:
95
+
96
+ ```python
97
+ # Synchronous bulk verification (max 50 emails)
98
+ response = client.verify_bulk(
99
+ emails=["user1@example.com", "user2@example.com", "user3@example.com"],
100
+ check_smtp=True, # Optional
101
+ )
102
+
103
+ # Returns BulkVerifyResponse directly
104
+ print(f"Total: {response.total}")
105
+ print(f"Credits used: {response.credits_used}")
106
+
107
+ for result in response.results:
108
+ print(f"{result.email}: {result.status}")
109
+ print(f" Deliverable: {result.is_deliverable}")
110
+ print(f" Disposable: {result.is_disposable}")
111
+ print(f" Catchall: {result.is_catchall}")
112
+ print(f" Role: {result.is_role}")
113
+ ```
114
+
115
+ ## File Upload (Async Verification)
116
+
117
+ For large lists, use `upload_file()` for asynchronous file verification:
118
+
119
+ ```python
120
+ # Upload a file for async verification
121
+ job = client.upload_file(
122
+ file_path="emails.csv",
123
+ check_smtp=True,
124
+ email_column="email", # Column name for CSV files
125
+ preserve_original=True, # Keep original columns in results
126
+ )
127
+
128
+ print(f"Job ID: {job.job_id}")
129
+ print(f"Status: {job.status}")
130
+
131
+ # Get job status (with optional long-polling)
132
+ status = client.get_file_job_status(
133
+ job_id=job.job_id,
134
+ timeout=60, # Long-poll for up to 60 seconds (0-300)
135
+ )
136
+ print(f"Progress: {status.progress_percent}%")
137
+
138
+ # Wait for completion (polling)
139
+ completed = client.wait_for_file_job(
140
+ job_id=job.job_id,
141
+ poll_interval=5.0, # seconds
142
+ max_wait=600.0, # seconds
143
+ )
144
+
145
+ # Get results with filter options
146
+ results = client.get_file_job_results(
147
+ job_id=job.job_id,
148
+ limit=100,
149
+ offset=0,
150
+ valid=True, # Include valid emails
151
+ invalid=True, # Include invalid emails
152
+ unknown=True, # Include unknown emails
153
+ risky=True, # Include risky emails
154
+ disposable=True, # Include disposable emails
155
+ catchall=True, # Include catch-all emails
156
+ role=True, # Include role-based emails
157
+ )
158
+
159
+ for item in results.results:
160
+ print(f"{item.email}: {item.status}")
161
+ ```
162
+
163
+ ## Async Support
164
+
165
+ ```python
166
+ import asyncio
167
+ from billionverify import AsyncBillionVerify
168
+
169
+ async def main():
170
+ async with AsyncBillionVerify(api_key="your-api-key") as client:
171
+ # Single verification
172
+ result = await client.verify("user@example.com")
173
+ print(result.status)
174
+
175
+ # Bulk verification
176
+ response = await client.verify_bulk([
177
+ "user1@example.com",
178
+ "user2@example.com"
179
+ ])
180
+ for r in response.results:
181
+ print(f"{r.email}: {r.status}")
182
+
183
+ asyncio.run(main())
184
+ ```
185
+
186
+ ## Health Check
187
+
188
+ Check API health status (no authentication required):
189
+
190
+ ```python
191
+ health = client.health_check()
192
+ print(health.status) # 'ok'
193
+ print(health.version) # API version
194
+ ```
195
+
196
+ ## Credits
197
+
198
+ ```python
199
+ credits = client.get_credits()
200
+ print(credits.credits_balance) # Available credits
201
+ print(credits.credits_consumed) # Credits used
202
+ print(credits.credits_added) # Total credits added
203
+ print(credits.api_key_name) # API key name
204
+ ```
205
+
206
+ ## Webhooks
207
+
208
+ Webhooks support events: `file.completed`, `file.failed`
209
+
210
+ ```python
211
+ # Create a webhook
212
+ webhook = client.create_webhook(
213
+ url="https://your-app.com/webhooks/billionverify",
214
+ events=["file.completed", "file.failed"],
215
+ )
216
+ print(f"Webhook ID: {webhook.id}")
217
+ print(f"Secret: {webhook.secret}") # Save this for signature verification
218
+
219
+ # List webhooks
220
+ webhooks = client.list_webhooks()
221
+ for wh in webhooks:
222
+ print(f"{wh.id}: {wh.url}")
223
+
224
+ # Delete a webhook
225
+ client.delete_webhook(webhook.id)
226
+
227
+ # Verify webhook signature
228
+ from billionverify import BillionVerify
229
+
230
+ is_valid = BillionVerify.verify_webhook_signature(
231
+ payload=raw_body,
232
+ signature=signature_header,
233
+ secret="your-webhook-secret",
234
+ )
235
+ ```
236
+
237
+ ## Error Handling
238
+
239
+ ```python
240
+ from billionverify import (
241
+ BillionVerify,
242
+ AuthenticationError,
243
+ RateLimitError,
244
+ ValidationError,
245
+ InsufficientCreditsError,
246
+ NotFoundError,
247
+ TimeoutError,
248
+ )
249
+
250
+ try:
251
+ result = client.verify("user@example.com")
252
+ except AuthenticationError:
253
+ print("Invalid API key")
254
+ except RateLimitError as e:
255
+ print(f"Rate limited. Retry after {e.retry_after} seconds")
256
+ except ValidationError as e:
257
+ print(f"Invalid input: {e.message}")
258
+ except InsufficientCreditsError:
259
+ print("Not enough credits")
260
+ except NotFoundError:
261
+ print("Resource not found")
262
+ except TimeoutError:
263
+ print("Request timed out")
264
+ ```
265
+
266
+ ## Context Manager
267
+
268
+ ```python
269
+ with BillionVerify(api_key="your-api-key") as client:
270
+ result = client.verify("user@example.com")
271
+ print(result.status)
272
+ # Connection is automatically closed
273
+ ```
274
+
275
+ ## Type Hints
276
+
277
+ This SDK includes full type annotations for IDE support and type checking.
278
+
279
+ ```python
280
+ from billionverify import (
281
+ VerificationResult,
282
+ BulkVerifyResponse,
283
+ FileJobResponse,
284
+ CreditsResponse,
285
+ VerificationStatus,
286
+ )
287
+
288
+ def process_result(result: VerificationResult) -> None:
289
+ if result.status == "valid":
290
+ print(f"Email {result.email} is valid")
291
+
292
+ if result.is_deliverable and not result.is_disposable:
293
+ print("Safe to send to this email")
294
+ ```
295
+
296
+ ## Status Values
297
+
298
+ The verification status can be one of:
299
+
300
+ - `valid` - Email is valid and deliverable
301
+ - `invalid` - Email is invalid or does not exist
302
+ - `unknown` - Could not determine status
303
+ - `risky` - Email exists but may have delivery issues
304
+ - `disposable` - Temporary/disposable email address
305
+ - `catchall` - Domain accepts all emails (catch-all)
306
+ - `role` - Role-based email (e.g., info@, support@)
307
+
308
+ ## License
309
+
310
+ MIT
@@ -0,0 +1,280 @@
1
+ # billionverify
2
+
3
+ Official BillionVerify Python SDK for email verification.
4
+
5
+ **Documentation:** https://billionverify.com/docs
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install billionverify
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```python
16
+ from billionverify import BillionVerify
17
+
18
+ client = BillionVerify(api_key="your-api-key")
19
+
20
+ # Verify a single email
21
+ result = client.verify("user@example.com")
22
+ print(result.status) # 'valid', 'invalid', 'unknown', 'risky', 'disposable', 'catchall', 'role'
23
+ print(result.is_deliverable) # True or False
24
+ ```
25
+
26
+ ## Configuration
27
+
28
+ ```python
29
+ client = BillionVerify(
30
+ api_key="your-api-key", # Required
31
+ base_url="https://api.billionverify.com/v1", # Optional
32
+ timeout=30.0, # Optional: Request timeout in seconds (default: 30)
33
+ retries=3, # Optional: Number of retries (default: 3)
34
+ )
35
+ ```
36
+
37
+ ## Single Email Verification
38
+
39
+ Uses the `/verify/single` endpoint:
40
+
41
+ ```python
42
+ result = client.verify(
43
+ email="user@example.com",
44
+ check_smtp=True, # Optional: Perform SMTP verification (default: True)
45
+ )
46
+
47
+ # Flat response structure
48
+ print(result.email) # 'user@example.com'
49
+ print(result.status) # 'valid', 'invalid', 'unknown', 'risky', 'disposable', 'catchall', 'role'
50
+ print(result.score) # 0.95
51
+ print(result.is_deliverable) # True
52
+ print(result.is_disposable) # False
53
+ print(result.is_catchall) # False
54
+ print(result.is_role) # False
55
+ print(result.is_free) # True
56
+ print(result.domain) # 'example.com'
57
+ print(result.reason) # 'Valid email address'
58
+ print(result.smtp_check) # True (whether SMTP was performed)
59
+ print(result.credits_used) # 1
60
+ ```
61
+
62
+ ## Bulk Email Verification (Synchronous)
63
+
64
+ Verify up to 50 emails synchronously using `verify_bulk()`:
65
+
66
+ ```python
67
+ # Synchronous bulk verification (max 50 emails)
68
+ response = client.verify_bulk(
69
+ emails=["user1@example.com", "user2@example.com", "user3@example.com"],
70
+ check_smtp=True, # Optional
71
+ )
72
+
73
+ # Returns BulkVerifyResponse directly
74
+ print(f"Total: {response.total}")
75
+ print(f"Credits used: {response.credits_used}")
76
+
77
+ for result in response.results:
78
+ print(f"{result.email}: {result.status}")
79
+ print(f" Deliverable: {result.is_deliverable}")
80
+ print(f" Disposable: {result.is_disposable}")
81
+ print(f" Catchall: {result.is_catchall}")
82
+ print(f" Role: {result.is_role}")
83
+ ```
84
+
85
+ ## File Upload (Async Verification)
86
+
87
+ For large lists, use `upload_file()` for asynchronous file verification:
88
+
89
+ ```python
90
+ # Upload a file for async verification
91
+ job = client.upload_file(
92
+ file_path="emails.csv",
93
+ check_smtp=True,
94
+ email_column="email", # Column name for CSV files
95
+ preserve_original=True, # Keep original columns in results
96
+ )
97
+
98
+ print(f"Job ID: {job.job_id}")
99
+ print(f"Status: {job.status}")
100
+
101
+ # Get job status (with optional long-polling)
102
+ status = client.get_file_job_status(
103
+ job_id=job.job_id,
104
+ timeout=60, # Long-poll for up to 60 seconds (0-300)
105
+ )
106
+ print(f"Progress: {status.progress_percent}%")
107
+
108
+ # Wait for completion (polling)
109
+ completed = client.wait_for_file_job(
110
+ job_id=job.job_id,
111
+ poll_interval=5.0, # seconds
112
+ max_wait=600.0, # seconds
113
+ )
114
+
115
+ # Get results with filter options
116
+ results = client.get_file_job_results(
117
+ job_id=job.job_id,
118
+ limit=100,
119
+ offset=0,
120
+ valid=True, # Include valid emails
121
+ invalid=True, # Include invalid emails
122
+ unknown=True, # Include unknown emails
123
+ risky=True, # Include risky emails
124
+ disposable=True, # Include disposable emails
125
+ catchall=True, # Include catch-all emails
126
+ role=True, # Include role-based emails
127
+ )
128
+
129
+ for item in results.results:
130
+ print(f"{item.email}: {item.status}")
131
+ ```
132
+
133
+ ## Async Support
134
+
135
+ ```python
136
+ import asyncio
137
+ from billionverify import AsyncBillionVerify
138
+
139
+ async def main():
140
+ async with AsyncBillionVerify(api_key="your-api-key") as client:
141
+ # Single verification
142
+ result = await client.verify("user@example.com")
143
+ print(result.status)
144
+
145
+ # Bulk verification
146
+ response = await client.verify_bulk([
147
+ "user1@example.com",
148
+ "user2@example.com"
149
+ ])
150
+ for r in response.results:
151
+ print(f"{r.email}: {r.status}")
152
+
153
+ asyncio.run(main())
154
+ ```
155
+
156
+ ## Health Check
157
+
158
+ Check API health status (no authentication required):
159
+
160
+ ```python
161
+ health = client.health_check()
162
+ print(health.status) # 'ok'
163
+ print(health.version) # API version
164
+ ```
165
+
166
+ ## Credits
167
+
168
+ ```python
169
+ credits = client.get_credits()
170
+ print(credits.credits_balance) # Available credits
171
+ print(credits.credits_consumed) # Credits used
172
+ print(credits.credits_added) # Total credits added
173
+ print(credits.api_key_name) # API key name
174
+ ```
175
+
176
+ ## Webhooks
177
+
178
+ Webhooks support events: `file.completed`, `file.failed`
179
+
180
+ ```python
181
+ # Create a webhook
182
+ webhook = client.create_webhook(
183
+ url="https://your-app.com/webhooks/billionverify",
184
+ events=["file.completed", "file.failed"],
185
+ )
186
+ print(f"Webhook ID: {webhook.id}")
187
+ print(f"Secret: {webhook.secret}") # Save this for signature verification
188
+
189
+ # List webhooks
190
+ webhooks = client.list_webhooks()
191
+ for wh in webhooks:
192
+ print(f"{wh.id}: {wh.url}")
193
+
194
+ # Delete a webhook
195
+ client.delete_webhook(webhook.id)
196
+
197
+ # Verify webhook signature
198
+ from billionverify import BillionVerify
199
+
200
+ is_valid = BillionVerify.verify_webhook_signature(
201
+ payload=raw_body,
202
+ signature=signature_header,
203
+ secret="your-webhook-secret",
204
+ )
205
+ ```
206
+
207
+ ## Error Handling
208
+
209
+ ```python
210
+ from billionverify import (
211
+ BillionVerify,
212
+ AuthenticationError,
213
+ RateLimitError,
214
+ ValidationError,
215
+ InsufficientCreditsError,
216
+ NotFoundError,
217
+ TimeoutError,
218
+ )
219
+
220
+ try:
221
+ result = client.verify("user@example.com")
222
+ except AuthenticationError:
223
+ print("Invalid API key")
224
+ except RateLimitError as e:
225
+ print(f"Rate limited. Retry after {e.retry_after} seconds")
226
+ except ValidationError as e:
227
+ print(f"Invalid input: {e.message}")
228
+ except InsufficientCreditsError:
229
+ print("Not enough credits")
230
+ except NotFoundError:
231
+ print("Resource not found")
232
+ except TimeoutError:
233
+ print("Request timed out")
234
+ ```
235
+
236
+ ## Context Manager
237
+
238
+ ```python
239
+ with BillionVerify(api_key="your-api-key") as client:
240
+ result = client.verify("user@example.com")
241
+ print(result.status)
242
+ # Connection is automatically closed
243
+ ```
244
+
245
+ ## Type Hints
246
+
247
+ This SDK includes full type annotations for IDE support and type checking.
248
+
249
+ ```python
250
+ from billionverify import (
251
+ VerificationResult,
252
+ BulkVerifyResponse,
253
+ FileJobResponse,
254
+ CreditsResponse,
255
+ VerificationStatus,
256
+ )
257
+
258
+ def process_result(result: VerificationResult) -> None:
259
+ if result.status == "valid":
260
+ print(f"Email {result.email} is valid")
261
+
262
+ if result.is_deliverable and not result.is_disposable:
263
+ print("Safe to send to this email")
264
+ ```
265
+
266
+ ## Status Values
267
+
268
+ The verification status can be one of:
269
+
270
+ - `valid` - Email is valid and deliverable
271
+ - `invalid` - Email is invalid or does not exist
272
+ - `unknown` - Could not determine status
273
+ - `risky` - Email exists but may have delivery issues
274
+ - `disposable` - Temporary/disposable email address
275
+ - `catchall` - Domain accepts all emails (catch-all)
276
+ - `role` - Role-based email (e.g., info@, support@)
277
+
278
+ ## License
279
+
280
+ MIT
@@ -0,0 +1,57 @@
1
+ """BillionVerify Python SDK for email verification."""
2
+
3
+ from .client import AsyncBillionVerify, BillionVerify
4
+ from .exceptions import (
5
+ AuthenticationError,
6
+ BillionVerifyError,
7
+ InsufficientCreditsError,
8
+ NotFoundError,
9
+ RateLimitError,
10
+ TimeoutError,
11
+ ValidationError,
12
+ )
13
+ from .types import (
14
+ BulkVerificationResult,
15
+ BulkVerifyResponse,
16
+ CreditsResponse,
17
+ DomainReputation,
18
+ FileJobResponse,
19
+ FileResultItem,
20
+ FileResultsResponse,
21
+ HealthCheckResponse,
22
+ JobStatus,
23
+ VerificationResult,
24
+ VerificationStatus,
25
+ Webhook,
26
+ WebhookEvent,
27
+ )
28
+
29
+ __version__ = "1.0.0"
30
+
31
+ __all__ = [
32
+ # Clients
33
+ "BillionVerify",
34
+ "AsyncBillionVerify",
35
+ # Types
36
+ "VerificationResult",
37
+ "VerificationStatus",
38
+ "BulkVerificationResult",
39
+ "BulkVerifyResponse",
40
+ "FileJobResponse",
41
+ "FileResultItem",
42
+ "FileResultsResponse",
43
+ "CreditsResponse",
44
+ "HealthCheckResponse",
45
+ "DomainReputation",
46
+ "JobStatus",
47
+ "Webhook",
48
+ "WebhookEvent",
49
+ # Exceptions
50
+ "BillionVerifyError",
51
+ "AuthenticationError",
52
+ "RateLimitError",
53
+ "ValidationError",
54
+ "InsufficientCreditsError",
55
+ "NotFoundError",
56
+ "TimeoutError",
57
+ ]