面向开发者
API
API 做两件事:对账户里的站点发起扫描;读取你的账户在应用里能读到的内容——站点、扫描状态与报告。它属于 Gold 与 Platinum 套餐。
认证
- 在应用的“设置 → API”里创建 key。它只显示一次;我们保存的是它的指纹,不是 key 本身。
- 每个请求都以 Bearer 令牌的形式带上它。Cookie 会被忽略。
- 一把 key 有一个或两个权限范围:reports:read(站点、扫描状态、报告)与 scans:create(发起扫描)。
- key 不等于登录。它不能更改你的账户、站点、账单或 key 本身。如果泄露,撤销它即可,其他 key 不受影响。
所有路径都在 https://app.geoready.org/api 之下。机器可读的接口描述在 https://app.geoready.org/api/v1/openapi.json。
接口
| 请求 | Scope | 作用 |
|---|---|---|
| GET /v1/me | — | 你的套餐、本周额度,以及发起请求的这把 key。 |
| GET /v1/sites | reports:read | 账户里的站点。 |
| POST /v1/scans | scans:create | 对其中一个站点发起扫描。 |
| GET /v1/scans/{jobId} | reports:read | 你的账户发起的某次扫描的状态。 |
| GET /v1/reports | reports:read | 每个站点最近完成的扫描——每站最多 8 条,不是完整历史。 |
| GET /v1/reports/{jobId} | reports:read | 一份报告,内容与详细程度和你在应用里看到的相同。加上 ?locale=zh 返回中文。 |
GET /v1/me
curl https://app.geoready.org/api/v1/me -H "Authorization: Bearer geoready_…"
# 200
{
"plan": "gold",
"quota": { "limit": 260, "remaining": 259, "windowStart": "2026-10-08T00:00:00.000Z" },
"key": { "name": "Nightly report job", "scopes": ["reports:read", "scans:create"] }
}GET /v1/sites
curl https://app.geoready.org/api/v1/sites -H "Authorization: Bearer geoready_…"
# 200
{ "sites": [ { "siteKey": "example.com", "role": "OWNED" }, { "siteKey": "example.org", "role": "COMPETITOR" } ] }POST /v1/scans
curl -X POST https://app.geoready.org/api/v1/scans \
-H "Authorization: Bearer geoready_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: nightly-2026-10-13" \
-d '{"domain": "example.com"}'
# 202 — a new scan; 200 with "replayed": true when this Idempotency-Key was used before
{
"jobId": "0199d2c4-7c1e-7a55-9c7d-3f0f1c2b4a10",
"siteKey": "example.com",
"replayed": false,
"quota": { "limit": 260, "remaining": 259, "windowStart": "2026-10-08T00:00:00.000Z" }
}GET /v1/scans/{jobId}
curl https://app.geoready.org/api/v1/scans/0199d2c4-7c1e-7a55-9c7d-3f0f1c2b4a10 -H "Authorization: Bearer geoready_…"
# 200 — status is PENDING, RUNNING, SUCCEEDED, INCOMPLETE or FAILED
{
"jobId": "0199d2c4-7c1e-7a55-9c7d-3f0f1c2b4a10",
"siteKey": "example.com",
"status": "SUCCEEDED",
"createdAt": "2026-10-13T02:00:00.000Z",
"finishedAt": "2026-10-13T02:00:41.000Z"
}GET /v1/reports
curl https://app.geoready.org/api/v1/reports -H "Authorization: Bearer geoready_…"
# 200 — baseScore is null when a scan produced no score
{
"sites": [
{
"siteKey": "example.com",
"role": "OWNED",
"reports": [
{ "jobId": "0199d2c4-…", "status": "SUCCEEDED", "checkedAt": "2026-10-13T02:00:41.000Z", "baseScore": 72, "pagesLimit": 10 }
]
}
]
}GET /v1/reports/{jobId}
curl "https://app.geoready.org/api/v1/reports/0199d2c4-7c1e-7a55-9c7d-3f0f1c2b4a10?locale=en" -H "Authorization: Bearer geoready_…"
# 200 — abridged: every check row has id, displayClass, goal, outcome, evidence, fix, prompt
{
"reportId": "0199d2c4-…",
"siteKey": "example.com",
"checkedAt": "2026-10-13T02:00:41.000Z",
"complete": true,
"baseScore": 72,
"coverage": 1,
"fullAccess": true,
"baseModules": [ { "id": "F08", "displayClass": "scored-fail", "verdict": "fail", "kind": "scoring", "goal": "…", "outcome": "…", "evidence": [], "fix": "…", "prompt": "…" } ],
"agentModules": [ … ],
"extraModules": [ … ],
"paid": { "paidScore": 90, "pagesSampled": 10, "modules": [ … ] },
"ai": []
}
# 409 while the scan has no finished report
{ "code": "report-not-ready", "message": "…", "details": { "status": "RUNNING" } }发起扫描
- 站点必须已经在账户的站点列表里。API 不会替你添加站点。
- 每次扫描消耗一次每周额度——与应用共用同一个额度。没有单独的 API 额度,API 也不能发起免费检测。
- 应答里有扫描的 id 和你当前的额度。轮询状态接口,直到状态为 SUCCEEDED、INCOMPLETE 或 FAILED,然后读取报告。
安全地重试
- 带上 Idempotency-Key 请求头(8–128 个可打印 ASCII 字符),请求就可以安全地重试。
- 用相同的键、相同的域名再次请求,会返回第一次的扫描,"replayed": true,状态码 200,不扣额度——即使此后额度已用完、站点已从列表移除,或那次扫描失败了。想重新扫描,请换一个新的键。
- 同一个键配不同的域名会被拒绝(409)。
- 键属于发送它的那把 API key:两把 key 可以各自使用相同的值,互不影响。
- 没有固定的有效期:只要那次扫描还在,键就一直被记住。
- 套餐结束后,重放与其他请求一样被拒绝(403)。
错误
所有错误的结构相同:一个稳定的代码、一句给人看的说明,有时带有详情。
{ "code": "quota-exhausted", "message": "…", "details": { "quota": { "limit": 260, "remaining": 0, "windowStart": "…" } } }| 状态码 | 代码 | 何时出现 |
|---|---|---|
| 400 | invalid-request | 请求体、参数或 Idempotency-Key 请求头格式不对。 |
| 400 | invalid-domain | 无法识别这个域名。 |
| 401 | unauthorized | 没有 key,或 key 不存在、已撤销、已过期。这几种情况的应答完全相同。 |
| 403 | api-not-in-plan | 你的套餐不包含 API(或已结束)。 |
| 403 | insufficient-scope | 这把 key 没有此请求需要的权限范围。 |
| 403 | quota-exhausted | 本周额度已用完。 |
| 404 | not-found | 路径、扫描或报告不存在——或者你无权读取。 |
| 404 | site-not-tracked | 这个域名不在你账户的站点列表里。 |
| 409 | idempotency-conflict | 这个 Idempotency-Key 已经用于另一个域名。 |
| 409 | report-not-ready | 这次扫描还没有成品报告;details.status 给出当前状态。 |
| 429 | rate-limited | 请求过多。Retry-After 给出何时可以重试。 |
| 500 | internal-error | 我们这边出了问题。 |
Webhook
本服务尚未开启 Webhook。目前请轮询扫描状态接口。
限流
- 每把 key 每分钟 60 次,每个账户每分钟 120 次,每个地址每分钟 300 次。
- 每把 key 每分钟最多发起 10 次扫描。
- 每个地址每分钟最多 30 次缺少 key 或 key 错误的请求。
- 超出限制会得到 429,并带有 Retry-After 请求头。
API 不做的事
- 免费检测,以及对不在你列表里的站点的扫描。
- 添加或删除站点、账单、账户设置、key 管理。
- 发起 AI 分析,或更改报告是否分享。