blitz-api-py 0.1.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.
- blitz_api_py-0.1.0/.gitignore +31 -0
- blitz_api_py-0.1.0/CHANGELOG.md +17 -0
- blitz_api_py-0.1.0/LICENSE +21 -0
- blitz_api_py-0.1.0/PKG-INFO +220 -0
- blitz_api_py-0.1.0/README.md +189 -0
- blitz_api_py-0.1.0/pyproject.toml +129 -0
- blitz_api_py-0.1.0/src/blitz_api/__init__.py +65 -0
- blitz_api_py-0.1.0/src/blitz_api/_base_client.py +191 -0
- blitz_api_py-0.1.0/src/blitz_api/_client.py +13 -0
- blitz_api_py-0.1.0/src/blitz_api/_client_async.py +143 -0
- blitz_api_py-0.1.0/src/blitz_api/_client_sync.py +145 -0
- blitz_api_py-0.1.0/src/blitz_api/_compat.py +26 -0
- blitz_api_py-0.1.0/src/blitz_api/_constants.py +36 -0
- blitz_api_py-0.1.0/src/blitz_api/_exceptions.py +113 -0
- blitz_api_py-0.1.0/src/blitz_api/_pagination_async.py +128 -0
- blitz_api_py-0.1.0/src/blitz_api/_pagination_base.py +52 -0
- blitz_api_py-0.1.0/src/blitz_api/_pagination_sync.py +130 -0
- blitz_api_py-0.1.0/src/blitz_api/_rate_limit.py +14 -0
- blitz_api_py-0.1.0/src/blitz_api/_rate_limit_async.py +67 -0
- blitz_api_py-0.1.0/src/blitz_api/_rate_limit_sync.py +69 -0
- blitz_api_py-0.1.0/src/blitz_api/_version.py +1 -0
- blitz_api_py-0.1.0/src/blitz_api/py.typed +0 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/__init__.py +27 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/_async/__init__.py +1 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/_async/account.py +27 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/_async/enrichment.py +116 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/_async/search.py +153 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/_async/utils.py +43 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/_sync/__init__.py +3 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/_sync/account.py +29 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/_sync/enrichment.py +118 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/_sync/search.py +155 -0
- blitz_api_py-0.1.0/src/blitz_api/resources/_sync/utils.py +45 -0
- blitz_api_py-0.1.0/src/blitz_api/types/__init__.py +108 -0
- blitz_api_py-0.1.0/src/blitz_api/types/_models.py +23 -0
- blitz_api_py-0.1.0/src/blitz_api/types/account.py +27 -0
- blitz_api_py-0.1.0/src/blitz_api/types/enrichment.py +76 -0
- blitz_api_py-0.1.0/src/blitz_api/types/enums.py +633 -0
- blitz_api_py-0.1.0/src/blitz_api/types/filters.py +130 -0
- blitz_api_py-0.1.0/src/blitz_api/types/search.py +36 -0
- blitz_api_py-0.1.0/src/blitz_api/types/shared.py +119 -0
- blitz_api_py-0.1.0/src/blitz_api/types/utils.py +35 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.egg
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Tooling caches
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.pyright/
|
|
18
|
+
.ruff_cache/
|
|
19
|
+
.pytest_cache/
|
|
20
|
+
.coverage
|
|
21
|
+
.coverage.*
|
|
22
|
+
htmlcov/
|
|
23
|
+
coverage.xml
|
|
24
|
+
|
|
25
|
+
# uv
|
|
26
|
+
# (uv.lock IS committed; only ignore caches)
|
|
27
|
+
|
|
28
|
+
# Editors / OS
|
|
29
|
+
.idea/
|
|
30
|
+
.vscode/
|
|
31
|
+
.DS_Store
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0](https://github.com/api-blitz/blitz-api-py/compare/v0.1.0...v0.1.0) (2026-06-02)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* typed Python SDK for the Blitz API with automated releases ([5641a51](https://github.com/api-blitz/blitz-api-py/commit/5641a51b405108029bc701988e08d6210a1255f6))
|
|
9
|
+
|
|
10
|
+
## Changelog
|
|
11
|
+
|
|
12
|
+
All notable changes to this project are documented in this file.
|
|
13
|
+
|
|
14
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
|
|
15
|
+
and the changelog is maintained automatically by
|
|
16
|
+
[release-please](https://github.com/googleapis/release-please) from
|
|
17
|
+
[Conventional Commits](https://www.conventionalcommits.org/).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Blitz API
|
|
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.
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: blitz-api-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Typed Python SDK for the Blitz API — B2B data, search, and enrichment.
|
|
5
|
+
Project-URL: Homepage, https://blitz-api.ai
|
|
6
|
+
Project-URL: Documentation, https://docs.blitz-api.ai
|
|
7
|
+
Project-URL: Repository, https://github.com/api-blitz/blitz-api-py
|
|
8
|
+
Project-URL: Issues, https://github.com/api-blitz/blitz-api-py/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/api-blitz/blitz-api-py/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: Blitz API <founders@blitz-api.ai>
|
|
11
|
+
License-Expression: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: b2b,blitz,blitz-api,enrichment,gtm,linkedin,sales,sdk
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: httpx<1,>=0.28
|
|
28
|
+
Requires-Dist: pydantic<3,>=2.13
|
|
29
|
+
Requires-Dist: typing-extensions>=4.10
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# blitz-api-py
|
|
33
|
+
|
|
34
|
+
The typed Python SDK for the [Blitz API](https://blitz-api.ai) — B2B data, search,
|
|
35
|
+
and enrichment.
|
|
36
|
+
|
|
37
|
+
- **Fully typed** — Pydantic v2 response models with attribute access and IDE
|
|
38
|
+
autocomplete, `TypedDict` request filters, and a shipped `py.typed` marker so
|
|
39
|
+
mypy/pyright see the types in your own code.
|
|
40
|
+
- **Sync & async** — `BlitzAPI` and `AsyncBlitzAPI` over `httpx`.
|
|
41
|
+
- **Resilient** — built-in client-side rate limiting, retries with backoff on
|
|
42
|
+
`429`/`5xx`, and a typed exception hierarchy.
|
|
43
|
+
- **Forward-compatible** — new fields the API adds never break deserialization.
|
|
44
|
+
|
|
45
|
+
> Create and manage API keys at [app.blitz-api.ai](https://app.blitz-api.ai).
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install blitz-api-py
|
|
51
|
+
# or: uv add blitz-api-py
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Requires Python 3.10+.
|
|
55
|
+
|
|
56
|
+
## Quickstart
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from blitz_api import BlitzAPI
|
|
60
|
+
from blitz_api.types import Industry, JobLevel
|
|
61
|
+
|
|
62
|
+
# api_key defaults to the BLITZ_API_KEY environment variable.
|
|
63
|
+
with BlitzAPI() as client:
|
|
64
|
+
# Health-check the key before a batch job.
|
|
65
|
+
info = client.account.key_info()
|
|
66
|
+
print(info.valid, info.remaining_credits, info.max_requests_per_seconds)
|
|
67
|
+
|
|
68
|
+
# LinkedIn profile URL -> verified work email.
|
|
69
|
+
email = client.enrichment.email(
|
|
70
|
+
person_linkedin_url="https://www.linkedin.com/in/example-person",
|
|
71
|
+
)
|
|
72
|
+
if email.found:
|
|
73
|
+
print(email.email)
|
|
74
|
+
|
|
75
|
+
# Search people with typed, autocompleted filters.
|
|
76
|
+
people = client.search.people(
|
|
77
|
+
company={"industry": {"include": [Industry.SOFTWARE_DEVELOPMENT]}},
|
|
78
|
+
people={"job_level": [JobLevel.VP]},
|
|
79
|
+
max_results=10,
|
|
80
|
+
)
|
|
81
|
+
for person in people.results:
|
|
82
|
+
print(person.full_name, person.headline)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Async
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
import asyncio
|
|
89
|
+
from blitz_api import AsyncBlitzAPI
|
|
90
|
+
|
|
91
|
+
async def main() -> None:
|
|
92
|
+
async with AsyncBlitzAPI() as client:
|
|
93
|
+
result = await client.enrichment.company(
|
|
94
|
+
company_linkedin_url="https://www.linkedin.com/company/openai",
|
|
95
|
+
)
|
|
96
|
+
print(result.company.name if result.company else None)
|
|
97
|
+
|
|
98
|
+
asyncio.run(main())
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Authentication
|
|
102
|
+
|
|
103
|
+
Pass the key explicitly or via the `BLITZ_API_KEY` environment variable:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
client = BlitzAPI(api_key="sk_...") # explicit
|
|
107
|
+
client = BlitzAPI() # reads BLITZ_API_KEY
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The key is sent in the `x-api-key` header. Never expose it in client-side code —
|
|
111
|
+
always call the API from your backend.
|
|
112
|
+
|
|
113
|
+
## Endpoints
|
|
114
|
+
|
|
115
|
+
All methods are grouped into four namespaces:
|
|
116
|
+
|
|
117
|
+
| Namespace | Methods |
|
|
118
|
+
| --- | --- |
|
|
119
|
+
| `client.account` | `key_info()` |
|
|
120
|
+
| `client.search` | `people()`, `companies()`, `employee_finder()`, `waterfall_icp()` |
|
|
121
|
+
| `client.enrichment` | `email()`, `phone()`, `email_to_person()`, `phone_to_person()`, `company()`, `domain_to_linkedin()`, `linkedin_to_domain()` |
|
|
122
|
+
| `client.utils` | `current_date()`, `company_employment_distribution()` |
|
|
123
|
+
|
|
124
|
+
Every method returns a typed Pydantic model (see `blitz_api.types`). Enum-backed
|
|
125
|
+
filter fields (e.g. `Industry`, `JobLevel`, `Continent`) accept either an enum
|
|
126
|
+
member or a raw string.
|
|
127
|
+
|
|
128
|
+
## Pagination
|
|
129
|
+
|
|
130
|
+
The search methods return an **auto-paginating page**: iterate it and the SDK fetches
|
|
131
|
+
each subsequent page for you. `search.people`/`search.companies` are cursor-based;
|
|
132
|
+
`search.employee_finder` is page-based — both behave identically here.
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
# Iterate every matching person across all pages — no cursor handling needed.
|
|
136
|
+
for person in client.search.people(people={"job_level": ["VP"]}):
|
|
137
|
+
print(person.full_name)
|
|
138
|
+
|
|
139
|
+
# Async works the same way.
|
|
140
|
+
async for person in await async_client.search.people(people={"job_level": ["VP"]}):
|
|
141
|
+
...
|
|
142
|
+
|
|
143
|
+
# Bound how much you pull.
|
|
144
|
+
for person in client.search.people(...).auto_paging_iter(max_items=200):
|
|
145
|
+
...
|
|
146
|
+
|
|
147
|
+
# Per-page control: inspect totals / cursors as you go.
|
|
148
|
+
for page in client.search.companies(company={...}).iter_pages(max_pages=5):
|
|
149
|
+
print(page.total_results, len(page.results), page.cursor)
|
|
150
|
+
|
|
151
|
+
# Or page manually.
|
|
152
|
+
page = client.search.people(people={...}, max_results=50)
|
|
153
|
+
print(page.results, page.cursor)
|
|
154
|
+
nxt = page.get_next_page() # None once exhausted
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The page types (`CursorPage`, `PageNumberPage`, and their `Async*` variants) are
|
|
158
|
+
exported from `blitz_api`.
|
|
159
|
+
|
|
160
|
+
## Configuration
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
client = BlitzAPI(
|
|
164
|
+
api_key=None, # falls back to BLITZ_API_KEY
|
|
165
|
+
base_url="https://api.blitz-api.ai",
|
|
166
|
+
timeout=30.0, # seconds, or an httpx.Timeout
|
|
167
|
+
max_retries=3, # retries on 429 / 5xx / network errors
|
|
168
|
+
rate_limit_rps=5.0, # client-side throttle; None to disable
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The client-side rate limiter is a sliding window — at most `rate_limit_rps` requests
|
|
173
|
+
in any rolling second — so a single client instance stays under the API's limit (5 req/s
|
|
174
|
+
by default; check your key's limit via
|
|
175
|
+
`client.account.key_info().max_requests_per_seconds`). Across multiple processes you may
|
|
176
|
+
still hit `429` — the retry path handles that.
|
|
177
|
+
|
|
178
|
+
Every method also accepts a per-call `timeout` (seconds or an `httpx.Timeout`) when one
|
|
179
|
+
endpoint needs longer than the client default:
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
client.search.people(people={"job_level": ["VP"]}, timeout=10.0)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Error handling
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from blitz_api import (
|
|
189
|
+
BlitzError, AuthenticationError, InsufficientCreditsError,
|
|
190
|
+
NotFoundError, RateLimitError, APIStatusError, APIConnectionError,
|
|
191
|
+
APITimeoutError, APIResponseValidationError,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
client.enrichment.email(person_linkedin_url="...")
|
|
196
|
+
except InsufficientCreditsError:
|
|
197
|
+
... # 402 — out of credits
|
|
198
|
+
except AuthenticationError:
|
|
199
|
+
... # 401 — bad key
|
|
200
|
+
except APIStatusError as err:
|
|
201
|
+
print(err.status_code, err.message, err.body)
|
|
202
|
+
except APIResponseValidationError:
|
|
203
|
+
... # 2xx whose body wasn't valid / didn't match the model
|
|
204
|
+
except BlitzError:
|
|
205
|
+
... # base class for everything this SDK raises
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
`429` and `5xx` are retried automatically (with backoff) up to `max_retries`;
|
|
209
|
+
`401`/`402`/`404` raise immediately. Connect timeouts and connection errors are retried,
|
|
210
|
+
but a **read timeout is not** — the server may already have processed (and billed) the
|
|
211
|
+
request, so it surfaces as `APITimeoutError` rather than risking a double charge.
|
|
212
|
+
|
|
213
|
+
## Development
|
|
214
|
+
|
|
215
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, the test/type/lint
|
|
216
|
+
commands, the enum code generator, and the automated release process.
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# blitz-api-py
|
|
2
|
+
|
|
3
|
+
The typed Python SDK for the [Blitz API](https://blitz-api.ai) — B2B data, search,
|
|
4
|
+
and enrichment.
|
|
5
|
+
|
|
6
|
+
- **Fully typed** — Pydantic v2 response models with attribute access and IDE
|
|
7
|
+
autocomplete, `TypedDict` request filters, and a shipped `py.typed` marker so
|
|
8
|
+
mypy/pyright see the types in your own code.
|
|
9
|
+
- **Sync & async** — `BlitzAPI` and `AsyncBlitzAPI` over `httpx`.
|
|
10
|
+
- **Resilient** — built-in client-side rate limiting, retries with backoff on
|
|
11
|
+
`429`/`5xx`, and a typed exception hierarchy.
|
|
12
|
+
- **Forward-compatible** — new fields the API adds never break deserialization.
|
|
13
|
+
|
|
14
|
+
> Create and manage API keys at [app.blitz-api.ai](https://app.blitz-api.ai).
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install blitz-api-py
|
|
20
|
+
# or: uv add blitz-api-py
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Requires Python 3.10+.
|
|
24
|
+
|
|
25
|
+
## Quickstart
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from blitz_api import BlitzAPI
|
|
29
|
+
from blitz_api.types import Industry, JobLevel
|
|
30
|
+
|
|
31
|
+
# api_key defaults to the BLITZ_API_KEY environment variable.
|
|
32
|
+
with BlitzAPI() as client:
|
|
33
|
+
# Health-check the key before a batch job.
|
|
34
|
+
info = client.account.key_info()
|
|
35
|
+
print(info.valid, info.remaining_credits, info.max_requests_per_seconds)
|
|
36
|
+
|
|
37
|
+
# LinkedIn profile URL -> verified work email.
|
|
38
|
+
email = client.enrichment.email(
|
|
39
|
+
person_linkedin_url="https://www.linkedin.com/in/example-person",
|
|
40
|
+
)
|
|
41
|
+
if email.found:
|
|
42
|
+
print(email.email)
|
|
43
|
+
|
|
44
|
+
# Search people with typed, autocompleted filters.
|
|
45
|
+
people = client.search.people(
|
|
46
|
+
company={"industry": {"include": [Industry.SOFTWARE_DEVELOPMENT]}},
|
|
47
|
+
people={"job_level": [JobLevel.VP]},
|
|
48
|
+
max_results=10,
|
|
49
|
+
)
|
|
50
|
+
for person in people.results:
|
|
51
|
+
print(person.full_name, person.headline)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Async
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import asyncio
|
|
58
|
+
from blitz_api import AsyncBlitzAPI
|
|
59
|
+
|
|
60
|
+
async def main() -> None:
|
|
61
|
+
async with AsyncBlitzAPI() as client:
|
|
62
|
+
result = await client.enrichment.company(
|
|
63
|
+
company_linkedin_url="https://www.linkedin.com/company/openai",
|
|
64
|
+
)
|
|
65
|
+
print(result.company.name if result.company else None)
|
|
66
|
+
|
|
67
|
+
asyncio.run(main())
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Authentication
|
|
71
|
+
|
|
72
|
+
Pass the key explicitly or via the `BLITZ_API_KEY` environment variable:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
client = BlitzAPI(api_key="sk_...") # explicit
|
|
76
|
+
client = BlitzAPI() # reads BLITZ_API_KEY
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
The key is sent in the `x-api-key` header. Never expose it in client-side code —
|
|
80
|
+
always call the API from your backend.
|
|
81
|
+
|
|
82
|
+
## Endpoints
|
|
83
|
+
|
|
84
|
+
All methods are grouped into four namespaces:
|
|
85
|
+
|
|
86
|
+
| Namespace | Methods |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| `client.account` | `key_info()` |
|
|
89
|
+
| `client.search` | `people()`, `companies()`, `employee_finder()`, `waterfall_icp()` |
|
|
90
|
+
| `client.enrichment` | `email()`, `phone()`, `email_to_person()`, `phone_to_person()`, `company()`, `domain_to_linkedin()`, `linkedin_to_domain()` |
|
|
91
|
+
| `client.utils` | `current_date()`, `company_employment_distribution()` |
|
|
92
|
+
|
|
93
|
+
Every method returns a typed Pydantic model (see `blitz_api.types`). Enum-backed
|
|
94
|
+
filter fields (e.g. `Industry`, `JobLevel`, `Continent`) accept either an enum
|
|
95
|
+
member or a raw string.
|
|
96
|
+
|
|
97
|
+
## Pagination
|
|
98
|
+
|
|
99
|
+
The search methods return an **auto-paginating page**: iterate it and the SDK fetches
|
|
100
|
+
each subsequent page for you. `search.people`/`search.companies` are cursor-based;
|
|
101
|
+
`search.employee_finder` is page-based — both behave identically here.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
# Iterate every matching person across all pages — no cursor handling needed.
|
|
105
|
+
for person in client.search.people(people={"job_level": ["VP"]}):
|
|
106
|
+
print(person.full_name)
|
|
107
|
+
|
|
108
|
+
# Async works the same way.
|
|
109
|
+
async for person in await async_client.search.people(people={"job_level": ["VP"]}):
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
# Bound how much you pull.
|
|
113
|
+
for person in client.search.people(...).auto_paging_iter(max_items=200):
|
|
114
|
+
...
|
|
115
|
+
|
|
116
|
+
# Per-page control: inspect totals / cursors as you go.
|
|
117
|
+
for page in client.search.companies(company={...}).iter_pages(max_pages=5):
|
|
118
|
+
print(page.total_results, len(page.results), page.cursor)
|
|
119
|
+
|
|
120
|
+
# Or page manually.
|
|
121
|
+
page = client.search.people(people={...}, max_results=50)
|
|
122
|
+
print(page.results, page.cursor)
|
|
123
|
+
nxt = page.get_next_page() # None once exhausted
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The page types (`CursorPage`, `PageNumberPage`, and their `Async*` variants) are
|
|
127
|
+
exported from `blitz_api`.
|
|
128
|
+
|
|
129
|
+
## Configuration
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
client = BlitzAPI(
|
|
133
|
+
api_key=None, # falls back to BLITZ_API_KEY
|
|
134
|
+
base_url="https://api.blitz-api.ai",
|
|
135
|
+
timeout=30.0, # seconds, or an httpx.Timeout
|
|
136
|
+
max_retries=3, # retries on 429 / 5xx / network errors
|
|
137
|
+
rate_limit_rps=5.0, # client-side throttle; None to disable
|
|
138
|
+
)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The client-side rate limiter is a sliding window — at most `rate_limit_rps` requests
|
|
142
|
+
in any rolling second — so a single client instance stays under the API's limit (5 req/s
|
|
143
|
+
by default; check your key's limit via
|
|
144
|
+
`client.account.key_info().max_requests_per_seconds`). Across multiple processes you may
|
|
145
|
+
still hit `429` — the retry path handles that.
|
|
146
|
+
|
|
147
|
+
Every method also accepts a per-call `timeout` (seconds or an `httpx.Timeout`) when one
|
|
148
|
+
endpoint needs longer than the client default:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
client.search.people(people={"job_level": ["VP"]}, timeout=10.0)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Error handling
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from blitz_api import (
|
|
158
|
+
BlitzError, AuthenticationError, InsufficientCreditsError,
|
|
159
|
+
NotFoundError, RateLimitError, APIStatusError, APIConnectionError,
|
|
160
|
+
APITimeoutError, APIResponseValidationError,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
client.enrichment.email(person_linkedin_url="...")
|
|
165
|
+
except InsufficientCreditsError:
|
|
166
|
+
... # 402 — out of credits
|
|
167
|
+
except AuthenticationError:
|
|
168
|
+
... # 401 — bad key
|
|
169
|
+
except APIStatusError as err:
|
|
170
|
+
print(err.status_code, err.message, err.body)
|
|
171
|
+
except APIResponseValidationError:
|
|
172
|
+
... # 2xx whose body wasn't valid / didn't match the model
|
|
173
|
+
except BlitzError:
|
|
174
|
+
... # base class for everything this SDK raises
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`429` and `5xx` are retried automatically (with backoff) up to `max_retries`;
|
|
178
|
+
`401`/`402`/`404` raise immediately. Connect timeouts and connection errors are retried,
|
|
179
|
+
but a **read timeout is not** — the server may already have processed (and billed) the
|
|
180
|
+
request, so it surfaces as `APITimeoutError` rather than risking a double charge.
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, the test/type/lint
|
|
185
|
+
commands, the enum code generator, and the automated release process.
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "blitz-api-py"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Typed Python SDK for the Blitz API — B2B data, search, and enrichment."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Blitz API", email = "founders@blitz-api.ai" }]
|
|
14
|
+
keywords = ["blitz", "blitz-api", "b2b", "enrichment", "linkedin", "sales", "gtm", "sdk"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
"Typing :: Typed",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"httpx>=0.28,<1",
|
|
31
|
+
"pydantic>=2.13,<3",
|
|
32
|
+
"typing-extensions>=4.10",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://blitz-api.ai"
|
|
37
|
+
Documentation = "https://docs.blitz-api.ai"
|
|
38
|
+
Repository = "https://github.com/api-blitz/blitz-api-py"
|
|
39
|
+
Issues = "https://github.com/api-blitz/blitz-api-py/issues"
|
|
40
|
+
Changelog = "https://github.com/api-blitz/blitz-api-py/blob/main/CHANGELOG.md"
|
|
41
|
+
|
|
42
|
+
[dependency-groups]
|
|
43
|
+
dev = [
|
|
44
|
+
"ruff>=0.15",
|
|
45
|
+
"mypy>=2.1",
|
|
46
|
+
"pyright>=1.1.390",
|
|
47
|
+
"pytest>=8.3",
|
|
48
|
+
"pytest-asyncio>=0.24",
|
|
49
|
+
"pytest-httpx>=0.35",
|
|
50
|
+
"pytest-cov>=6.0",
|
|
51
|
+
"tokenize-rt>=6.2.0",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[tool.hatch.version]
|
|
55
|
+
path = "src/blitz_api/_version.py"
|
|
56
|
+
|
|
57
|
+
[tool.hatch.build.targets.wheel]
|
|
58
|
+
packages = ["src/blitz_api"]
|
|
59
|
+
|
|
60
|
+
[tool.hatch.build.targets.sdist]
|
|
61
|
+
include = ["src/blitz_api", "README.md", "LICENSE", "CHANGELOG.md"]
|
|
62
|
+
|
|
63
|
+
[tool.ruff]
|
|
64
|
+
line-length = 100
|
|
65
|
+
src = ["src", "tests", "scripts", "examples"]
|
|
66
|
+
target-version = "py310"
|
|
67
|
+
# Machine-generated modules own their own formatting (a drift guard verifies them);
|
|
68
|
+
# keep ruff from reformatting them. mypy/pyright still type-check them.
|
|
69
|
+
# - types/enums.py <- scripts/gen_enums.py
|
|
70
|
+
# - *_sync.py + _sync/ <- scripts/gen_sync.py (transliterated from the async source)
|
|
71
|
+
extend-exclude = [
|
|
72
|
+
"src/blitz_api/types/enums.py",
|
|
73
|
+
"src/blitz_api/_client_sync.py",
|
|
74
|
+
"src/blitz_api/_pagination_sync.py",
|
|
75
|
+
"src/blitz_api/_rate_limit_sync.py",
|
|
76
|
+
"src/blitz_api/resources/_sync",
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
[tool.ruff.lint]
|
|
80
|
+
select = ["E", "F", "I", "UP", "B", "C4", "SIM", "PIE", "RUF"]
|
|
81
|
+
# RUF022: __all__ is intentionally grouped by category, not alphabetized.
|
|
82
|
+
ignore = ["RUF022"]
|
|
83
|
+
|
|
84
|
+
[tool.ruff.lint.per-file-ignores]
|
|
85
|
+
# RUF012: `= []` defaults are safe on Pydantic models (deep-copied per instance)
|
|
86
|
+
# and keep pyright strict happy (it infers the type from the field annotation).
|
|
87
|
+
"src/blitz_api/types/*.py" = ["RUF012"]
|
|
88
|
+
"src/blitz_api/_pagination_async.py" = ["RUF012"]
|
|
89
|
+
|
|
90
|
+
[tool.ruff.lint.isort]
|
|
91
|
+
known-first-party = ["blitz_api"]
|
|
92
|
+
|
|
93
|
+
[tool.mypy]
|
|
94
|
+
python_version = "3.10"
|
|
95
|
+
strict = true
|
|
96
|
+
warn_unreachable = true
|
|
97
|
+
warn_redundant_casts = true
|
|
98
|
+
plugins = ["pydantic.mypy"]
|
|
99
|
+
files = ["src", "tests", "scripts", "examples"]
|
|
100
|
+
|
|
101
|
+
# tokenize-rt (used only by scripts/gen_sync.py) ships no type stubs.
|
|
102
|
+
[[tool.mypy.overrides]]
|
|
103
|
+
module = ["tokenize_rt"]
|
|
104
|
+
ignore_missing_imports = true
|
|
105
|
+
|
|
106
|
+
# The `# type: ignore[override]` on the page __aiter__ is unused in the async source
|
|
107
|
+
# (BaseModel has no __aiter__) but required in the generated sync twin's __iter__.
|
|
108
|
+
[[tool.mypy.overrides]]
|
|
109
|
+
module = ["blitz_api._pagination_async"]
|
|
110
|
+
warn_unused_ignores = false
|
|
111
|
+
|
|
112
|
+
[tool.pyright]
|
|
113
|
+
include = ["src", "tests", "examples"]
|
|
114
|
+
pythonVersion = "3.10"
|
|
115
|
+
typeCheckingMode = "strict"
|
|
116
|
+
reportMissingTypeStubs = false
|
|
117
|
+
# Resources intentionally call the client's internal `_request` across modules;
|
|
118
|
+
# this is internal encapsulation, not a public-API typing concern (mypy allows it).
|
|
119
|
+
reportPrivateUsage = false
|
|
120
|
+
|
|
121
|
+
[tool.pytest.ini_options]
|
|
122
|
+
asyncio_mode = "auto"
|
|
123
|
+
testpaths = ["tests"]
|
|
124
|
+
pythonpath = ["."]
|
|
125
|
+
addopts = "-ra"
|
|
126
|
+
|
|
127
|
+
[tool.coverage.run]
|
|
128
|
+
source = ["blitz_api"]
|
|
129
|
+
branch = true
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Typed Python SDK for the Blitz API.
|
|
2
|
+
|
|
3
|
+
Quickstart::
|
|
4
|
+
|
|
5
|
+
from blitz_api import BlitzAPI
|
|
6
|
+
|
|
7
|
+
client = BlitzAPI() # reads the BLITZ_API_KEY environment variable
|
|
8
|
+
result = client.enrichment.email(
|
|
9
|
+
person_linkedin_url="https://www.linkedin.com/in/example",
|
|
10
|
+
)
|
|
11
|
+
print(result.found, result.email)
|
|
12
|
+
|
|
13
|
+
See https://docs.blitz-api.ai for the full API reference.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from ._client import AsyncBlitzAPI, BlitzAPI
|
|
19
|
+
from ._exceptions import (
|
|
20
|
+
APIConnectionError,
|
|
21
|
+
APIResponseValidationError,
|
|
22
|
+
APIStatusError,
|
|
23
|
+
APITimeoutError,
|
|
24
|
+
AuthenticationError,
|
|
25
|
+
BlitzError,
|
|
26
|
+
InsufficientCreditsError,
|
|
27
|
+
NotFoundError,
|
|
28
|
+
RateLimitError,
|
|
29
|
+
ServerError,
|
|
30
|
+
)
|
|
31
|
+
from ._pagination_async import AsyncCursorPage, AsyncPageNumberPage
|
|
32
|
+
from ._pagination_sync import CursorPage, PageNumberPage
|
|
33
|
+
from ._version import __version__
|
|
34
|
+
from .types import (
|
|
35
|
+
CompanyFilter,
|
|
36
|
+
Industry,
|
|
37
|
+
PeopleFilter,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
__all__ = [
|
|
41
|
+
"__version__",
|
|
42
|
+
# clients
|
|
43
|
+
"BlitzAPI",
|
|
44
|
+
"AsyncBlitzAPI",
|
|
45
|
+
# pagination (returned by the search.* methods)
|
|
46
|
+
"CursorPage",
|
|
47
|
+
"AsyncCursorPage",
|
|
48
|
+
"PageNumberPage",
|
|
49
|
+
"AsyncPageNumberPage",
|
|
50
|
+
# exceptions
|
|
51
|
+
"BlitzError",
|
|
52
|
+
"APIConnectionError",
|
|
53
|
+
"APITimeoutError",
|
|
54
|
+
"APIResponseValidationError",
|
|
55
|
+
"APIStatusError",
|
|
56
|
+
"AuthenticationError",
|
|
57
|
+
"InsufficientCreditsError",
|
|
58
|
+
"NotFoundError",
|
|
59
|
+
"RateLimitError",
|
|
60
|
+
"ServerError",
|
|
61
|
+
# commonly-used types (full set under blitz_api.types)
|
|
62
|
+
"Industry",
|
|
63
|
+
"CompanyFilter",
|
|
64
|
+
"PeopleFilter",
|
|
65
|
+
]
|