blitz-api-py 0.2.0__tar.gz → 0.4.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.2.0 → blitz_api_py-0.4.0}/CHANGELOG.md +19 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/PKG-INFO +97 -3
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/README.md +96 -2
- blitz_api_py-0.4.0/src/blitz_api/_version.py +1 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/_async/utils.py +21 -1
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/_sync/utils.py +21 -1
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/types/__init__.py +4 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/types/utils.py +20 -0
- blitz_api_py-0.2.0/src/blitz_api/_version.py +0 -1
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/.gitignore +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/LICENSE +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/pyproject.toml +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/__init__.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_base_client.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_client.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_client_async.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_client_sync.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_compat.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_constants.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_exceptions.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_pagination_async.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_pagination_base.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_pagination_sync.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_rate_limit.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_rate_limit_async.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/_rate_limit_sync.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/py.typed +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/__init__.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/_async/__init__.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/_async/account.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/_async/enrichment.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/_async/search.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/_sync/__init__.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/_sync/account.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/_sync/enrichment.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/resources/_sync/search.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/types/_models.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/types/account.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/types/enrichment.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/types/enums.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/types/filters.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/types/search.py +0 -0
- {blitz_api_py-0.2.0 → blitz_api_py-0.4.0}/src/blitz_api/types/shared.py +0 -0
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.4.0](https://github.com/api-blitz/blitz-api-py/compare/v0.3.0...v0.4.0) (2026-06-17)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add company department distribution endpoint and response models ([b64a6d9](https://github.com/api-blitz/blitz-api-py/commit/b64a6d9bada069206c77ee11c9f86fae01ef6baa))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
* port JS SDK README upgrade (badges, billing note, TOC, example) ([#11](https://github.com/api-blitz/blitz-api-py/issues/11)) ([2276513](https://github.com/api-blitz/blitz-api-py/commit/2276513cdae4c23d4a1b50f59366e7156bf81019))
|
|
14
|
+
|
|
15
|
+
## [0.3.0](https://github.com/api-blitz/blitz-api-py/compare/v0.2.0...v0.3.0) (2026-06-04)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* generate enums from the live OpenAPI spec with offline drift guard ([#9](https://github.com/api-blitz/blitz-api-py/issues/9)) ([fee865c](https://github.com/api-blitz/blitz-api-py/commit/fee865c4d8f7c685330794fa79921dccd7556e37))
|
|
21
|
+
|
|
3
22
|
## [0.2.0](https://github.com/api-blitz/blitz-api-py/compare/v0.1.0...v0.2.0) (2026-06-02)
|
|
4
23
|
|
|
5
24
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: blitz-api-py
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Typed Python SDK for the Blitz API — B2B data, search, and enrichment.
|
|
5
5
|
Project-URL: Homepage, https://blitz-api.ai
|
|
6
6
|
Project-URL: Documentation, https://docs.blitz-api.ai
|
|
@@ -31,6 +31,11 @@ Description-Content-Type: text/markdown
|
|
|
31
31
|
|
|
32
32
|
# blitz-api-py
|
|
33
33
|
|
|
34
|
+
[](https://pypi.org/project/blitz-api-py/)
|
|
35
|
+
[](https://pypi.org/project/blitz-api-py/)
|
|
36
|
+
[](https://github.com/api-blitz/blitz-api-py/actions/workflows/ci.yml)
|
|
37
|
+
[](./LICENSE)
|
|
38
|
+
|
|
34
39
|
The typed Python SDK for the [Blitz API](https://blitz-api.ai) — B2B data, search,
|
|
35
40
|
and enrichment.
|
|
36
41
|
|
|
@@ -41,14 +46,35 @@ and enrichment.
|
|
|
41
46
|
- **Resilient** — built-in client-side rate limiting, retries with backoff on
|
|
42
47
|
`429`/`5xx`, and a typed exception hierarchy.
|
|
43
48
|
- **Forward-compatible** — new fields the API adds never break deserialization.
|
|
49
|
+
- **1:1 with the API** — request filters and response fields are snake_case,
|
|
50
|
+
matching [docs.blitz-api.ai](https://docs.blitz-api.ai).
|
|
44
51
|
|
|
45
52
|
> Create and manage API keys at [app.blitz-api.ai](https://app.blitz-api.ai).
|
|
46
53
|
|
|
54
|
+
> **Billing.** Blitz bills **per result**. A bare `for person in client.search.people(...)`
|
|
55
|
+
> loop streams every match up to the server-side limit (people: 50k results), which can be
|
|
56
|
+
> a lot of credits. Bound spend with **`max_items`** (a client-side total cap on
|
|
57
|
+
> `.collect()` / `.auto_paging_iter()`, never sent on the wire) — details in
|
|
58
|
+
> [Pagination](#pagination).
|
|
59
|
+
|
|
60
|
+
## Contents
|
|
61
|
+
|
|
62
|
+
- [Installation](#installation)
|
|
63
|
+
- [Quickstart](#quickstart)
|
|
64
|
+
- [Example: find, enrich, collect](#example-find-enrich-collect)
|
|
65
|
+
- [Authentication](#authentication)
|
|
66
|
+
- [Endpoints](#endpoints)
|
|
67
|
+
- [Pagination](#pagination)
|
|
68
|
+
- [Configuration](#configuration)
|
|
69
|
+
- [Error handling](#error-handling)
|
|
70
|
+
- [Forward compatibility](#forward-compatibility)
|
|
71
|
+
- [Development](#development)
|
|
72
|
+
|
|
47
73
|
## Installation
|
|
48
74
|
|
|
49
75
|
```bash
|
|
50
76
|
pip install blitz-api-py
|
|
51
|
-
# or: uv add blitz-api-py
|
|
77
|
+
# or: poetry add blitz-api-py / uv add blitz-api-py
|
|
52
78
|
```
|
|
53
79
|
|
|
54
80
|
Requires Python 3.10+.
|
|
@@ -98,6 +124,61 @@ async def main() -> None:
|
|
|
98
124
|
asyncio.run(main())
|
|
99
125
|
```
|
|
100
126
|
|
|
127
|
+
## Example: find, enrich, collect
|
|
128
|
+
|
|
129
|
+
A complete flow — find people, enrich each one's verified work email, collect the
|
|
130
|
+
contacts. `max_items` caps the total fetched so the run can't surprise you with credits.
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from blitz_api import BlitzAPI
|
|
134
|
+
from blitz_api.types import Industry, JobLevel
|
|
135
|
+
|
|
136
|
+
client = BlitzAPI() # reads BLITZ_API_KEY
|
|
137
|
+
|
|
138
|
+
# 1. Find up to 25 VPs at software companies (typed filters, 1:1 with the API).
|
|
139
|
+
leads = client.search.people(
|
|
140
|
+
company={"industry": {"include": [Industry.SOFTWARE_DEVELOPMENT]}},
|
|
141
|
+
people={"job_level": [JobLevel.VP]},
|
|
142
|
+
max_results=25,
|
|
143
|
+
).collect(max_items=25) # client-side total cap — bounds credit spend
|
|
144
|
+
|
|
145
|
+
# 2. Enrich each lead's verified work email from their LinkedIn profile URL.
|
|
146
|
+
contacts: list[dict[str, str | None]] = []
|
|
147
|
+
for person in leads:
|
|
148
|
+
if not person.linkedin_url:
|
|
149
|
+
continue
|
|
150
|
+
result = client.enrichment.email(person_linkedin_url=person.linkedin_url)
|
|
151
|
+
if result.found:
|
|
152
|
+
contacts.append({"name": person.full_name, "email": result.email})
|
|
153
|
+
|
|
154
|
+
print(f"Collected {len(contacts)} contacts")
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
What comes back is typed and snake_case. A `Person` from the search above (fields are a
|
|
158
|
+
**superset** — only what the profile has is populated, and unknown fields the API adds
|
|
159
|
+
later are preserved):
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
Person(
|
|
163
|
+
full_name="Jordan Lee",
|
|
164
|
+
headline="VP of Engineering at Acme",
|
|
165
|
+
linkedin_url="https://www.linkedin.com/in/example-person",
|
|
166
|
+
location=Location(city="San Francisco", state_code="CA", country_code="US", continent="North America"),
|
|
167
|
+
experiences=[Experience(job_title="VP of Engineering", company_name="Acme", job_is_current=True)],
|
|
168
|
+
# first_name, last_name, skills, education, certifications, … also present
|
|
169
|
+
)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
And `enrichment.email(...)` returns:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
EmailEnrichmentResponse(
|
|
176
|
+
found=True,
|
|
177
|
+
email="jordan@acme.com",
|
|
178
|
+
all_emails=[EmailMatch(email="jordan@acme.com", email_domain="acme.com")],
|
|
179
|
+
)
|
|
180
|
+
```
|
|
181
|
+
|
|
101
182
|
## Authentication
|
|
102
183
|
|
|
103
184
|
Pass the key explicitly or via the `BLITZ_API_KEY` environment variable:
|
|
@@ -119,7 +200,7 @@ All methods are grouped into four namespaces:
|
|
|
119
200
|
| `client.account` | `key_info()` |
|
|
120
201
|
| `client.search` | `people()`, `companies()`, `employee_finder()`, `waterfall_icp()` |
|
|
121
202
|
| `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()` |
|
|
203
|
+
| `client.utils` | `current_date()`, `company_employment_distribution()`, `company_department_distribution()` |
|
|
123
204
|
|
|
124
205
|
Every method returns a typed Pydantic model (see `blitz_api.types`). Enum-backed
|
|
125
206
|
filter fields (e.g. `Industry`, `JobLevel`, `Continent`) accept either an enum
|
|
@@ -131,6 +212,13 @@ The search methods return an **auto-paginating page**: iterate it and the SDK fe
|
|
|
131
212
|
each subsequent page for you. `search.people`/`search.companies` are cursor-based;
|
|
132
213
|
`search.employee_finder` is page-based — both behave identically here.
|
|
133
214
|
|
|
215
|
+
> **`max_results` is the page size, not a total.** It's results per page, and the API
|
|
216
|
+
> **bills 1 credit per result returned**. A bare `for person in client.search.people(...)`
|
|
217
|
+
> loop streams *every* match up to the server-side limit (people: 50k results / 1k pages;
|
|
218
|
+
> employee finder: 10k), which can be a lot of credits. Bound it with **`max_items`** on
|
|
219
|
+
> `.collect()` / `.auto_paging_iter()` (a client-side total cap — never sent on the wire),
|
|
220
|
+
> `break` out of the loop, or drive pages manually.
|
|
221
|
+
|
|
134
222
|
```python
|
|
135
223
|
# Iterate every matching person across all pages — no cursor handling needed.
|
|
136
224
|
for person in client.search.people(people={"job_level": ["VP"]}):
|
|
@@ -210,6 +298,12 @@ except BlitzError:
|
|
|
210
298
|
but a **read timeout is not** — the server may already have processed (and billed) the
|
|
211
299
|
request, so it surfaces as `APITimeoutError` rather than risking a double charge.
|
|
212
300
|
|
|
301
|
+
## Forward compatibility
|
|
302
|
+
|
|
303
|
+
Response models subclass a base configured with `extra="allow"`, so a field the API adds
|
|
304
|
+
before this SDK models it is still present on the parsed object (via attribute access or
|
|
305
|
+
`.model_extra`). Known fields stay precisely typed.
|
|
306
|
+
|
|
213
307
|
## Development
|
|
214
308
|
|
|
215
309
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, the test/type/lint
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# blitz-api-py
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/blitz-api-py/)
|
|
4
|
+
[](https://pypi.org/project/blitz-api-py/)
|
|
5
|
+
[](https://github.com/api-blitz/blitz-api-py/actions/workflows/ci.yml)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
|
|
3
8
|
The typed Python SDK for the [Blitz API](https://blitz-api.ai) — B2B data, search,
|
|
4
9
|
and enrichment.
|
|
5
10
|
|
|
@@ -10,14 +15,35 @@ and enrichment.
|
|
|
10
15
|
- **Resilient** — built-in client-side rate limiting, retries with backoff on
|
|
11
16
|
`429`/`5xx`, and a typed exception hierarchy.
|
|
12
17
|
- **Forward-compatible** — new fields the API adds never break deserialization.
|
|
18
|
+
- **1:1 with the API** — request filters and response fields are snake_case,
|
|
19
|
+
matching [docs.blitz-api.ai](https://docs.blitz-api.ai).
|
|
13
20
|
|
|
14
21
|
> Create and manage API keys at [app.blitz-api.ai](https://app.blitz-api.ai).
|
|
15
22
|
|
|
23
|
+
> **Billing.** Blitz bills **per result**. A bare `for person in client.search.people(...)`
|
|
24
|
+
> loop streams every match up to the server-side limit (people: 50k results), which can be
|
|
25
|
+
> a lot of credits. Bound spend with **`max_items`** (a client-side total cap on
|
|
26
|
+
> `.collect()` / `.auto_paging_iter()`, never sent on the wire) — details in
|
|
27
|
+
> [Pagination](#pagination).
|
|
28
|
+
|
|
29
|
+
## Contents
|
|
30
|
+
|
|
31
|
+
- [Installation](#installation)
|
|
32
|
+
- [Quickstart](#quickstart)
|
|
33
|
+
- [Example: find, enrich, collect](#example-find-enrich-collect)
|
|
34
|
+
- [Authentication](#authentication)
|
|
35
|
+
- [Endpoints](#endpoints)
|
|
36
|
+
- [Pagination](#pagination)
|
|
37
|
+
- [Configuration](#configuration)
|
|
38
|
+
- [Error handling](#error-handling)
|
|
39
|
+
- [Forward compatibility](#forward-compatibility)
|
|
40
|
+
- [Development](#development)
|
|
41
|
+
|
|
16
42
|
## Installation
|
|
17
43
|
|
|
18
44
|
```bash
|
|
19
45
|
pip install blitz-api-py
|
|
20
|
-
# or: uv add blitz-api-py
|
|
46
|
+
# or: poetry add blitz-api-py / uv add blitz-api-py
|
|
21
47
|
```
|
|
22
48
|
|
|
23
49
|
Requires Python 3.10+.
|
|
@@ -67,6 +93,61 @@ async def main() -> None:
|
|
|
67
93
|
asyncio.run(main())
|
|
68
94
|
```
|
|
69
95
|
|
|
96
|
+
## Example: find, enrich, collect
|
|
97
|
+
|
|
98
|
+
A complete flow — find people, enrich each one's verified work email, collect the
|
|
99
|
+
contacts. `max_items` caps the total fetched so the run can't surprise you with credits.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from blitz_api import BlitzAPI
|
|
103
|
+
from blitz_api.types import Industry, JobLevel
|
|
104
|
+
|
|
105
|
+
client = BlitzAPI() # reads BLITZ_API_KEY
|
|
106
|
+
|
|
107
|
+
# 1. Find up to 25 VPs at software companies (typed filters, 1:1 with the API).
|
|
108
|
+
leads = client.search.people(
|
|
109
|
+
company={"industry": {"include": [Industry.SOFTWARE_DEVELOPMENT]}},
|
|
110
|
+
people={"job_level": [JobLevel.VP]},
|
|
111
|
+
max_results=25,
|
|
112
|
+
).collect(max_items=25) # client-side total cap — bounds credit spend
|
|
113
|
+
|
|
114
|
+
# 2. Enrich each lead's verified work email from their LinkedIn profile URL.
|
|
115
|
+
contacts: list[dict[str, str | None]] = []
|
|
116
|
+
for person in leads:
|
|
117
|
+
if not person.linkedin_url:
|
|
118
|
+
continue
|
|
119
|
+
result = client.enrichment.email(person_linkedin_url=person.linkedin_url)
|
|
120
|
+
if result.found:
|
|
121
|
+
contacts.append({"name": person.full_name, "email": result.email})
|
|
122
|
+
|
|
123
|
+
print(f"Collected {len(contacts)} contacts")
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
What comes back is typed and snake_case. A `Person` from the search above (fields are a
|
|
127
|
+
**superset** — only what the profile has is populated, and unknown fields the API adds
|
|
128
|
+
later are preserved):
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
Person(
|
|
132
|
+
full_name="Jordan Lee",
|
|
133
|
+
headline="VP of Engineering at Acme",
|
|
134
|
+
linkedin_url="https://www.linkedin.com/in/example-person",
|
|
135
|
+
location=Location(city="San Francisco", state_code="CA", country_code="US", continent="North America"),
|
|
136
|
+
experiences=[Experience(job_title="VP of Engineering", company_name="Acme", job_is_current=True)],
|
|
137
|
+
# first_name, last_name, skills, education, certifications, … also present
|
|
138
|
+
)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
And `enrichment.email(...)` returns:
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
EmailEnrichmentResponse(
|
|
145
|
+
found=True,
|
|
146
|
+
email="jordan@acme.com",
|
|
147
|
+
all_emails=[EmailMatch(email="jordan@acme.com", email_domain="acme.com")],
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
70
151
|
## Authentication
|
|
71
152
|
|
|
72
153
|
Pass the key explicitly or via the `BLITZ_API_KEY` environment variable:
|
|
@@ -88,7 +169,7 @@ All methods are grouped into four namespaces:
|
|
|
88
169
|
| `client.account` | `key_info()` |
|
|
89
170
|
| `client.search` | `people()`, `companies()`, `employee_finder()`, `waterfall_icp()` |
|
|
90
171
|
| `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()` |
|
|
172
|
+
| `client.utils` | `current_date()`, `company_employment_distribution()`, `company_department_distribution()` |
|
|
92
173
|
|
|
93
174
|
Every method returns a typed Pydantic model (see `blitz_api.types`). Enum-backed
|
|
94
175
|
filter fields (e.g. `Industry`, `JobLevel`, `Continent`) accept either an enum
|
|
@@ -100,6 +181,13 @@ The search methods return an **auto-paginating page**: iterate it and the SDK fe
|
|
|
100
181
|
each subsequent page for you. `search.people`/`search.companies` are cursor-based;
|
|
101
182
|
`search.employee_finder` is page-based — both behave identically here.
|
|
102
183
|
|
|
184
|
+
> **`max_results` is the page size, not a total.** It's results per page, and the API
|
|
185
|
+
> **bills 1 credit per result returned**. A bare `for person in client.search.people(...)`
|
|
186
|
+
> loop streams *every* match up to the server-side limit (people: 50k results / 1k pages;
|
|
187
|
+
> employee finder: 10k), which can be a lot of credits. Bound it with **`max_items`** on
|
|
188
|
+
> `.collect()` / `.auto_paging_iter()` (a client-side total cap — never sent on the wire),
|
|
189
|
+
> `break` out of the loop, or drive pages manually.
|
|
190
|
+
|
|
103
191
|
```python
|
|
104
192
|
# Iterate every matching person across all pages — no cursor handling needed.
|
|
105
193
|
for person in client.search.people(people={"job_level": ["VP"]}):
|
|
@@ -179,6 +267,12 @@ except BlitzError:
|
|
|
179
267
|
but a **read timeout is not** — the server may already have processed (and billed) the
|
|
180
268
|
request, so it surfaces as `APITimeoutError` rather than risking a double charge.
|
|
181
269
|
|
|
270
|
+
## Forward compatibility
|
|
271
|
+
|
|
272
|
+
Response models subclass a base configured with `extra="allow"`, so a field the API adds
|
|
273
|
+
before this SDK models it is still present on the parsed object (via attribute access or
|
|
274
|
+
`.model_extra`). Known fields stay precisely typed.
|
|
275
|
+
|
|
182
276
|
## Development
|
|
183
277
|
|
|
184
278
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, the test/type/lint
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.4.0" # x-release-please-version
|
|
@@ -5,13 +5,18 @@ from __future__ import annotations
|
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
7
|
from ..._compat import TimeoutParam
|
|
8
|
-
from ...types.utils import
|
|
8
|
+
from ...types.utils import (
|
|
9
|
+
CompanyDepartmentDistributionResponse,
|
|
10
|
+
CompanyEmploymentDistributionResponse,
|
|
11
|
+
CurrentDateResponse,
|
|
12
|
+
)
|
|
9
13
|
|
|
10
14
|
if TYPE_CHECKING:
|
|
11
15
|
from ..._client import AsyncBlitzAPI
|
|
12
16
|
|
|
13
17
|
_CURRENT_DATE = "/v2/utils/current-date"
|
|
14
18
|
_EMPLOYMENT_DISTRIBUTION = "/v2/utils/company-employment-distribution"
|
|
19
|
+
_DEPARTMENT_DISTRIBUTION = "/v2/utils/company-department-distribution"
|
|
15
20
|
|
|
16
21
|
|
|
17
22
|
class AsyncUtilsResource:
|
|
@@ -41,3 +46,18 @@ class AsyncUtilsResource:
|
|
|
41
46
|
cast_to=CompanyEmploymentDistributionResponse,
|
|
42
47
|
timeout=timeout,
|
|
43
48
|
)
|
|
49
|
+
|
|
50
|
+
async def company_department_distribution(
|
|
51
|
+
self, *, company_linkedin_url: str, timeout: TimeoutParam = None
|
|
52
|
+
) -> CompanyDepartmentDistributionResponse:
|
|
53
|
+
"""Get a company's employee count broken down by department.
|
|
54
|
+
|
|
55
|
+
Employees with no classified department are counted under ``"Other"``.
|
|
56
|
+
"""
|
|
57
|
+
return await self._client._request(
|
|
58
|
+
"POST",
|
|
59
|
+
_DEPARTMENT_DISTRIBUTION,
|
|
60
|
+
body={"company_linkedin_url": company_linkedin_url},
|
|
61
|
+
cast_to=CompanyDepartmentDistributionResponse,
|
|
62
|
+
timeout=timeout,
|
|
63
|
+
)
|
|
@@ -7,13 +7,18 @@ from __future__ import annotations
|
|
|
7
7
|
from typing import TYPE_CHECKING
|
|
8
8
|
|
|
9
9
|
from ..._compat import TimeoutParam
|
|
10
|
-
from ...types.utils import
|
|
10
|
+
from ...types.utils import (
|
|
11
|
+
CompanyDepartmentDistributionResponse,
|
|
12
|
+
CompanyEmploymentDistributionResponse,
|
|
13
|
+
CurrentDateResponse,
|
|
14
|
+
)
|
|
11
15
|
|
|
12
16
|
if TYPE_CHECKING:
|
|
13
17
|
from ..._client import BlitzAPI
|
|
14
18
|
|
|
15
19
|
_CURRENT_DATE = "/v2/utils/current-date"
|
|
16
20
|
_EMPLOYMENT_DISTRIBUTION = "/v2/utils/company-employment-distribution"
|
|
21
|
+
_DEPARTMENT_DISTRIBUTION = "/v2/utils/company-department-distribution"
|
|
17
22
|
|
|
18
23
|
|
|
19
24
|
class UtilsResource:
|
|
@@ -43,3 +48,18 @@ class UtilsResource:
|
|
|
43
48
|
cast_to=CompanyEmploymentDistributionResponse,
|
|
44
49
|
timeout=timeout,
|
|
45
50
|
)
|
|
51
|
+
|
|
52
|
+
def company_department_distribution(
|
|
53
|
+
self, *, company_linkedin_url: str, timeout: TimeoutParam = None
|
|
54
|
+
) -> CompanyDepartmentDistributionResponse:
|
|
55
|
+
"""Get a company's employee count broken down by department.
|
|
56
|
+
|
|
57
|
+
Employees with no classified department are counted under ``"Other"``.
|
|
58
|
+
"""
|
|
59
|
+
return self._client._request(
|
|
60
|
+
"POST",
|
|
61
|
+
_DEPARTMENT_DISTRIBUTION,
|
|
62
|
+
body={"company_linkedin_url": company_linkedin_url},
|
|
63
|
+
cast_to=CompanyDepartmentDistributionResponse,
|
|
64
|
+
timeout=timeout,
|
|
65
|
+
)
|
|
@@ -53,8 +53,10 @@ from .shared import (
|
|
|
53
53
|
Person,
|
|
54
54
|
)
|
|
55
55
|
from .utils import (
|
|
56
|
+
CompanyDepartmentDistributionResponse,
|
|
56
57
|
CompanyEmploymentDistributionResponse,
|
|
57
58
|
CurrentDateResponse,
|
|
59
|
+
DepartmentDistributionItem,
|
|
58
60
|
EmploymentDistributionItem,
|
|
59
61
|
)
|
|
60
62
|
|
|
@@ -105,4 +107,6 @@ __all__ = [
|
|
|
105
107
|
"CurrentDateResponse",
|
|
106
108
|
"EmploymentDistributionItem",
|
|
107
109
|
"CompanyEmploymentDistributionResponse",
|
|
110
|
+
"DepartmentDistributionItem",
|
|
111
|
+
"CompanyDepartmentDistributionResponse",
|
|
108
112
|
]
|
|
@@ -8,6 +8,8 @@ __all__ = [
|
|
|
8
8
|
"CurrentDateResponse",
|
|
9
9
|
"EmploymentDistributionItem",
|
|
10
10
|
"CompanyEmploymentDistributionResponse",
|
|
11
|
+
"DepartmentDistributionItem",
|
|
12
|
+
"CompanyDepartmentDistributionResponse",
|
|
11
13
|
]
|
|
12
14
|
|
|
13
15
|
|
|
@@ -33,3 +35,21 @@ class CompanyEmploymentDistributionResponse(BlitzModel):
|
|
|
33
35
|
company_linkedin_url: str | None = None
|
|
34
36
|
total_employees: int | None = None
|
|
35
37
|
distribution: list[EmploymentDistributionItem] = []
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class DepartmentDistributionItem(BlitzModel):
|
|
41
|
+
"""Employee count for a single department (Blitz job function).
|
|
42
|
+
|
|
43
|
+
Employees with no classified department are counted under ``"Other"``.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
department: str | None = None
|
|
47
|
+
count: int | None = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CompanyDepartmentDistributionResponse(BlitzModel):
|
|
51
|
+
"""Result of ``utils.company_department_distribution``."""
|
|
52
|
+
|
|
53
|
+
company_linkedin_url: str | None = None
|
|
54
|
+
total_employees: int | None = None
|
|
55
|
+
distribution: list[DepartmentDistributionItem] = []
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.0" # x-release-please-version
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|