blitz-api-py 0.1.0__py3-none-any.whl
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/__init__.py +65 -0
- blitz_api/_base_client.py +191 -0
- blitz_api/_client.py +13 -0
- blitz_api/_client_async.py +143 -0
- blitz_api/_client_sync.py +145 -0
- blitz_api/_compat.py +26 -0
- blitz_api/_constants.py +36 -0
- blitz_api/_exceptions.py +113 -0
- blitz_api/_pagination_async.py +128 -0
- blitz_api/_pagination_base.py +52 -0
- blitz_api/_pagination_sync.py +130 -0
- blitz_api/_rate_limit.py +14 -0
- blitz_api/_rate_limit_async.py +67 -0
- blitz_api/_rate_limit_sync.py +69 -0
- blitz_api/_version.py +1 -0
- blitz_api/py.typed +0 -0
- blitz_api/resources/__init__.py +27 -0
- blitz_api/resources/_async/__init__.py +1 -0
- blitz_api/resources/_async/account.py +27 -0
- blitz_api/resources/_async/enrichment.py +116 -0
- blitz_api/resources/_async/search.py +153 -0
- blitz_api/resources/_async/utils.py +43 -0
- blitz_api/resources/_sync/__init__.py +3 -0
- blitz_api/resources/_sync/account.py +29 -0
- blitz_api/resources/_sync/enrichment.py +118 -0
- blitz_api/resources/_sync/search.py +155 -0
- blitz_api/resources/_sync/utils.py +45 -0
- blitz_api/types/__init__.py +108 -0
- blitz_api/types/_models.py +23 -0
- blitz_api/types/account.py +27 -0
- blitz_api/types/enrichment.py +76 -0
- blitz_api/types/enums.py +633 -0
- blitz_api/types/filters.py +130 -0
- blitz_api/types/search.py +36 -0
- blitz_api/types/shared.py +119 -0
- blitz_api/types/utils.py +35 -0
- blitz_api_py-0.1.0.dist-info/METADATA +220 -0
- blitz_api_py-0.1.0.dist-info/RECORD +40 -0
- blitz_api_py-0.1.0.dist-info/WHEEL +4 -0
- blitz_api_py-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""Typed request structures (``TypedDict``) for the search endpoints.
|
|
2
|
+
|
|
3
|
+
These give call-site autocomplete and static checking for the nested filter
|
|
4
|
+
objects accepted by ``search.people`` and ``search.companies``. Enum-constrained
|
|
5
|
+
fields accept either an enum member (autocompleted, e.g. ``Industry.BANKING``) or
|
|
6
|
+
a raw string, so power users are never blocked.
|
|
7
|
+
|
|
8
|
+
All keys are optional unless noted; omit a filter to leave it unset.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing_extensions import NotRequired, TypedDict
|
|
14
|
+
|
|
15
|
+
from .enums import (
|
|
16
|
+
CompanyType,
|
|
17
|
+
Continent,
|
|
18
|
+
EmployeeRange,
|
|
19
|
+
Industry,
|
|
20
|
+
JobFunction,
|
|
21
|
+
JobLevel,
|
|
22
|
+
SalesRegion,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Each accepts an enum member (autocompleted) or a raw string, so callers are
|
|
26
|
+
# never blocked by a value missing from the vendored taxonomy. Note: because the
|
|
27
|
+
# enums subclass ``str``, ``Enum | str`` collapses to ``str`` for the type checker, so
|
|
28
|
+
# the taxonomy is an autocomplete aid, not a statically enforced constraint — a typo'd
|
|
29
|
+
# value is sent as-is and rejected (or ignored) by the API rather than caught by mypy.
|
|
30
|
+
IndustryValue = Industry | str
|
|
31
|
+
CompanyTypeValue = CompanyType | str
|
|
32
|
+
EmployeeRangeValue = EmployeeRange | str
|
|
33
|
+
ContinentValue = Continent | str
|
|
34
|
+
SalesRegionValue = SalesRegion | str
|
|
35
|
+
JobFunctionValue = JobFunction | str
|
|
36
|
+
JobLevelValue = JobLevel | str
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class KeywordFilter(TypedDict, total=False):
|
|
40
|
+
"""Free-text include/exclude keyword filter."""
|
|
41
|
+
|
|
42
|
+
include: list[str]
|
|
43
|
+
exclude: list[str]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class IndustryFilter(TypedDict, total=False):
|
|
47
|
+
"""Include/exclude filter over the fixed industry taxonomy."""
|
|
48
|
+
|
|
49
|
+
include: list[IndustryValue]
|
|
50
|
+
exclude: list[IndustryValue]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class CompanyTypeFilter(TypedDict, total=False):
|
|
54
|
+
"""Include/exclude filter over company types."""
|
|
55
|
+
|
|
56
|
+
include: list[CompanyTypeValue]
|
|
57
|
+
exclude: list[CompanyTypeValue]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class RangeFilter(TypedDict, total=False):
|
|
61
|
+
"""Numeric range filter. ``0`` means unset for most fields."""
|
|
62
|
+
|
|
63
|
+
min: float
|
|
64
|
+
max: float
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class CompanyHQFilter(TypedDict, total=False):
|
|
68
|
+
"""Headquarters-location filter for company search."""
|
|
69
|
+
|
|
70
|
+
city: KeywordFilter
|
|
71
|
+
country_code: list[str]
|
|
72
|
+
continent: list[ContinentValue]
|
|
73
|
+
sales_region: list[SalesRegionValue]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class CompanyFilter(TypedDict, total=False):
|
|
77
|
+
"""Company search criteria, shared by ``search.companies`` and ``search.people``."""
|
|
78
|
+
|
|
79
|
+
linkedin_url: list[str]
|
|
80
|
+
name: KeywordFilter
|
|
81
|
+
industry: IndustryFilter
|
|
82
|
+
type: CompanyTypeFilter
|
|
83
|
+
employee_range: list[EmployeeRangeValue]
|
|
84
|
+
employee_count: RangeFilter
|
|
85
|
+
min_linkedin_followers: int
|
|
86
|
+
revenue: RangeFilter
|
|
87
|
+
naics_code: KeywordFilter
|
|
88
|
+
sic_code: KeywordFilter
|
|
89
|
+
web_traffic: RangeFilter
|
|
90
|
+
ad_spend: RangeFilter
|
|
91
|
+
keywords: KeywordFilter
|
|
92
|
+
founded_year: RangeFilter
|
|
93
|
+
hq: CompanyHQFilter
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class PeopleJobTitleFilter(TypedDict, total=False):
|
|
97
|
+
"""Job-title filter. Wrap a value in ``[brackets]`` for an exact match."""
|
|
98
|
+
|
|
99
|
+
include_linkedin_headline: bool
|
|
100
|
+
include: list[str]
|
|
101
|
+
exclude: list[str]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class PeopleLocationFilter(TypedDict, total=False):
|
|
105
|
+
"""Location filter for the people side of a people search."""
|
|
106
|
+
|
|
107
|
+
city: list[str]
|
|
108
|
+
country_code: list[str]
|
|
109
|
+
continent: list[ContinentValue]
|
|
110
|
+
sales_region: list[SalesRegionValue]
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class PeopleFilter(TypedDict, total=False):
|
|
114
|
+
"""People search criteria for ``search.people``."""
|
|
115
|
+
|
|
116
|
+
job_title: PeopleJobTitleFilter
|
|
117
|
+
job_function: list[JobFunctionValue]
|
|
118
|
+
job_level: list[JobLevelValue]
|
|
119
|
+
min_connections: int
|
|
120
|
+
location: PeopleLocationFilter
|
|
121
|
+
education: KeywordFilter
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class CascadeTier(TypedDict):
|
|
125
|
+
"""One tier of a waterfall ICP cascade, tried in order until results are found."""
|
|
126
|
+
|
|
127
|
+
include_title: list[str]
|
|
128
|
+
location: list[str]
|
|
129
|
+
include_headline_search: bool
|
|
130
|
+
exclude_title: NotRequired[list[str]]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Response models for the Search resource.
|
|
2
|
+
|
|
3
|
+
``search.people``/``search.companies`` return ``CursorPage[...]`` and
|
|
4
|
+
``search.employee_finder`` returns ``PageNumberPage[...]`` (auto-paging page objects
|
|
5
|
+
defined in :mod:`blitz_api._pagination_async` / ``_pagination_sync``), so those
|
|
6
|
+
per-endpoint response models no longer live here. Only the non-paginated waterfall
|
|
7
|
+
result does.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from ._models import BlitzModel
|
|
13
|
+
from .shared import Person
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"WaterfallIcpMatch",
|
|
17
|
+
"WaterfallIcpResponse",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class WaterfallIcpMatch(BlitzModel):
|
|
22
|
+
"""A single match from a waterfall ICP search.
|
|
23
|
+
|
|
24
|
+
``icp`` is the cascade tier that matched (1 = highest priority) and
|
|
25
|
+
``ranking`` is the overall relevance within the company (1 = most relevant).
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
icp: int | None = None
|
|
29
|
+
ranking: int | None = None
|
|
30
|
+
person: Person | None = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class WaterfallIcpResponse(BlitzModel):
|
|
34
|
+
"""Result of ``search.waterfall_icp``."""
|
|
35
|
+
|
|
36
|
+
results: list[WaterfallIcpMatch] = []
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"""Response models shared across multiple Blitz API endpoints.
|
|
2
|
+
|
|
3
|
+
These mirror the JSON the API returns. Field shapes vary slightly between
|
|
4
|
+
endpoints (e.g. ``Experience.company_name`` is only populated by people search,
|
|
5
|
+
``HQ.postcode``/``street`` only by company enrichment), so divergent fields are
|
|
6
|
+
modeled as ``Optional`` on a single superset type rather than duplicated.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from ._models import BlitzModel
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Location",
|
|
15
|
+
"Experience",
|
|
16
|
+
"Education",
|
|
17
|
+
"Certification",
|
|
18
|
+
"Person",
|
|
19
|
+
"HQ",
|
|
20
|
+
"Company",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Location(BlitzModel):
|
|
25
|
+
"""A geographic location attached to a person or a job."""
|
|
26
|
+
|
|
27
|
+
city: str | None = None
|
|
28
|
+
state_code: str | None = None
|
|
29
|
+
country_code: str | None = None
|
|
30
|
+
continent: str | None = None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Experience(BlitzModel):
|
|
34
|
+
"""A single role from a person's work history."""
|
|
35
|
+
|
|
36
|
+
job_title: str | None = None
|
|
37
|
+
# Populated by ``search.people``; absent from employee-finder / reverse lookups.
|
|
38
|
+
company_name: str | None = None
|
|
39
|
+
company_linkedin_url: str | None = None
|
|
40
|
+
company_linkedin_id: str | None = None
|
|
41
|
+
company_domain: str | None = None
|
|
42
|
+
job_description: str | None = None
|
|
43
|
+
job_start_date: str | None = None
|
|
44
|
+
job_end_date: str | None = None
|
|
45
|
+
job_is_current: bool | None = None
|
|
46
|
+
job_location: Location | None = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Education(BlitzModel):
|
|
50
|
+
"""A single education entry from a person's profile."""
|
|
51
|
+
|
|
52
|
+
school: str | None = None
|
|
53
|
+
degree: str | None = None
|
|
54
|
+
start_date: str | None = None
|
|
55
|
+
end_date: str | None = None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Certification(BlitzModel):
|
|
59
|
+
"""A professional certification listed on a person's profile."""
|
|
60
|
+
|
|
61
|
+
authority: str | None = None
|
|
62
|
+
name: str | None = None
|
|
63
|
+
url: str | None = None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class Person(BlitzModel):
|
|
67
|
+
"""A person profile returned by search and reverse-enrichment endpoints."""
|
|
68
|
+
|
|
69
|
+
first_name: str | None = None
|
|
70
|
+
last_name: str | None = None
|
|
71
|
+
full_name: str | None = None
|
|
72
|
+
nickname: str | None = None
|
|
73
|
+
civility_title: str | None = None
|
|
74
|
+
headline: str | None = None
|
|
75
|
+
about_me: str | None = None
|
|
76
|
+
location: Location | None = None
|
|
77
|
+
linkedin_url: str | None = None
|
|
78
|
+
connections_count: int | None = None
|
|
79
|
+
profile_picture_url: str | None = None
|
|
80
|
+
experiences: list[Experience] = []
|
|
81
|
+
education: list[Education] = []
|
|
82
|
+
skills: list[str] = []
|
|
83
|
+
certifications: list[Certification] = []
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class HQ(BlitzModel):
|
|
87
|
+
"""A company's headquarters location.
|
|
88
|
+
|
|
89
|
+
Company enrichment returns ``postcode`` and ``street`` in addition to the
|
|
90
|
+
fields company search returns; both are optional here.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
city: str | None = None
|
|
94
|
+
state: str | None = None
|
|
95
|
+
postcode: str | None = None
|
|
96
|
+
country_code: str | None = None
|
|
97
|
+
country_name: str | None = None
|
|
98
|
+
region: str | None = None
|
|
99
|
+
continent: str | None = None
|
|
100
|
+
street: str | None = None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class Company(BlitzModel):
|
|
104
|
+
"""A company profile returned by company search and company enrichment."""
|
|
105
|
+
|
|
106
|
+
linkedin_url: str | None = None
|
|
107
|
+
linkedin_id: int | None = None
|
|
108
|
+
name: str | None = None
|
|
109
|
+
about: str | None = None
|
|
110
|
+
specialties: list[str] | None = None
|
|
111
|
+
industry: str | None = None
|
|
112
|
+
type: str | None = None
|
|
113
|
+
size: str | None = None
|
|
114
|
+
employees_on_linkedin: int | None = None
|
|
115
|
+
followers: int | None = None
|
|
116
|
+
founded_year: int | None = None
|
|
117
|
+
hq: HQ | None = None
|
|
118
|
+
domain: str | None = None
|
|
119
|
+
website: str | None = None
|
blitz_api/types/utils.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Response models for the Utilities resource."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ._models import BlitzModel
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"CurrentDateResponse",
|
|
9
|
+
"EmploymentDistributionItem",
|
|
10
|
+
"CompanyEmploymentDistributionResponse",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CurrentDateResponse(BlitzModel):
|
|
15
|
+
"""Result of ``utils.current_date``."""
|
|
16
|
+
|
|
17
|
+
datetime: str | None = None
|
|
18
|
+
timestamp: int | None = None
|
|
19
|
+
timezone: str | None = None
|
|
20
|
+
timezone_name: str | None = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class EmploymentDistributionItem(BlitzModel):
|
|
24
|
+
"""Employee count for a single country."""
|
|
25
|
+
|
|
26
|
+
country: str | None = None
|
|
27
|
+
count: int | None = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CompanyEmploymentDistributionResponse(BlitzModel):
|
|
31
|
+
"""Result of ``utils.company_employment_distribution``."""
|
|
32
|
+
|
|
33
|
+
company_linkedin_url: str | None = None
|
|
34
|
+
total_employees: int | None = None
|
|
35
|
+
distribution: list[EmploymentDistributionItem] = []
|
|
@@ -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,40 @@
|
|
|
1
|
+
blitz_api/__init__.py,sha256=zkPoN9id23GMbR-r6d4e-twmwd1yL2zp92MOKJjdOUs,1546
|
|
2
|
+
blitz_api/_base_client.py,sha256=SRkyPmxD8Tl4-N_3IRjmWB7S4AvTqLl2HBXkA4yB5TQ,7006
|
|
3
|
+
blitz_api/_client.py,sha256=KvsDperoLthofE3T35atm7pzcAfr6FvaFqvOcR5kQCI,500
|
|
4
|
+
blitz_api/_client_async.py,sha256=7xYjsjkmhiK7_j4IuWopvtpSklg2Xbpuo_8Rw3B80EY,5099
|
|
5
|
+
blitz_api/_client_sync.py,sha256=i3lA0WL0_ehnfXWLarlXRzGFIjfteg-AE0PbRzaMYoY,5098
|
|
6
|
+
blitz_api/_compat.py,sha256=zAQwSfCQbC-zzHGyUHCvRQXNf4Eao-7tCgzaaVJI-So,1113
|
|
7
|
+
blitz_api/_constants.py,sha256=Bd-HojCZNBDVS5v295vunawszR43wRO44HFmmS_VnVQ,1322
|
|
8
|
+
blitz_api/_exceptions.py,sha256=doWnsQbd5XaQqXB2pqgMseeYwwPWHETugzlMzJJEWeg,3798
|
|
9
|
+
blitz_api/_pagination_async.py,sha256=e2MlTRh32gQSaaq-RqRB2Ocy0VQDSkSA4XSg_tI1id8,4746
|
|
10
|
+
blitz_api/_pagination_base.py,sha256=3kVPUkEIz4G74j2AJLwhqNmnyekbT4S-3mG2-PvwL1U,1920
|
|
11
|
+
blitz_api/_pagination_sync.py,sha256=mqt6iA8Dits3RK_T3TX2JLK1dcE0yAU93VKDGaBBU9U,4823
|
|
12
|
+
blitz_api/_rate_limit.py,sha256=cm6P1pWWdS7gc0Br-rVJ6MuMlRhWGluVDBq--cGUUdI,578
|
|
13
|
+
blitz_api/_rate_limit_async.py,sha256=kcG31uHWo3exMZDzX9wFVeTU-LccJWqF_38ELN_Vqek,2711
|
|
14
|
+
blitz_api/_rate_limit_sync.py,sha256=gCqLYCsXh8-m5sVOuntGEUjskavT1Uyh_IY7S-OBky8,2863
|
|
15
|
+
blitz_api/_version.py,sha256=IOEzhCbX916JIkvZr03GFY4bl9YqSkfjdqLO4qJ2FOs,50
|
|
16
|
+
blitz_api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
|
+
blitz_api/resources/__init__.py,sha256=mWslhutXjoMVu53ANbvreG53epF_l0R-gvKMlBzZ0YA,856
|
|
18
|
+
blitz_api/resources/_async/__init__.py,sha256=fayqNuA3x517MqMUeDrjrsAcgw-3Gkz7E0d7sZX2HeA,69
|
|
19
|
+
blitz_api/resources/_async/account.py,sha256=-vn2XZv0HLixsAYgO9gHl_FsLvh9u3f9az7Kpy4parM,758
|
|
20
|
+
blitz_api/resources/_async/enrichment.py,sha256=t08fk4M1Jue6M4ZMWBvxSAwkVDkF-FiBXFxkxM8PP0M,3862
|
|
21
|
+
blitz_api/resources/_async/search.py,sha256=Ncyj0O17Pe3qI_KoONmX1pX149oYsZb4QA-qfQm5gDE,5224
|
|
22
|
+
blitz_api/resources/_async/utils.py,sha256=bUO0OgAT9nhq7kvfHK1mH8PLMEvqJI70ZFDErs-BHQA,1453
|
|
23
|
+
blitz_api/resources/_sync/__init__.py,sha256=bA0UT2ZHjIc6IlkaFDVk_Ii-Z_Y66-Zse_aADGAHQLw,253
|
|
24
|
+
blitz_api/resources/_sync/account.py,sha256=l6y-zT2bucXNw7Ahq0JDgBLj1z7-uBk2TkZQGjmIiCw,914
|
|
25
|
+
blitz_api/resources/_sync/enrichment.py,sha256=gDLSEs7OVzeP2rD34UGGjH0bhv4K1kWf61eeSz5iE0g,3949
|
|
26
|
+
blitz_api/resources/_sync/search.py,sha256=WxiYS-_59d2jv6Zgv825ot4347ic8_xvl-sBs26RE28,5302
|
|
27
|
+
blitz_api/resources/_sync/utils.py,sha256=iRDdno1LVceUokCmnn24gtEIcuuSmh6FSrNhgY6MvtM,1595
|
|
28
|
+
blitz_api/types/__init__.py,sha256=IIJtCbDf8siDZTJrepph6N1k7KQ1dwAoaiXkxwErO_4,2257
|
|
29
|
+
blitz_api/types/_models.py,sha256=ap1K9KVVdO_9cvnKYC8YElZUYfNKYRgs3WArd3NJ-dY,752
|
|
30
|
+
blitz_api/types/account.py,sha256=DZ9x7Mx5ad8AJCgZ5y8nnYOuANNeDrs9brzezmg69XQ,702
|
|
31
|
+
blitz_api/types/enrichment.py,sha256=ymNXS_j4oi6oKSe87787wQC1mvhUc-zUK0Iorb22sUU,2036
|
|
32
|
+
blitz_api/types/enums.py,sha256=91PyiqGSfY39h774HbLo6IHY0Uxzc0l37bBWKpQTsDE,34617
|
|
33
|
+
blitz_api/types/filters.py,sha256=F-XoRSOFxovWgGE03cvK-MvsJbdOcsT6uSTuu4WEIj4,3805
|
|
34
|
+
blitz_api/types/search.py,sha256=-BSEfeLyVb_GR8YICeLHecA-AM5tvWpS8mlmH9X194I,1019
|
|
35
|
+
blitz_api/types/shared.py,sha256=1oJriRNR8xEAEJnZtXqviH1l6C1Sl8_LHh0M_udTUFs,3446
|
|
36
|
+
blitz_api/types/utils.py,sha256=VYzPZy2p8GfXnETQ1gPCqKPT4LrCvUal_oTtcJ9LBdQ,878
|
|
37
|
+
blitz_api_py-0.1.0.dist-info/METADATA,sha256=uLaH_JG_zedmpu2Bv_EoSpzCM2NzGQqfEMJi_Hp7J-o,7829
|
|
38
|
+
blitz_api_py-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
39
|
+
blitz_api_py-0.1.0.dist-info/licenses/LICENSE,sha256=mTMTtchY-ZrmXdm9MUKXRPzPi7eR2CE2BA5Ocm9qw0M,1066
|
|
40
|
+
blitz_api_py-0.1.0.dist-info/RECORD,,
|
|
@@ -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.
|