Apify API:从运行 Actor 到读取 Dataset 的最小安全闭环
披露:本页含联盟推广链接。你通过本页链接注册 Apify 并付费,我们可能获得佣金——不影响你支付的价格,也不改变我们如实记录的实测数据。 我们如何实测 →
证据口径:本页于 2026-07-31 按 Apify API v2、Run Actor、Get run、Run task 与 Runs and builds 官方文档核对。下面是占位符示例,没有真实 token,也没有为本站触发新 run。
API 接入的可靠路径不是“一次请求永远等到数据回来”,而是:
POST 启动 Actor/Task
→ 保存 Run ID
→ 查询状态
→ 从 Run 读取 defaultDatasetId
→ 显式分批读取 Dataset items
短任务可以使用同步 endpoint;耗时、结果量或失败重试不可预测时,优先异步流程。
第一步:把 token 放在 header
在 Apify Console 的 API & Integrations 页面创建或读取所需 token,把它放进服务器端秘密变量。官方仍允许 URL query token,但 URL 容易进入浏览历史、代理日志和监控记录,新代码应使用 Bearer header。
export APIFY_TOKEN="<YOUR_TOKEN>"
不要把真实 token 提交到仓库、前端 bundle、截图或聊天。
第二步:异步启动 Actor
下面以 Actor ID 占位符演示;请求体必须按该 Actor 当前 input schema 调整:
curl --request POST "https://api.apify.com/v2/actors/<ACTOR_ID>/runs" \
--header "Authorization: Bearer $APIFY_TOKEN" \
--header "Content-Type: application/json" \
--data '{"queries":"coffee shops in Shanghai","maxCrawledPlacesPerSearch":10}'
原始 REST 响应外层是 data,Run ID 位于 data.id,状态和默认 Dataset ID 分别位于 data.status 与 data.defaultDatasetId。不要照搬已解包 API client 的字段路径:
{
"data": {
"id": "<RUN_ID>",
"status": "READY",
"defaultDatasetId": "<DATASET_ID>"
}
}
HTTP 201 只说明 run 已创建,不代表采集成功,也不代表 Dataset 已有完整结果。
Actor、Task 与 Build 怎么选
| 需求 | 调用方式 | 关键边界 |
|---|---|---|
| 每次传入一套新输入 | POST /v2/actors/<ACTOR_ID>/runs | 请求体必须符合该 Actor 当前 input schema |
| 重复运行已保存配置 | POST /v2/actor-tasks/<TASK_ID>/runs | 请求体只覆盖传入字段;未传字段继续使用 Task 或 Actor schema 的默认值 |
| 需要固定运行版本 | 在 run URL 加 build=<BUILD_NUMBER> | build 接受 tag 或 build number;buildId 是响应字段,不是这里的请求参数 |
latest 等 tag 可以被重新指向。固定精确 build number 能降低新版本改变输入或输出的风险,但也会错过更新;升级前仍要用小样本复核 schema。
第三步:查询 Run 状态
curl "https://api.apify.com/v2/actor-runs/<RUN_ID>" \
--header "Authorization: Bearer $APIFY_TOKEN"
状态门禁应使用刷新后的 data.status:
READY、RUNNING、TIMING-OUT、ABORTING都不是完成证据。- 只有
SUCCEEDED可以进入正常下游。 FAILED、TIMED-OUT、ABORTED都应进入失败处理。
失败或超时先读日志,不要在没有退避和上限的循环里重新运行。Run 刚完成时,费用、事件数和部分统计可能仍是初步值;需要结算级数字时,按官方建议约 10 秒后再读取一次 Run。
第四步:从 Run 读取 Dataset ID,再拉结果
从 Run 对象取 defaultDatasetId,不要永久写死一次测试的 Dataset ID:
curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json&limit=100&offset=0" \
--header "Authorization: Bearer $APIFY_TOKEN"
当前示例显式设置了 limit=100,所以要按分页响应头推进 offset 直到覆盖总量。Dataset API 未设置 limit 时没有默认条数上限,但大结果仍建议分批读取,并保存 Run ID、Dataset ID、输入摘要和采集时间。参数和分页细节见 Storage 与导出指南。
同步还是异步
| 条件 | 建议 |
|---|---|
| 小输入、调用方可等待 | 同步 run-sync;官方当前上限为 300 秒 |
| 希望异步请求短暂等待 | 在 run 或 Get run 上使用 waitForFinish;最多 60 秒,返回时仍可能是 RUNNING |
| 运行时间不确定、结果大、需要 webhook/轮询 | 异步 run,并对刷新后的 Run 做状态门禁 |
| 定时或重复配置 | 先保存 Task,再由 API 运行;配置方法见 Tasks 与 Schedules |
| AI Agent 自动调用 | 先读 schema 与费用,限制输入和预算,再运行 |
同步 endpoint、waitForFinish 和 HTTP 成功码都只是等待或传输行为,不能替代 SUCCEEDED 状态判断。
预算闸:两个参数不能混用
maxTotalChargeUsd限制单次 run 的总收费,官方将它定义为适用于所有 pricing models 的总额上限。maxItems只限制 pay-per-result Actor 可收费的 Dataset items;它不保证 Actor 实际只返回这么多条,也不是其他定价模型的通用数量上限。timeout、memory与build也可作为 run 参数,但不要在 Actor 文档没有要求时盲目调高资源。
这些参数是事故护栏,不是报价。提交前仍要核对当前 Actor 的 计费模型,完成后以 Console/Billing 的实际记录为准。
上线前必须处理的失败条件
- 401/403:token 缺失、失效或权限不足;不要改成 query token“试试看”。
- 429/5xx:按响应与官方当前限制做有上限的指数退避,避免重试风暴。
- Run 失败:保存日志与输入,区分目标站、Actor 和调用方问题。
- Dataset 为空:成功状态也可能产生空结果;业务验收不能只看 HTTP 200。
- 重复结果:用 Run ID 和业务键设计幂等,不把网络重试变成重复写入。
- 版本漂移:记录实际
buildId与buildNumber;固定版本时传build,不要把响应里的buildId拼回 run URL。
Node.js 与 Python 可使用官方 API client 减少轮询样板;无论用 REST 还是 client,上面的 Run → status → Dataset 证据链不变。
先在任务页看清输入、输出和费用,再把同一个 Actor 接进 API。
apify/google-search-scraper 按 $0.0045/条计,$5 免费额度约合 1,111 条;不绑卡,跑完再决定要不要付费。
免费跑我自己的第一批 →