everyinfra 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,219 @@
1
+ Metadata-Version: 2.4
2
+ Name: everyinfra
3
+ Version: 0.3.0
4
+ Summary: EveryInfra official client — data, captcha, search and text models behind one key
5
+ Author: EveryInfra
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://everyinfra.com
8
+ Project-URL: Documentation, https://everyinfra.com/docs
9
+ Keywords: captcha,scraping,search,api,everyinfra
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Intended Audience :: Developers
12
+ Requires-Python: >=3.9
13
+ Description-Content-Type: text/markdown
14
+
15
+ # everyinfra — EveryInfra 官方 Python 客户端
16
+
17
+ > v0.3.0 起在 PyPI 发布(包名 `everyinfra`);同一份 wheel 也附在 EveryInfra 官方 GitHub Release,作为备选安装地址。服务是否开放及领取条件以官网和实际接口为准。
18
+
19
+ 一把 Key、一个钱包,接入数据采集、验证码和搜索;充值过的账户还可以免费使用 OpenAI 兼容的 AI 接口。
20
+
21
+ ```bash
22
+ pip install everyinfra
23
+ ```
24
+
25
+ 装不上 PyPI 时,可以直接装 GitHub Release 里的同一份 wheel(附 `SHA256SUMS`):
26
+
27
+ ```bash
28
+ pip install https://github.com/everyinfra/everyinfra-agent-plugins/releases/download/sdk-v0.3.0/everyinfra-0.3.0-py3-none-any.whl
29
+ ```
30
+
31
+ ```python
32
+ from everyinfra import EveryInfra
33
+
34
+ ei = EveryInfra("YOUR_API_KEY")
35
+
36
+ posts = ei.data("xiaohongshu", "search", keyword="露营", limit=20)
37
+ sol = ei.captcha("turnstile", website_url="https://…", website_key="0x…")
38
+ hits = ei.search("web", q="EU AI Act 合规要求")
39
+ acct = ei.account() # 余额、本月折扣档、每分钟次数、近 24 小时用量(只读、不计费)
40
+ ```
41
+
42
+ 零依赖(只用标准库)。完整 API 文档:<https://everyinfra.com/docs>
43
+
44
+ ---
45
+
46
+ ## 命令行
47
+
48
+ 同一个包自带命令行 `everyinfra`(也可以 `python -m everyinfra`),给终端里的人和编码 agent 用:
49
+
50
+ ```bash
51
+ pipx install everyinfra # 或 uv tool install everyinfra;pip install everyinfra 也带命令行
52
+ everyinfra login # 先验 Key 再存到 ~/.config/everyinfra/config.json(权限 0600)
53
+ everyinfra catalog --find "tiktok comments" # 免费:找能力
54
+ everyinfra describe tiktok/comments # 免费:参数、返回字段、价格
55
+ everyinfra data tiktok comments -p url=https://www.tiktok.com/@a/video/1 --limit 50 --dry-run
56
+ everyinfra data tiktok comments -p url=https://www.tiktok.com/@a/video/1 --limit 50 \
57
+ --max-cost-usd 0.01 --output comments.json
58
+ ```
59
+
60
+ - Key 依次读 `--key`、环境变量 `EVERYINFRA_API_KEY`、`everyinfra login` 存的那份。目录、`describe`、`--dry-run` 不要 Key。
61
+ - `-p key=value` 按目录里的参数类型转(整数、布尔、数组…),`-p key:=JSON` 原样传 JSON;数组参数可以写多次 `-p urls=A -p urls=B`。
62
+ - `--dry-run` 只打印请求和按标价估的费用,什么都不发;`--max-cost-usd` 估价超过就不发。
63
+ - 接管道或加 `--json` 时输出 JSON,`--jsonl` 每行一条,`--fields a,b` 只取字段,`--output FILE` 写全量、屏幕只给摘要。
64
+ 付费命令结束时 stderr 打一行本次扣费和余额;出错时 stderr 是和 API 同形的 JSON。
65
+ - 退出码:0 成功,2 请求不对,3 鉴权或权限,4 余额不足,5 限速(JSON 里带 `retry_after`),6 临时失败可重试,
66
+ 7 结果未知(不要重试,先用 `everyinfra requests --id …` 或 `everyinfra job …` 核对),1 其他。
67
+ - 其他命令:`search`、`captcha types` / `captcha solve`、`job`、`ai chat` / `ai map`(逐行跑 AI,遇限速自动等)、
68
+ `account`、`usage`、`requests`、`billing`、`whats-new`、`support draft` / `support open`、`doctor`。
69
+ - `everyinfra mcp-config --client claude|cursor|codex|vscode` 打印各家的 MCP 配置(默认引用环境变量,不把 Key 写进文件);
70
+ `everyinfra skills install` 把给编码 agent 的使用说明装进 Claude Code、Codex、Cursor、VS Code 的技能目录。
71
+
72
+ ---
73
+
74
+ ## 类型补全是这个包的主要价值
75
+
76
+ 我们的 API 是「一个端点 + 一个判别字段」,包一层 HTTP 对你几乎没有增量 ——
77
+ 你自己写 `requests.post` 一样能用。这个包补上自动生成的判别值与必填参数。
78
+
79
+ ```python
80
+ from everyinfra.catalog import CaptchaType, DataCapability, CAPTCHA_REQUIRED
81
+
82
+ CAPTCHA_REQUIRED["turnstile"] # ('website_url', 'website_key')
83
+ CAPTCHA_REQUIRED["anubis"] # ('website_url', 'proxy')
84
+ ```
85
+
86
+ `Platform` / `DataCapability` / `CaptchaType` / `SearchTool`
87
+ 都是 `Literal` 类型,IDE 直接补全、拼错当场标红。这份静态目录由后端注册表生成;
88
+ 实时可用性仍以在线目录为准。
89
+
90
+ ⚠ **目录里刻意没有价格和可用性。** 价格会调、`available` 每分钟都可能变化 ——
91
+ 烤进一个版本化的包,你就会拿着旧数据算错账,或者调一个此刻根本不可用的能力。
92
+ 那些请现读:`ei.captcha_types()` / `ei.search_tools()` / `ei.data_catalog()`(都免费、不鉴权)。
93
+
94
+ ---
95
+
96
+ ## 三件会影响你写法的事
97
+
98
+ ### 1. 默认超时 300 秒,别随手调小
99
+
100
+ 公网同步调用 **85 秒**内必回(超时回 `503 timeout`、不计费),验证码最长 **120 秒**。
101
+ 客户端超时低于服务端还在算的时间会出现最坏的一种情况:**我们已经扣了费并且还在算,你先放弃了** ——
102
+ 钱花了、结果没拿到,只能人工对账才发现。
103
+
104
+ 真的不想等就用异步(对任何数据能力都生效,少数只能同步的批量能力会直接 422、不扣费):
105
+
106
+ ```python
107
+ sub = ei.data("xiaohongshu", "search", keyword="露营", mode="async") # 立即返回 job_id
108
+ res = ei.wait_job(sub["job_id"]) # 轮询到结束;也可以自己隔一会儿 ei.job(job_id)
109
+ res["status"], res.get("results") # succeeded / failed / submit_unknown
110
+ ```
111
+
112
+ `data()` 每次调用都自动带 `Idempotency-Key`(同步、异步都是):断线后 SDK 重发,同步调用拿回第一次的响应(`replayed: true`、billing 不变),异步拿回同一个任务,都不会再扣一次。要跨进程重试同一个逻辑请求,自己传一个稳定的 Key(`idempotency_key=`)。
113
+
114
+ ### 2. 验证码的解有九种形状,我们不替你取值
115
+
116
+ | 形状 | 长什么样 | 怎么用 |
117
+ |---|---|---|
118
+ | `token` | `"0.aBcD…"` | 填回目标站表单 |
119
+ | `cookie` | `"name=值; Path=/"` | 作为 Cookie 带上后续请求 |
120
+ | `text` | `"3f8a"` | 填回输入框 |
121
+ | `number` | `42` | 按数值驱动你自己的浏览器 |
122
+ | `fields` | `{"ticket": …, "randstr": …}` | 多个字段一起回填 |
123
+ | `points` | `[{"x": 36, "y": 26}]` | 按坐标依次点击 |
124
+ | `boxes` | `[{"x_min": …, "x_max": …, …}]` | 按矩形拖框 |
125
+ | `tokens` | `["ct_aBc…", "ct_dEf…"]` | 一批令牌,在后续请求上逐个用掉 |
126
+ | `indices` | `[0, 2, 6]` | 按编号点格子(0 起,从左上角按行数) |
127
+
128
+ 前四种是标量,响应里额外给一个顶层 `token` 可以直接用;**后五种没有**,
129
+ 只能从 `solution` 取。调 `ei.captcha_types()` 看每种类型的 `solution.shape`。
130
+
131
+ ⚠ 有几类**要你自带代理**(`CAPTCHA_REQUIRED` 里含 `proxy` 的那些):它们交付的是
132
+ Cookie 而不是 token,而 Cookie 绑定求解时的出口 IP —— 用我们的出口解出来,
133
+ 你拿去用是无效的。代理必须是**你后续请求要用的那个出口**。
134
+
135
+ ### 3. 响应整包返回,`billing` 不要忽略
136
+
137
+ ```python
138
+ r = ei.data("amazon", "reviews", urls=[...])
139
+ r["billing"] # {"charged": true, "charged_usd": 0.111111, "balance_after_usd": 12.23, "units": 20, ...}
140
+ r["partial"] # 扇出能力只采到一部分时为 true
141
+ r["missed"] # 没采到的那几个目标 —— 这些不收钱
142
+ ```
143
+
144
+ `billing.charged_usd` 是本次实扣(USD;没扣为 0,结果未知为 `None`),`billing.balance_after_usd`
145
+ 是本次结算后的钱包余额(读不到时为 `None`)。`credits` / `cny` 等旧字段仍在,只作兼容。
146
+ 我们不替你加工这些字段,因为那是你对账的依据。
147
+
148
+ ---
149
+
150
+ ## 查余额与用量
151
+
152
+ ```python
153
+ acct = ei.account() # GET /api/v1/account,同一把 Key
154
+ acct["balance"]["total_usd"] # 付费 + 赠送合计(USD),扣费时先用赠送余额
155
+ acct["balance"]["bonus_expiring"] # 还没到期的限时赠送额度
156
+ acct["discount"] # 本月充值折扣档与距下一档的差额;经服务商开通的账户为 None
157
+ acct["rate_limit"]["requests_per_minute"] # 账户内所有 Key 合计的每分钟次数上限(AI 接口另有分档)
158
+ acct["usage_24h"] # 近 24 小时调用数与实际花费,this_key 是当前这把 Key 的
159
+ ```
160
+
161
+ 只读、不计费、不计入每分钟次数;任何产品线范围的 Key 都能读。跑批量前先看余额够不够,
162
+ 跑的过程中看每次响应的 `billing.balance_after_usd` 就行,不用反复调它。
163
+
164
+ ---
165
+
166
+ ## 错误处理
167
+
168
+ ```python
169
+ from everyinfra import BadRequest, OutcomeUnknown, PermissionDenied, QuotaExhausted
170
+
171
+ try:
172
+ ei.data("xiaohongshu", "serch") # 拼错了
173
+ except BadRequest as e:
174
+ print(e.message) # 会列出该平台全部 action 并给 did-you-mean
175
+ print(e.did_you_mean) # ['search'] —— 结构化候选,自动修正用它,别解析英文句子
176
+ print(e.param, e.allowed)
177
+ print(e.request_id) # 报障时把这个给我们
178
+ ```
179
+
180
+ 内置重试**只覆盖确定不会重复扣费的失败**:503 服务暂时出错 / 超时(不计费)、429(按 `Retry-After`
181
+ 退避)、请求根本没发出去的网络错误(连不上、DNS 失败)。**402 余额不足与 4xx 参数错一律不重试** ——
182
+ 那些重试必然同样失败,只会让你以为程序还在正常工作。
183
+
184
+ | 异常 | 什么时候 | 怎么办 |
185
+ |---|---|---|
186
+ | `AuthError` | 401:没带 Key、Key 无效或过期 | 换 Key |
187
+ | `QuotaExhausted` | 402:余额不足 | 充值,不要等 |
188
+ | `AccountDisabled` | 403 `account_disabled` | 联系客服 |
189
+ | `PermissionDenied` | 其余 403:AI 接口未充值 / 已暂停、Key 的产品线或 IP 限制 | 看 `code` |
190
+ | `BadRequest` | 400 / 422:参数不合约 | 按 `did_you_mean` / `allowed` 改 |
191
+ | `RateLimited` | 429 | SDK 已按 `retry_after` 退避重试过 |
192
+ | `UpstreamError` | 503:暂时出错 / 超时,不计费 | SDK 已重试过;`timeout` 就改用 `mode="async"` |
193
+ | `OutcomeUnknown` | 结果未知:退款或提交在核对,或 POST 发出后断线 | **不要重发**,留着 `request_id` / `job_id` 先核对 |
194
+
195
+ `request_id` 一定要留着,那是我们排查一次具体调用的唯一凭据。
196
+
197
+ ---
198
+
199
+ ## AI 接口(充值用户免费)
200
+
201
+ `ei.chat()` 调 OpenAI 兼容的 AI 接口 `POST /api/v1/chat/completions`:充值过的账户免费用,不扣余额;输入不限内容和格式,用来整理和清洗你在 EveryInfra 采集的数据。按累计充值分档限速,账户内所有 Key 合计:不满 $100 每分钟 50 次,满 $100 每分钟 500 次,满 $500 每分钟 5000 次。同一账户同时在跑的请求数也按档位放宽(5 / 50 / 500 个)。分档是上限,不是承诺的吞吐:服务忙时返回 503 `ai_busy`。超限、并发满和服务忙都带 `Retry-After`。没充值过的账户返回 403 `ai_topup_required`。模型清单以 `GET /api/v1/models` 为准,默认 `gemini-3.6-flash`。
202
+
203
+ SDK 里的 `chat()` 只走非流式。要流式(`stream=True`),或者想用现成的 OpenAI 生态,直接用 OpenAI SDK,把 `base_url` 指到 `https://api.everyinfra.com/api/v1`,`api_key` 填同一把 EveryInfra Key。
204
+
205
+ 数据清洗(`cleanup_*`)已在 2026-10-05 下线:原接口返回 410 `data_cleanup_retired`,改用上面的 AI 接口。
206
+
207
+ ---
208
+
209
+ ## 账户 Webhook:校验签名
210
+
211
+ 在控制台「设置 → Webhook」填一个 HTTPS 地址,选「余额低于阈值」(`balance.low`)和/或「异步任务结束」(`job.finished`),余额降到阈值或任务结束时会收到签名的 POST。请求体是 `{"id", "type", "created_at", "data"}`;`job.finished` 不带结果数据,用提交任务的那把 Key 调 `GET /api/v1/jobs/{job_id}` 取。
212
+
213
+ ```python
214
+ from everyinfra.webhooks import verify_signature
215
+
216
+ ok = verify_signature(signing_secret, request.headers, raw_body) # 一定要传原始请求体
217
+ ```
218
+
219
+ 时间戳与当前时间相差超过 5 分钟会被拒绝(防重放)。投递是至少一次:返回任意 2xx 表示收到,否则按 30 秒、2 分钟、10 分钟、30 分钟、2 小时、6 小时重试,最多 7 次,所以请按 `id`(同 `X-EveryInfra-Event-Id` 头)去重。
@@ -0,0 +1,205 @@
1
+ # everyinfra — EveryInfra 官方 Python 客户端
2
+
3
+ > v0.3.0 起在 PyPI 发布(包名 `everyinfra`);同一份 wheel 也附在 EveryInfra 官方 GitHub Release,作为备选安装地址。服务是否开放及领取条件以官网和实际接口为准。
4
+
5
+ 一把 Key、一个钱包,接入数据采集、验证码和搜索;充值过的账户还可以免费使用 OpenAI 兼容的 AI 接口。
6
+
7
+ ```bash
8
+ pip install everyinfra
9
+ ```
10
+
11
+ 装不上 PyPI 时,可以直接装 GitHub Release 里的同一份 wheel(附 `SHA256SUMS`):
12
+
13
+ ```bash
14
+ pip install https://github.com/everyinfra/everyinfra-agent-plugins/releases/download/sdk-v0.3.0/everyinfra-0.3.0-py3-none-any.whl
15
+ ```
16
+
17
+ ```python
18
+ from everyinfra import EveryInfra
19
+
20
+ ei = EveryInfra("YOUR_API_KEY")
21
+
22
+ posts = ei.data("xiaohongshu", "search", keyword="露营", limit=20)
23
+ sol = ei.captcha("turnstile", website_url="https://…", website_key="0x…")
24
+ hits = ei.search("web", q="EU AI Act 合规要求")
25
+ acct = ei.account() # 余额、本月折扣档、每分钟次数、近 24 小时用量(只读、不计费)
26
+ ```
27
+
28
+ 零依赖(只用标准库)。完整 API 文档:<https://everyinfra.com/docs>
29
+
30
+ ---
31
+
32
+ ## 命令行
33
+
34
+ 同一个包自带命令行 `everyinfra`(也可以 `python -m everyinfra`),给终端里的人和编码 agent 用:
35
+
36
+ ```bash
37
+ pipx install everyinfra # 或 uv tool install everyinfra;pip install everyinfra 也带命令行
38
+ everyinfra login # 先验 Key 再存到 ~/.config/everyinfra/config.json(权限 0600)
39
+ everyinfra catalog --find "tiktok comments" # 免费:找能力
40
+ everyinfra describe tiktok/comments # 免费:参数、返回字段、价格
41
+ everyinfra data tiktok comments -p url=https://www.tiktok.com/@a/video/1 --limit 50 --dry-run
42
+ everyinfra data tiktok comments -p url=https://www.tiktok.com/@a/video/1 --limit 50 \
43
+ --max-cost-usd 0.01 --output comments.json
44
+ ```
45
+
46
+ - Key 依次读 `--key`、环境变量 `EVERYINFRA_API_KEY`、`everyinfra login` 存的那份。目录、`describe`、`--dry-run` 不要 Key。
47
+ - `-p key=value` 按目录里的参数类型转(整数、布尔、数组…),`-p key:=JSON` 原样传 JSON;数组参数可以写多次 `-p urls=A -p urls=B`。
48
+ - `--dry-run` 只打印请求和按标价估的费用,什么都不发;`--max-cost-usd` 估价超过就不发。
49
+ - 接管道或加 `--json` 时输出 JSON,`--jsonl` 每行一条,`--fields a,b` 只取字段,`--output FILE` 写全量、屏幕只给摘要。
50
+ 付费命令结束时 stderr 打一行本次扣费和余额;出错时 stderr 是和 API 同形的 JSON。
51
+ - 退出码:0 成功,2 请求不对,3 鉴权或权限,4 余额不足,5 限速(JSON 里带 `retry_after`),6 临时失败可重试,
52
+ 7 结果未知(不要重试,先用 `everyinfra requests --id …` 或 `everyinfra job …` 核对),1 其他。
53
+ - 其他命令:`search`、`captcha types` / `captcha solve`、`job`、`ai chat` / `ai map`(逐行跑 AI,遇限速自动等)、
54
+ `account`、`usage`、`requests`、`billing`、`whats-new`、`support draft` / `support open`、`doctor`。
55
+ - `everyinfra mcp-config --client claude|cursor|codex|vscode` 打印各家的 MCP 配置(默认引用环境变量,不把 Key 写进文件);
56
+ `everyinfra skills install` 把给编码 agent 的使用说明装进 Claude Code、Codex、Cursor、VS Code 的技能目录。
57
+
58
+ ---
59
+
60
+ ## 类型补全是这个包的主要价值
61
+
62
+ 我们的 API 是「一个端点 + 一个判别字段」,包一层 HTTP 对你几乎没有增量 ——
63
+ 你自己写 `requests.post` 一样能用。这个包补上自动生成的判别值与必填参数。
64
+
65
+ ```python
66
+ from everyinfra.catalog import CaptchaType, DataCapability, CAPTCHA_REQUIRED
67
+
68
+ CAPTCHA_REQUIRED["turnstile"] # ('website_url', 'website_key')
69
+ CAPTCHA_REQUIRED["anubis"] # ('website_url', 'proxy')
70
+ ```
71
+
72
+ `Platform` / `DataCapability` / `CaptchaType` / `SearchTool`
73
+ 都是 `Literal` 类型,IDE 直接补全、拼错当场标红。这份静态目录由后端注册表生成;
74
+ 实时可用性仍以在线目录为准。
75
+
76
+ ⚠ **目录里刻意没有价格和可用性。** 价格会调、`available` 每分钟都可能变化 ——
77
+ 烤进一个版本化的包,你就会拿着旧数据算错账,或者调一个此刻根本不可用的能力。
78
+ 那些请现读:`ei.captcha_types()` / `ei.search_tools()` / `ei.data_catalog()`(都免费、不鉴权)。
79
+
80
+ ---
81
+
82
+ ## 三件会影响你写法的事
83
+
84
+ ### 1. 默认超时 300 秒,别随手调小
85
+
86
+ 公网同步调用 **85 秒**内必回(超时回 `503 timeout`、不计费),验证码最长 **120 秒**。
87
+ 客户端超时低于服务端还在算的时间会出现最坏的一种情况:**我们已经扣了费并且还在算,你先放弃了** ——
88
+ 钱花了、结果没拿到,只能人工对账才发现。
89
+
90
+ 真的不想等就用异步(对任何数据能力都生效,少数只能同步的批量能力会直接 422、不扣费):
91
+
92
+ ```python
93
+ sub = ei.data("xiaohongshu", "search", keyword="露营", mode="async") # 立即返回 job_id
94
+ res = ei.wait_job(sub["job_id"]) # 轮询到结束;也可以自己隔一会儿 ei.job(job_id)
95
+ res["status"], res.get("results") # succeeded / failed / submit_unknown
96
+ ```
97
+
98
+ `data()` 每次调用都自动带 `Idempotency-Key`(同步、异步都是):断线后 SDK 重发,同步调用拿回第一次的响应(`replayed: true`、billing 不变),异步拿回同一个任务,都不会再扣一次。要跨进程重试同一个逻辑请求,自己传一个稳定的 Key(`idempotency_key=`)。
99
+
100
+ ### 2. 验证码的解有九种形状,我们不替你取值
101
+
102
+ | 形状 | 长什么样 | 怎么用 |
103
+ |---|---|---|
104
+ | `token` | `"0.aBcD…"` | 填回目标站表单 |
105
+ | `cookie` | `"name=值; Path=/"` | 作为 Cookie 带上后续请求 |
106
+ | `text` | `"3f8a"` | 填回输入框 |
107
+ | `number` | `42` | 按数值驱动你自己的浏览器 |
108
+ | `fields` | `{"ticket": …, "randstr": …}` | 多个字段一起回填 |
109
+ | `points` | `[{"x": 36, "y": 26}]` | 按坐标依次点击 |
110
+ | `boxes` | `[{"x_min": …, "x_max": …, …}]` | 按矩形拖框 |
111
+ | `tokens` | `["ct_aBc…", "ct_dEf…"]` | 一批令牌,在后续请求上逐个用掉 |
112
+ | `indices` | `[0, 2, 6]` | 按编号点格子(0 起,从左上角按行数) |
113
+
114
+ 前四种是标量,响应里额外给一个顶层 `token` 可以直接用;**后五种没有**,
115
+ 只能从 `solution` 取。调 `ei.captcha_types()` 看每种类型的 `solution.shape`。
116
+
117
+ ⚠ 有几类**要你自带代理**(`CAPTCHA_REQUIRED` 里含 `proxy` 的那些):它们交付的是
118
+ Cookie 而不是 token,而 Cookie 绑定求解时的出口 IP —— 用我们的出口解出来,
119
+ 你拿去用是无效的。代理必须是**你后续请求要用的那个出口**。
120
+
121
+ ### 3. 响应整包返回,`billing` 不要忽略
122
+
123
+ ```python
124
+ r = ei.data("amazon", "reviews", urls=[...])
125
+ r["billing"] # {"charged": true, "charged_usd": 0.111111, "balance_after_usd": 12.23, "units": 20, ...}
126
+ r["partial"] # 扇出能力只采到一部分时为 true
127
+ r["missed"] # 没采到的那几个目标 —— 这些不收钱
128
+ ```
129
+
130
+ `billing.charged_usd` 是本次实扣(USD;没扣为 0,结果未知为 `None`),`billing.balance_after_usd`
131
+ 是本次结算后的钱包余额(读不到时为 `None`)。`credits` / `cny` 等旧字段仍在,只作兼容。
132
+ 我们不替你加工这些字段,因为那是你对账的依据。
133
+
134
+ ---
135
+
136
+ ## 查余额与用量
137
+
138
+ ```python
139
+ acct = ei.account() # GET /api/v1/account,同一把 Key
140
+ acct["balance"]["total_usd"] # 付费 + 赠送合计(USD),扣费时先用赠送余额
141
+ acct["balance"]["bonus_expiring"] # 还没到期的限时赠送额度
142
+ acct["discount"] # 本月充值折扣档与距下一档的差额;经服务商开通的账户为 None
143
+ acct["rate_limit"]["requests_per_minute"] # 账户内所有 Key 合计的每分钟次数上限(AI 接口另有分档)
144
+ acct["usage_24h"] # 近 24 小时调用数与实际花费,this_key 是当前这把 Key 的
145
+ ```
146
+
147
+ 只读、不计费、不计入每分钟次数;任何产品线范围的 Key 都能读。跑批量前先看余额够不够,
148
+ 跑的过程中看每次响应的 `billing.balance_after_usd` 就行,不用反复调它。
149
+
150
+ ---
151
+
152
+ ## 错误处理
153
+
154
+ ```python
155
+ from everyinfra import BadRequest, OutcomeUnknown, PermissionDenied, QuotaExhausted
156
+
157
+ try:
158
+ ei.data("xiaohongshu", "serch") # 拼错了
159
+ except BadRequest as e:
160
+ print(e.message) # 会列出该平台全部 action 并给 did-you-mean
161
+ print(e.did_you_mean) # ['search'] —— 结构化候选,自动修正用它,别解析英文句子
162
+ print(e.param, e.allowed)
163
+ print(e.request_id) # 报障时把这个给我们
164
+ ```
165
+
166
+ 内置重试**只覆盖确定不会重复扣费的失败**:503 服务暂时出错 / 超时(不计费)、429(按 `Retry-After`
167
+ 退避)、请求根本没发出去的网络错误(连不上、DNS 失败)。**402 余额不足与 4xx 参数错一律不重试** ——
168
+ 那些重试必然同样失败,只会让你以为程序还在正常工作。
169
+
170
+ | 异常 | 什么时候 | 怎么办 |
171
+ |---|---|---|
172
+ | `AuthError` | 401:没带 Key、Key 无效或过期 | 换 Key |
173
+ | `QuotaExhausted` | 402:余额不足 | 充值,不要等 |
174
+ | `AccountDisabled` | 403 `account_disabled` | 联系客服 |
175
+ | `PermissionDenied` | 其余 403:AI 接口未充值 / 已暂停、Key 的产品线或 IP 限制 | 看 `code` |
176
+ | `BadRequest` | 400 / 422:参数不合约 | 按 `did_you_mean` / `allowed` 改 |
177
+ | `RateLimited` | 429 | SDK 已按 `retry_after` 退避重试过 |
178
+ | `UpstreamError` | 503:暂时出错 / 超时,不计费 | SDK 已重试过;`timeout` 就改用 `mode="async"` |
179
+ | `OutcomeUnknown` | 结果未知:退款或提交在核对,或 POST 发出后断线 | **不要重发**,留着 `request_id` / `job_id` 先核对 |
180
+
181
+ `request_id` 一定要留着,那是我们排查一次具体调用的唯一凭据。
182
+
183
+ ---
184
+
185
+ ## AI 接口(充值用户免费)
186
+
187
+ `ei.chat()` 调 OpenAI 兼容的 AI 接口 `POST /api/v1/chat/completions`:充值过的账户免费用,不扣余额;输入不限内容和格式,用来整理和清洗你在 EveryInfra 采集的数据。按累计充值分档限速,账户内所有 Key 合计:不满 $100 每分钟 50 次,满 $100 每分钟 500 次,满 $500 每分钟 5000 次。同一账户同时在跑的请求数也按档位放宽(5 / 50 / 500 个)。分档是上限,不是承诺的吞吐:服务忙时返回 503 `ai_busy`。超限、并发满和服务忙都带 `Retry-After`。没充值过的账户返回 403 `ai_topup_required`。模型清单以 `GET /api/v1/models` 为准,默认 `gemini-3.6-flash`。
188
+
189
+ SDK 里的 `chat()` 只走非流式。要流式(`stream=True`),或者想用现成的 OpenAI 生态,直接用 OpenAI SDK,把 `base_url` 指到 `https://api.everyinfra.com/api/v1`,`api_key` 填同一把 EveryInfra Key。
190
+
191
+ 数据清洗(`cleanup_*`)已在 2026-10-05 下线:原接口返回 410 `data_cleanup_retired`,改用上面的 AI 接口。
192
+
193
+ ---
194
+
195
+ ## 账户 Webhook:校验签名
196
+
197
+ 在控制台「设置 → Webhook」填一个 HTTPS 地址,选「余额低于阈值」(`balance.low`)和/或「异步任务结束」(`job.finished`),余额降到阈值或任务结束时会收到签名的 POST。请求体是 `{"id", "type", "created_at", "data"}`;`job.finished` 不带结果数据,用提交任务的那把 Key 调 `GET /api/v1/jobs/{job_id}` 取。
198
+
199
+ ```python
200
+ from everyinfra.webhooks import verify_signature
201
+
202
+ ok = verify_signature(signing_secret, request.headers, raw_body) # 一定要传原始请求体
203
+ ```
204
+
205
+ 时间戳与当前时间相差超过 5 分钟会被拒绝(防重放)。投递是至少一次:返回任意 2xx 表示收到,否则按 30 秒、2 分钟、10 分钟、30 分钟、2 小时、6 小时重试,最多 7 次,所以请按 `id`(同 `X-EveryInfra-Event-Id` 头)去重。
@@ -0,0 +1,20 @@
1
+ """EveryInfra 官方 Python 客户端。
2
+
3
+ pip install everyinfra
4
+
5
+ 备选:直接装 GitHub Release 里的同一份 wheel
6
+ pip install https://github.com/everyinfra/everyinfra-agent-plugins/releases/download/sdk-v0.3.0/everyinfra-0.3.0-py3-none-any.whl
7
+
8
+ 一把 Key、一个钱包,接入数据采集、验证码和搜索;充值过的账户还可以免费用 OpenAI 兼容的 AI 接口。
9
+ 完整文档 https://everyinfra.com/docs
10
+ 账户 Webhook 的签名校验:``from everyinfra.webhooks import verify_signature``。
11
+ 命令行:``everyinfra --help``(同一个包,见 ``everyinfra/cli.py``)。
12
+ """
13
+ from .client import (AccountDisabled, AuthError, BadRequest, EveryInfra,
14
+ EveryInfraError, OutcomeUnknown, PermissionDenied, QuotaExhausted,
15
+ RateLimited, UpstreamError)
16
+
17
+ __version__ = "0.3.0"
18
+ __all__ = ["EveryInfra", "EveryInfraError", "AuthError", "AccountDisabled",
19
+ "PermissionDenied", "OutcomeUnknown",
20
+ "QuotaExhausted", "RateLimited", "BadRequest", "UpstreamError"]
@@ -0,0 +1,4 @@
1
+ """`python -m everyinfra` = 命令行 `everyinfra`(见 cli.py)。"""
2
+ from .cli import main
3
+
4
+ raise SystemExit(main())