easydata-api 1.0.1__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.
- easydata_api-1.0.1/LICENSE +21 -0
- easydata_api-1.0.1/PKG-INFO +185 -0
- easydata_api-1.0.1/README.md +164 -0
- easydata_api-1.0.1/easydata_api/__init__.py +79 -0
- easydata_api-1.0.1/easydata_api/client.py +642 -0
- easydata_api-1.0.1/easydata_api/errors.py +199 -0
- easydata_api-1.0.1/easydata_api/webhooks.py +234 -0
- easydata_api-1.0.1/easydata_api.egg-info/PKG-INFO +185 -0
- easydata_api-1.0.1/easydata_api.egg-info/SOURCES.txt +13 -0
- easydata_api-1.0.1/easydata_api.egg-info/dependency_links.txt +1 -0
- easydata_api-1.0.1/easydata_api.egg-info/requires.txt +3 -0
- easydata_api-1.0.1/easydata_api.egg-info/top_level.txt +1 -0
- easydata_api-1.0.1/pyproject.toml +38 -0
- easydata_api-1.0.1/setup.cfg +4 -0
- easydata_api-1.0.1/tests/test_client.py +347 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EasyData
|
|
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,185 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: easydata-api
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Official Python client for the EasyData LinkedIn enrichment API
|
|
5
|
+
Author-email: EasyData Development <dev@easydata.win>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://easydata.win
|
|
8
|
+
Project-URL: Documentation, https://easydata.win/docs/api
|
|
9
|
+
Project-URL: Reference, https://easydata.win/openapi.yaml
|
|
10
|
+
Keywords: linkedin,enrichment,data,api,scraping
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Provides-Extra: ed25519
|
|
19
|
+
Requires-Dist: cryptography>=42; extra == "ed25519"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# EasyData for Python
|
|
23
|
+
|
|
24
|
+
The official client for the [EasyData](https://easydata.win) LinkedIn
|
|
25
|
+
enrichment API.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install easydata-api
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Zero dependencies. The client is `urllib` and the webhook HMAC is
|
|
32
|
+
`hmac`/`hashlib`, both standard library - an enrichment script should not drag a
|
|
33
|
+
dependency tree in behind it. Only the asymmetric webhook scheme needs an extra
|
|
34
|
+
(`pip install easydata-api[ed25519]`), and most receivers never use it.
|
|
35
|
+
|
|
36
|
+
## One record
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from easydata_api import EasyData
|
|
40
|
+
|
|
41
|
+
ed = EasyData() # reads EASYDATA_API_KEY
|
|
42
|
+
|
|
43
|
+
r = ed.profiles_enrich.sync("https://linkedin.com/in/satyanadella")
|
|
44
|
+
print(r.result.data["full_name"])
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`.sync()` takes **one** target and answers with **one** record, in the response.
|
|
48
|
+
It costs double, it accepts no webhooks and no `enrich`, and for a paged
|
|
49
|
+
operation it returns one upstream page. Those bounds are refusals, not quiet
|
|
50
|
+
downgrades.
|
|
51
|
+
|
|
52
|
+
If the deadline expires first you get `complete=False` and a real `batch_id`,
|
|
53
|
+
never a 504 - so you keep the handle to results you may already have been
|
|
54
|
+
charged for.
|
|
55
|
+
|
|
56
|
+
## A batch
|
|
57
|
+
|
|
58
|
+
Everything is a batch, including a batch of one. There is no ceiling to discover
|
|
59
|
+
between one target and fifty thousand.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
batch = ed.profiles_enrich(urls, external_id="crm-sync", find_emails=True)
|
|
63
|
+
|
|
64
|
+
for entry in ed.results(batch.batch_id):
|
|
65
|
+
if entry.ok:
|
|
66
|
+
save(entry.data)
|
|
67
|
+
else:
|
|
68
|
+
log(entry.input, entry.error["type"]) # and credits_used is 0
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`results()` is a generator over the cursor. Results **stream**: it yields rows
|
|
72
|
+
while the batch is still processing, so a long batch starts producing
|
|
73
|
+
immediately rather than after it finishes. It holds the cursor open until the
|
|
74
|
+
batch reaches a terminal state, sleeping for the server's own poll interval
|
|
75
|
+
between empty reads.
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
list(ed.results(batch_id, wait=False)) # what is readable right now
|
|
79
|
+
ed.wait(batch_id, timeout=600) # the counters, not the rows
|
|
80
|
+
ed.profiles_enrich.collect(urls) # submit and drain, in one call
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Retries and double-billing
|
|
84
|
+
|
|
85
|
+
Retries are on by default and cover exactly what is safe to repeat: `429`, `5xx`
|
|
86
|
+
and a transport failure. A `4xx` is raised immediately, because repeating a
|
|
87
|
+
refusal only spends the rate budget on a certain no.
|
|
88
|
+
|
|
89
|
+
Every submission carries an `Idempotency-Key` that the client mints, so the
|
|
90
|
+
retry is free: a retried submit resolves to the batch the first attempt created
|
|
91
|
+
rather than creating a second one and charging for it. Pass your own
|
|
92
|
+
`idempotency_key=` if your caller's retry needs the same guarantee.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
ed = EasyData(max_retries=5, timeout=180)
|
|
96
|
+
ed.rate_limits.remaining # read off the last response, including a 429
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`rate_limits` fields are `None` when the deployment publishes no ceiling. That
|
|
100
|
+
is what "no limit" looks like on the wire: an absent header, never a zero.
|
|
101
|
+
|
|
102
|
+
## Errors
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from easydata_api import QuotaExhausted, RateLimited, InvalidRequest, EasyDataError
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
ed.profiles_enrich(urls)
|
|
109
|
+
except InvalidRequest as e:
|
|
110
|
+
print(e.field, e.request_id) # quote request_id at support
|
|
111
|
+
except QuotaExhausted:
|
|
112
|
+
... # waiting for the month is the fix
|
|
113
|
+
except EasyDataError as e:
|
|
114
|
+
print(e.type, e.status)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
One class per `error.type`, all under `EasyDataError`. An `error.type` this
|
|
118
|
+
version has never heard of becomes a plain `EasyDataError` carrying that string
|
|
119
|
+
rather than a crash.
|
|
120
|
+
|
|
121
|
+
`EmailUnverified` is a `403` and is **not** a spent allowance: the default
|
|
122
|
+
allowance is gated on somebody having confirmed their email address, and
|
|
123
|
+
clicking the link fixes it.
|
|
124
|
+
|
|
125
|
+
## Webhooks
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from easydata_api import verify, VerificationError
|
|
129
|
+
|
|
130
|
+
@app.post("/webhooks/easydata")
|
|
131
|
+
def hook():
|
|
132
|
+
try:
|
|
133
|
+
d = verify(SECRET, request.headers, request.get_data())
|
|
134
|
+
except VerificationError:
|
|
135
|
+
return "", 400
|
|
136
|
+
|
|
137
|
+
if seen(d.id): # stable across every retry of the same delivery
|
|
138
|
+
return "", 200
|
|
139
|
+
|
|
140
|
+
if d.event == "batch.result":
|
|
141
|
+
handle(d.data["result"])
|
|
142
|
+
elif d.event == "batch.completed":
|
|
143
|
+
finish(d.data["batch_id"])
|
|
144
|
+
return "", 200
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Four events: `batch.started`, `batch.result` (one per row), `batch.completed`
|
|
148
|
+
and `batch.failed`. The fields are in `d.data`, one level down.
|
|
149
|
+
|
|
150
|
+
Two things that are silent when you get them wrong:
|
|
151
|
+
|
|
152
|
+
- **Verify against the raw body.** Re-serialising parsed JSON changes key order
|
|
153
|
+
and whitespace, and the signature will not match a body you rebuilt.
|
|
154
|
+
- **Never verify against `X-EasyData-Timestamp`.** It sits outside both signed
|
|
155
|
+
messages, so a replayed delivery can set it to anything. The `t` inside the
|
|
156
|
+
signature header is the only copy that cannot be edited.
|
|
157
|
+
|
|
158
|
+
For the asymmetric scheme, `webhook_id` is required - one key signs for every
|
|
159
|
+
customer, so checking `wid` is what stops another customer's genuine delivery
|
|
160
|
+
verifying against your receiver.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from easydata_api import verify_ed25519
|
|
164
|
+
d = verify_ed25519(PUBLIC_KEY, request.headers, body, webhook_id=MY_ENDPOINT_ID)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Everything else
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
ed.batch(batch_id) # one batch's state
|
|
171
|
+
ed.batches(status="completed", external_id="crm") # your batches
|
|
172
|
+
ed.cancel(batch_id) # delivered rows stay charged
|
|
173
|
+
ed.account() # allowance, ceilings, secret
|
|
174
|
+
ed.usage(from_="2026-09-01", to="2026-09-30") # spend by day and operation
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Operations are attributes: `profiles_enrich`, `profiles_activity`,
|
|
178
|
+
`profiles_posts`, `profiles_comments`, `profiles_reactions`, `companies_enrich`,
|
|
179
|
+
`posts_enrich`, `sales_search_people`, `sales_search_companies`.
|
|
180
|
+
|
|
181
|
+
## Reference
|
|
182
|
+
|
|
183
|
+
- [API reference](https://easydata.win/docs/api)
|
|
184
|
+
- [The batch model](https://easydata.win/docs/batches) - what a credit is, and why
|
|
185
|
+
- [OpenAPI 3.1](https://easydata.win/openapi.yaml)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# EasyData for Python
|
|
2
|
+
|
|
3
|
+
The official client for the [EasyData](https://easydata.win) LinkedIn
|
|
4
|
+
enrichment API.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install easydata-api
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Zero dependencies. The client is `urllib` and the webhook HMAC is
|
|
11
|
+
`hmac`/`hashlib`, both standard library - an enrichment script should not drag a
|
|
12
|
+
dependency tree in behind it. Only the asymmetric webhook scheme needs an extra
|
|
13
|
+
(`pip install easydata-api[ed25519]`), and most receivers never use it.
|
|
14
|
+
|
|
15
|
+
## One record
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from easydata_api import EasyData
|
|
19
|
+
|
|
20
|
+
ed = EasyData() # reads EASYDATA_API_KEY
|
|
21
|
+
|
|
22
|
+
r = ed.profiles_enrich.sync("https://linkedin.com/in/satyanadella")
|
|
23
|
+
print(r.result.data["full_name"])
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`.sync()` takes **one** target and answers with **one** record, in the response.
|
|
27
|
+
It costs double, it accepts no webhooks and no `enrich`, and for a paged
|
|
28
|
+
operation it returns one upstream page. Those bounds are refusals, not quiet
|
|
29
|
+
downgrades.
|
|
30
|
+
|
|
31
|
+
If the deadline expires first you get `complete=False` and a real `batch_id`,
|
|
32
|
+
never a 504 - so you keep the handle to results you may already have been
|
|
33
|
+
charged for.
|
|
34
|
+
|
|
35
|
+
## A batch
|
|
36
|
+
|
|
37
|
+
Everything is a batch, including a batch of one. There is no ceiling to discover
|
|
38
|
+
between one target and fifty thousand.
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
batch = ed.profiles_enrich(urls, external_id="crm-sync", find_emails=True)
|
|
42
|
+
|
|
43
|
+
for entry in ed.results(batch.batch_id):
|
|
44
|
+
if entry.ok:
|
|
45
|
+
save(entry.data)
|
|
46
|
+
else:
|
|
47
|
+
log(entry.input, entry.error["type"]) # and credits_used is 0
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`results()` is a generator over the cursor. Results **stream**: it yields rows
|
|
51
|
+
while the batch is still processing, so a long batch starts producing
|
|
52
|
+
immediately rather than after it finishes. It holds the cursor open until the
|
|
53
|
+
batch reaches a terminal state, sleeping for the server's own poll interval
|
|
54
|
+
between empty reads.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
list(ed.results(batch_id, wait=False)) # what is readable right now
|
|
58
|
+
ed.wait(batch_id, timeout=600) # the counters, not the rows
|
|
59
|
+
ed.profiles_enrich.collect(urls) # submit and drain, in one call
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Retries and double-billing
|
|
63
|
+
|
|
64
|
+
Retries are on by default and cover exactly what is safe to repeat: `429`, `5xx`
|
|
65
|
+
and a transport failure. A `4xx` is raised immediately, because repeating a
|
|
66
|
+
refusal only spends the rate budget on a certain no.
|
|
67
|
+
|
|
68
|
+
Every submission carries an `Idempotency-Key` that the client mints, so the
|
|
69
|
+
retry is free: a retried submit resolves to the batch the first attempt created
|
|
70
|
+
rather than creating a second one and charging for it. Pass your own
|
|
71
|
+
`idempotency_key=` if your caller's retry needs the same guarantee.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
ed = EasyData(max_retries=5, timeout=180)
|
|
75
|
+
ed.rate_limits.remaining # read off the last response, including a 429
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`rate_limits` fields are `None` when the deployment publishes no ceiling. That
|
|
79
|
+
is what "no limit" looks like on the wire: an absent header, never a zero.
|
|
80
|
+
|
|
81
|
+
## Errors
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from easydata_api import QuotaExhausted, RateLimited, InvalidRequest, EasyDataError
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
ed.profiles_enrich(urls)
|
|
88
|
+
except InvalidRequest as e:
|
|
89
|
+
print(e.field, e.request_id) # quote request_id at support
|
|
90
|
+
except QuotaExhausted:
|
|
91
|
+
... # waiting for the month is the fix
|
|
92
|
+
except EasyDataError as e:
|
|
93
|
+
print(e.type, e.status)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
One class per `error.type`, all under `EasyDataError`. An `error.type` this
|
|
97
|
+
version has never heard of becomes a plain `EasyDataError` carrying that string
|
|
98
|
+
rather than a crash.
|
|
99
|
+
|
|
100
|
+
`EmailUnverified` is a `403` and is **not** a spent allowance: the default
|
|
101
|
+
allowance is gated on somebody having confirmed their email address, and
|
|
102
|
+
clicking the link fixes it.
|
|
103
|
+
|
|
104
|
+
## Webhooks
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from easydata_api import verify, VerificationError
|
|
108
|
+
|
|
109
|
+
@app.post("/webhooks/easydata")
|
|
110
|
+
def hook():
|
|
111
|
+
try:
|
|
112
|
+
d = verify(SECRET, request.headers, request.get_data())
|
|
113
|
+
except VerificationError:
|
|
114
|
+
return "", 400
|
|
115
|
+
|
|
116
|
+
if seen(d.id): # stable across every retry of the same delivery
|
|
117
|
+
return "", 200
|
|
118
|
+
|
|
119
|
+
if d.event == "batch.result":
|
|
120
|
+
handle(d.data["result"])
|
|
121
|
+
elif d.event == "batch.completed":
|
|
122
|
+
finish(d.data["batch_id"])
|
|
123
|
+
return "", 200
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Four events: `batch.started`, `batch.result` (one per row), `batch.completed`
|
|
127
|
+
and `batch.failed`. The fields are in `d.data`, one level down.
|
|
128
|
+
|
|
129
|
+
Two things that are silent when you get them wrong:
|
|
130
|
+
|
|
131
|
+
- **Verify against the raw body.** Re-serialising parsed JSON changes key order
|
|
132
|
+
and whitespace, and the signature will not match a body you rebuilt.
|
|
133
|
+
- **Never verify against `X-EasyData-Timestamp`.** It sits outside both signed
|
|
134
|
+
messages, so a replayed delivery can set it to anything. The `t` inside the
|
|
135
|
+
signature header is the only copy that cannot be edited.
|
|
136
|
+
|
|
137
|
+
For the asymmetric scheme, `webhook_id` is required - one key signs for every
|
|
138
|
+
customer, so checking `wid` is what stops another customer's genuine delivery
|
|
139
|
+
verifying against your receiver.
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from easydata_api import verify_ed25519
|
|
143
|
+
d = verify_ed25519(PUBLIC_KEY, request.headers, body, webhook_id=MY_ENDPOINT_ID)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Everything else
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
ed.batch(batch_id) # one batch's state
|
|
150
|
+
ed.batches(status="completed", external_id="crm") # your batches
|
|
151
|
+
ed.cancel(batch_id) # delivered rows stay charged
|
|
152
|
+
ed.account() # allowance, ceilings, secret
|
|
153
|
+
ed.usage(from_="2026-09-01", to="2026-09-30") # spend by day and operation
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Operations are attributes: `profiles_enrich`, `profiles_activity`,
|
|
157
|
+
`profiles_posts`, `profiles_comments`, `profiles_reactions`, `companies_enrich`,
|
|
158
|
+
`posts_enrich`, `sales_search_people`, `sales_search_companies`.
|
|
159
|
+
|
|
160
|
+
## Reference
|
|
161
|
+
|
|
162
|
+
- [API reference](https://easydata.win/docs/api)
|
|
163
|
+
- [The batch model](https://easydata.win/docs/batches) - what a credit is, and why
|
|
164
|
+
- [OpenAPI 3.1](https://easydata.win/openapi.yaml)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""EasyData - LinkedIn data enrichment.
|
|
2
|
+
|
|
3
|
+
from easydata_api import EasyData
|
|
4
|
+
|
|
5
|
+
ed = EasyData() # reads EASYDATA_API_KEY
|
|
6
|
+
|
|
7
|
+
# One record, in this call, at twice the credits.
|
|
8
|
+
r = ed.profiles_enrich.sync("https://linkedin.com/in/satyanadella")
|
|
9
|
+
print(r.result.data["full_name"])
|
|
10
|
+
|
|
11
|
+
# A batch of any size, streamed as it drains.
|
|
12
|
+
batch = ed.profiles_enrich(urls, external_id="crm-sync")
|
|
13
|
+
for entry in ed.results(batch.batch_id):
|
|
14
|
+
if entry.ok:
|
|
15
|
+
save(entry.data)
|
|
16
|
+
|
|
17
|
+
Everything is a batch, including a batch of one, and you are billed per record
|
|
18
|
+
that resolves: a failure costs nothing.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from .client import (
|
|
22
|
+
DEFAULT_BASE_URL,
|
|
23
|
+
OPERATIONS,
|
|
24
|
+
Batch,
|
|
25
|
+
EasyData,
|
|
26
|
+
RateLimits,
|
|
27
|
+
ResultEntry,
|
|
28
|
+
ResultsPage,
|
|
29
|
+
SyncResult,
|
|
30
|
+
)
|
|
31
|
+
from .errors import (
|
|
32
|
+
CapacityUnavailable,
|
|
33
|
+
Conflict,
|
|
34
|
+
EasyDataError,
|
|
35
|
+
EmailUnverified,
|
|
36
|
+
InternalError,
|
|
37
|
+
InvalidAPIKey,
|
|
38
|
+
InvalidRequest,
|
|
39
|
+
NotFound,
|
|
40
|
+
NotImplementedYet,
|
|
41
|
+
QuotaExhausted,
|
|
42
|
+
RateLimited,
|
|
43
|
+
TransportError,
|
|
44
|
+
UnprocessableTarget,
|
|
45
|
+
UpstreamTimeout,
|
|
46
|
+
)
|
|
47
|
+
from .webhooks import Delivery, VerificationError, verify, verify_ed25519
|
|
48
|
+
|
|
49
|
+
__version__ = "1.0.1"
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"EasyData",
|
|
53
|
+
"Batch",
|
|
54
|
+
"ResultEntry",
|
|
55
|
+
"ResultsPage",
|
|
56
|
+
"SyncResult",
|
|
57
|
+
"RateLimits",
|
|
58
|
+
"OPERATIONS",
|
|
59
|
+
"DEFAULT_BASE_URL",
|
|
60
|
+
"EasyDataError",
|
|
61
|
+
"InvalidRequest",
|
|
62
|
+
"InvalidAPIKey",
|
|
63
|
+
"QuotaExhausted",
|
|
64
|
+
"EmailUnverified",
|
|
65
|
+
"NotFound",
|
|
66
|
+
"Conflict",
|
|
67
|
+
"UnprocessableTarget",
|
|
68
|
+
"RateLimited",
|
|
69
|
+
"NotImplementedYet",
|
|
70
|
+
"InternalError",
|
|
71
|
+
"UpstreamTimeout",
|
|
72
|
+
"CapacityUnavailable",
|
|
73
|
+
"TransportError",
|
|
74
|
+
"verify",
|
|
75
|
+
"verify_ed25519",
|
|
76
|
+
"Delivery",
|
|
77
|
+
"VerificationError",
|
|
78
|
+
"__version__",
|
|
79
|
+
]
|