> For the complete documentation index, see [llms.txt](https://docs.ipcheck.ing/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ipcheck.ing/developer/zh/reference/api-endpoints.md).

# API 端点

{% hint style="warning" %}
**这不是一个公共 API。** 这些路由用于服务 MyIP 自己的前端。它们受以下规则限制： `Referer` 检查，它们的形状会在不通知的情况下变化，而且没有版本管理、弃用策略或稳定性契约。不要针对其他实例的 `/api/*`。本页记录它们，以便 **你可以运维并调试你自己的部署**.
{% endhint %}

每个路由都定义在 `backend-server.js` 中，并挂载在后端服务器（`BACKEND_PORT`，默认 `11966`）。在生产环境中， `frontend-server.js` 代理 `/api` 从 `FRONTEND_PORT` （默认 `18966`）到后端，因此从部署外部看一切都位于 `/api` 同一个来源上。参见 [后端](/developer/zh/architecture/backend.md).

## 全局中间件

这些适用于 **每个** `/api/*` 路由，按挂载顺序生效。

| 顺序 | 中间件                                | 行为                                                                                                      |
| -- | ---------------------------------- | ------------------------------------------------------------------------------------------------------- |
| 1  | `pino-http`                        | 仅当 `LOG_HTTP=true`时。记录方法、URL 和状态。它在限流器之前挂载，因此 429 也会被记录。                                                |
| 2  | `express-rate-limit`               | 仅当 `SECURITY_RATE_LIMIT` 非零时启用。每个 IP 每 20 分钟窗口最多 N 个请求 → `429 {"message":"请求过多"}`。跳过 `/api/monitoring`. |
| 3  | `express-slow-down`                | 仅当 `SECURITY_DELAY_AFTER` 非零时启用。每个 IP 在 60 分钟窗口内请求 N 次后，增加 `命中次数 × 400 毫秒` 的延迟。跳过 `/api/monitoring`.    |
| 4  | `express.json({ limit: '500kb' })` | JSON 请求体解析。超过 500 KB 的请求体会得到一个原始的 `413`.                                                                |
| 5  | `Cache-Control: no-store`          | **每个路由的默认值。** 想要边缘缓存的路由会显式覆盖它。                                                                          |
| 6  | `requireReferer`                   | 拒绝任何其 `Referer` hostname 不是 `localhost` 或不在 `ALLOWED_DOMAINS`.                                          |

### Referer 入口

`requireReferer` 是每个请求首先遇到的检查。匹配是针对 `['localhost', ...ALLOWED_DOMAINS.split(',')]`.

| 条件                    | 响应                       |
| --------------------- | ------------------------ |
| 没有 `Referer` header   | `403 {"error":"你在做什么？"}` |
| `Referer` 已提供但主机名不被允许 | `403 {"error":"访问被拒绝"}`  |
| `Referer` 无法解析为 URL   | `403 {"error":"访问被拒绝"}`  |

这就是为什么 `curl http://your-host:18966/api/configs` 总是返回 403 —— curl 不发送任何 `Referer`。参见 [安全选项](/developer/zh/configuration/security-options.md).

### 缓存

该 `cacheable(seconds)` 中间件工厂包装 `res.json` 并设置 `Cache-Control: public, max-age=<seconds>` **仅在 2xx 响应上**，因此错误永远不会在边缘被缓存。它还会将该值存入 `res.locals.cacheControl` ，供流式传输二进制并绕过 `res.json` （只有 `/api/map` 会这样）。

任何未标记为可缓存的内容都继承全局的 `no-store` 默认值。

### 守卫

守卫位于 `common/guards.js` 并按路由应用。它们都会以 `400` 以及一个 JSON `错误` 字符串拒绝。

| 守卫                         | 读取         | 验证                              | 错误                        |
| -------------------------- | ---------- | ------------------------------- | ------------------------- |
| `requireValidIP()`         | `?ip`      | 有效的 IPv4 或 IPv6                 | `未提供 IP 地址` / `无效的 IP 地址` |
| `requireValidDomain()`     | `?domain`  | 语法正确的域名；将值原地转为小写，以便边缘缓存看到一个规范形式 | `未提供域名` / `无效的域名`         |
| `requireValidPrefix()`     | `?prefix`  | 格式正确的 CIDR（任意长度——前端决定量化方式）      | `未提供前缀` / `无效的前缀`         |
| `requireValidASN()`        | `?asn`     | 数字，可选 `AS` 前缀；会原地去除该前缀          | `未提供 ASN` / `无效的 ASN`     |
| `requireValidProviderId()` | `?id`      | 位于 service-status 提供商 slug 白名单中 | `未提供提供商 ID` / `无效的提供商 ID` |
| `requireValidReportId()`   | 路由参数 `:id` | 恰好 22 个 base64url 字符（16 个随机字节）  | `无效的报告 ID`                |

## IP 地理定位

所有这些都返回相同的标准化形状（`ip`, `city`, `region`, `country`, `country_name`, `country_code`, `latitude`, `longitude`, `asn`, `org`），由 `makeGeoHandler` 工厂函数生成，位于 `common/geo-handler.js`。参见 [IP 数据源](/developer/zh/architecture/ip-data-sources.md).

| 路由                     | 参数                     | 守卫 · 缓存                | 用途                                                                                                                   |
| ---------------------- | ---------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `GET /api/ipinfo`      | `ip`                   | `requireValidIP` · 1 天 | 通过 ipinfo.io 进行地理定位。 `IPINFO_API_KEY` 可选。                                                                            |
| `GET /api/ipapicom`    | `ip`, `lang` （默认 `en`) | `requireValidIP` · 1 天 | 通过 ip-api.com 进行地理定位。无需密钥。                                                                                           |
| `GET /api/ipsb`        | `ip`                   | `requireValidIP` · 1 天 | 通过 api.ip.sb 进行地理定位。无需密钥。                                                                                            |
| `GET /api/ipapiis`     | `ip`                   | `requireValidIP` · 1 天 | 通过 api.ipapi.is 进行地理定位；还返回 `isHosting` / `isProxy`。需要 `IPAPIIS_API_KEY`.                                             |
| `GET /api/ip2location` | `ip`                   | `requireValidIP` · 1 天 | 通过 ip2location.io 进行地理定位。需要 `IP2LOCATION_API_KEY`.                                                                   |
| `GET /api/maxmind`     | `ip`, `lang` （默认 `en`) | `requireValidIP` · 1 天 | 本地 GeoLite2 City + ASN 查询。需要 MaxMind 凭据或预先填充的 `.mmdb`; **503** ，当数据库未加载时。                                            |
| `GET /api/ipchecking`  | `ip`, `lang` （默认 `en`) | `requireValidIP` · 不存储 | 通过私有的 IPCheck.ing API 进行地理定位。需要 `IPCHECKING_API_KEY` + `IPCHECKING_API_ENDPOINT`; `500 {"error":"缺少 API 密钥"}` ，没有它们。 |

`lang` 在 `/api/maxmind` 上会针对 `['zh-CN', 'en', 'fr', 'ru']`进行验证；其他任何值都会回退到 `en`. `lang` 在 `/api/ipapicom` 和 `/api/ipchecking` 则会在不做验证的情况下直接转发给上游。

地理处理器上的上游失败会返回 `500 {"error": "<message>"}`.

## 网络工具

| 路由                                    | 参数                                                                         | 守卫 · 缓存                    | 用途                                                                                                       |
| ------------------------------------- | -------------------------------------------------------------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------- |
| `GET /api/whois`                      | `q` （IP 或域名）                                                               | 内联 · 1 天                   | WHOIS / RDAP 查询。IP 会优先发送到 RDAP， `whoiser` 作为回退。                                                          |
| `GET /api/dnsresolver`                | `hostname`, `type`                                                         | 内联 · 不存储                   | 并行通过多个普通 DNS 和 DoH 解析器解析一个主机名，用于污染对比。                                                                    |
| `GET /api/dnsleaktest/session/:token` | 路由 `:token` （32 个十六进制字符）， `lang` （默认 `zh-CN`)                              | 内联 · 不存储                   | 获取增强的 DNS 泄漏会话结果。转发请求头（包括 `Authorization`）并原样透传上游状态。需要 `IPCHECKING_API_KEY` + `IPCHECKING_API_ENDPOINT`. |
| `GET /api/ooni-blocking`              | `domain`                                                                   | `requireValidDomain` · 1 天 | 基于 30 天 UTC 窗口的 OONI 审查聚合；同时查询根域和 `www.` 变体并合并。                                                          |
| `GET /api/globalping-probes`          | —                                                                          | — · 7 天                    | 在线 Globalping 探针的紧凑国家清单，用于 MTR / 延迟 / 审查国家选择器。                                                           |
| `GET /api/macchecker`                 | `mac` （去除后恰好为 12 个十六进制字符 `:` 和 `-`)                                        | 内联 · 30 天                  | 通过 maclookup.app 进行 IEEE OUI 厂商查询。 `MAC_LOOKUP_API_KEY` 可选。                                              |
| `GET /api/map`                        | `latitude`, `longitude`, `language` （2 个字母）， `CanvasMode` (`Dark` （用于深色样式） | 内联 · 1 年                   | 代理 Google Static Maps JPEG。 **返回二进制**，不是 JSON。需要 `GOOGLE_MAP_API_KEY`.                                   |
| `GET /api/invisibility`               | `id` （28 个字母数字字符）                                                          | 内联 · 不存储                   | 轮询代理检测结果。上游 404 会被转换为 `200 {"status":"pending"}`。需要 `IPCHECKING_API_KEY` + `IPCHECKING_API_ENDPOINT`.    |

## ASN 与 BGP

| 路由                          | 参数              | 守卫 · 缓存                     | 用途                                                                                                                |
| --------------------------- | --------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `GET /api/cfradar`          | `asn`           | 内联 · 30 天                   | 某个 ASN 的 Cloudflare Radar 流量 / 采用情况分段。部分分段失败会被记录并继续提供；完全失败会返回 500。需要 `CLOUDFLARE_API_KEY`.                        |
| `GET /api/asn-history`      | `prefix` （CIDR） | `requireValidPrefix` · 30 天 | 来自 RIPEstat routing-history 的前缀历史 BGP 通告，并带有相对可见性百分比。上游非 2xx 返回 `502 {"error":"上游错误"}`. `RIPESTAT_SOURCE_APP` 可选。 |
| `GET /api/asn-connectivity` | `asn`           | `requireValidASN` · 30 天    | 从某个 ASN 到 Tier-1 骨干网的上游拓扑图，基于本地 CAIDA as-rel 快照构建。                                                                |

## 服务状态

这两个处理器都读取由后台轮询器按固定 5 分钟计划维护的内存快照。请求时二者都不会访问上游，因此请求量不会影响上游负载。

| 路由                               | 参数              | 守卫 · 缓存                         | 用途                      |
| -------------------------------- | --------------- | ------------------------------- | ----------------------- |
| `GET /api/service-status`        | —               | — · 5 分钟                        | 概览：每个提供商一个状态灯，没有大型详情数组。 |
| `GET /api/service-status/detail` | `id` （提供商 slug） | `requireValidProviderId` · 5 分钟 | 某一提供商的子组件以及近期事件。        |

## 平台

| 路由                               | 参数                             | 守卫 · 缓存                      | 用途                                                                     |
| -------------------------------- | ------------------------------ | ---------------------------- | ---------------------------------------------------------------------- |
| `GET /api/configs`               | —                              | — · 1 小时                     | 前端的功能标志。仅返回 **布尔值**，从不返回键值。                                            |
| `GET /api/github-stars`          | —                              | — · 1 天                      | 的星标数 `jason5ng32/MyIP`，匿名获取。                                           |
| `GET /api/getuserinfo`           | —（转发请求头，包括 `Authorization`)    | — · 不存储                      | 来自配套 API 的已登录用户资料。需要 `IPCHECKING_API_KEY` + `IPCHECKING_API_ENDPOINT`. |
| `PUT /api/updateuserachievement` | JSON 请求体                       | — · 不存储                      | 记录一次成就解锁。需要 `IPCHECKING_API_KEY` + `IPCHECKING_API_ENDPOINT`.          |
| `POST /api/report`               | JSON 请求体 `{ report, ttlDays }` | schema 白名单 + 大小上限 · 不存储      | 将可分享的诊断报告存储到 Workers KV，并返回其 id。需要所有三个 `CLOUDFLARE_*` 变量。              |
| `GET /api/report/:id`            | 路由 `:id` （22 个字符）              | `requireValidReportId` · 不存储 | 为只读端读取已存储的报告 `/r/:id` 页面。需要相同的三个 `CLOUDFLARE_*` 变量。                    |
| `POST /api/monitoring`           | 原始信封正文（最大 10 MB）               | 自有限流器 · no-store             | 第一方 Sentry 隧道。 **仅在……时挂载 `VITE_SENTRY_DSN_FRONTEND` 已设置。**             |

### `/api/configs` 响应

每个字段都是布尔值。 `originalSite` 是 `true` 仅当请求的 `Referer` 主机名属于 IPCheck.ing 的规范主机名之一时。

{% code title="GET /api/configs" %}

```json
{
  "map": false,
  "ipInfo": false,
  "ipChecking": false,
  "ip2location": false,
  "originalSite": false,
  "cloudFlare": false,
  "ipapiis": false,
  "reportSharing": false
}
```

{% endcode %}

### 报告共享状态码

| 状态码   | 含义                                               |
| ----- | ------------------------------------------------ |
| `503` | 这三个 `CLOUDFLARE_*` 变量未全部设置。                      |
| `400` | 报告正文未通过模式验证（`{"error":"无效报告","details":[...]}`). |
| `413` | 序列化后的报告超过 256 KB。                                |
| `404` | 在 `GET`：未找到报告或其 KV TTL 已过期。                      |

`ttlDays` 必须是 `1`, `3` 或 `7`；任何其他值都会被静默强制降为 `1`.

### `/api/monitoring`

仅在……时挂载 `VITE_SENTRY_DSN_FRONTEND` 设置在 **后端进程**。它有一个专用限流器：每个 IP 在 20 分钟窗口内 600 个请求，并且会被两个全局限流器明确跳过——遥测共享应用配额正是错误报告悄然失效的方式。正文使用 `express.raw` 使用一个通配类型匹配器进行解析，因为 Sentry Replay 信封发送时没有 `Content-Type` 任何内容。

## 缓存 TTL 摘要

| TTL        | 路由                                                                                                                                                                                                            |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 5 分钟       | `/api/service-status`, `/api/service-status/detail`                                                                                                                                                           |
| 1 小时       | `/api/configs`                                                                                                                                                                                                |
| 1 天        | `/api/ipinfo`, `/api/ipapicom`, `/api/ipsb`, `/api/ipapiis`, `/api/ip2location`, `/api/maxmind`, `/api/whois`, `/api/github-stars`, `/api/ooni-blocking`                                                      |
| 7 天        | `/api/globalping-probes`                                                                                                                                                                                      |
| 30 天       | `/api/cfradar`, `/api/asn-history`, `/api/asn-connectivity`, `/api/macchecker`                                                                                                                                |
| 1 年        | `/api/map`                                                                                                                                                                                                    |
| `no-store` | 其他所有内容 — `/api/ipchecking`, `/api/dnsresolver`, `/api/dnsleaktest/session/:token`, `/api/invisibility`, `/api/getuserinfo`, `/api/updateuserachievement`, `/api/report`, `/api/report/:id`, `/api/monitoring` |

TTL 的选择与每个上游的自然刷新节奏相匹配。共享报告会刻意保持 `no-store` 有意如此：边缘缓存可能会在报告的 KV 过期后仍提供它，而私有诊断数据不应放在公共缓存中。

## 非`/api` 路由

`frontend-server.js` 则提供其他所有内容：静态 `dist/` 输出，并按资源类别设置 `Cache-Control`，以及一个 SPA 历史回退，它会返回 `index.html` （带 `no-store`）用于最终路径段没有文件扩展名的 GET 导航。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.ipcheck.ing/developer/zh/reference/api-endpoints.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
