> 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/ru/reference/api-endpoints.md).

# API-эндпоинты

{% hint style="warning" %}
**Это не публичный API.** Эти маршруты существуют для обслуживания собственного фронтенда MyIP. Они защищены проверкой `Referer` referer, их форма меняется без уведомления, и нет версионирования, политики устаревания или контракта на стабильность. Не стройте интеграции с API другого экземпляра `/api/*`. На этой странице они задокументированы, чтобы **вы могли управлять своим развёртыванием и отлаживать его**.
{% endhint %}

Каждый маршрут определяется в `backend-server.js` и подключается на бэкенд-сервере (`BACKEND_PORT`, по умолчанию `11966`). В продакшене, `frontend-server.js` проксирует `/api` с `FRONTEND_PORT` (по умолчанию `18966`) на бэкенд, так что снаружи развёртывания всё находится под `/api` на одном origin. См. [Бэкенд](/developer/ru/architecture/backend.md).

## Глобальный middleware

Они применяются к **каждому** `/api/*` маршруту в порядке подключения.

| Порядок | Middleware                         | Поведение                                                                                                                                                                    |
| ------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1       | `pino-http`                        | Только когда `LOG_HTTP=true`. Логирует метод, URL и статус. Подключается до ограничителей, чтобы 429 тоже логировались.                                                      |
| 2       | `express-rate-limit`               | Только когда `SECURITY_RATE_LIMIT` не равно нулю. Максимум N запросов с одного IP за 20-минутное окно → `429 {"message":"Too Many Requests"}`. Пропускает `/api/monitoring`. |
| 3       | `express-slow-down`                | Только когда `SECURITY_DELAY_AFTER` не равно нулю. После N запросов с одного IP за 60-минутное окно добавляет `задержки × 400 мс` . Пропускает `/api/monitoring`.            |
| 4       | `express.json({ limit: '500kb' })` | Парсинг JSON-тела. Тела больше 500 КБ получают сырой `413`.                                                                                                                  |
| 5       | `Cache-Control: no-store`          | **По умолчанию для каждого маршрута.** Маршруты, которым нужно кэширование на краю, явно переопределяют его.                                                                 |
| 6       | `requireReferer`                   | Отклоняет любой запрос, у которого `Referer` hostname не `localhost` или находится в `ALLOWED_DOMAINS`.                                                                      |

### Проверка Referer

`requireReferer` — первое, с чем сталкивается каждый запрос. Сопоставление выполняется точным совпадением hostname с `['localhost', ...ALLOWED_DOMAINS.split(',')]`.

| Условие                                 | Ответ                             |
| --------------------------------------- | --------------------------------- |
| Нет `Referer` заголовка вообще          | `403 {"error":"Что вы делаете?"}` |
| `Referer` есть, но hostname не разрешён | `403 {"error":"Доступ запрещён"}` |
| `Referer` не удаётся разобрать как URL  | `403 {"error":"Доступ запрещён"}` |

Вот почему `curl http://your-host:18966/api/configs` всегда возвращает 403 — curl не отправляет `Referer`. См. [Параметры безопасности](/developer/ru/configuration/security-options.md).

### Кэширование

Middleware `cacheable(seconds)` оборачивает `res.json` и устанавливает `Cache-Control: public, max-age=<seconds>` **только для ответов 2xx**, поэтому ошибки никогда не кэшируются на краю. Также сохраняет значение в `res.locals.cacheControl` для обработчиков, которые передают бинарные данные и обходят `res.json` (только `/api/map` это делает).

Всё, что не помечено как cacheable, наследует глобальный `no-store` по умолчанию.

### Проверки

Проверки находятся в `common/guards.js` и применяются для каждого маршрута. Все они отклоняют с `400` и JSON `error` строкой.

| Проверка                   | Читает                  | Проверяет                                                                                                                   | Ошибки                                               |
| -------------------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `requireValidIP()`         | `?ip`                   | Действительный IPv4 или IPv6                                                                                                | `IP-адрес не указан` / `Неверный IP-адрес`           |
| `requireValidDomain()`     | `?domain`               | Синтаксически корректный домен; приводит значение к нижнему регистру на месте, чтобы edge-кэш видел одну каноническую форму | `Домен не указан` / `Неверный домен`                 |
| `requireValidPrefix()`     | `?prefix`               | Корректно сформированный CIDR (любой длины — фронтенд решает квантизацию)                                                   | `Префикс не указан` / `Неверный префикс`             |
| `requireValidASN()`        | `?asn`                  | Числовой, необязательный `AS` префикс; удаляет префикс на месте                                                             | `ASN не указан` / `Неверный ASN`                     |
| `requireValidProviderId()` | `?id`                   | Принадлежность к списку разрешённых slug поставщиков service-status                                                         | `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/ru/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` · no-store | Геолокация через частный API IPCheck.ing. Нужен `IPCHECKING_API_KEY` + `IPCHECKING_API_ENDPOINT`; `500 {"error":"Отсутствует API-ключ"}` без них. |

`lang` в `/api/maxmind` проверяется по `['zh-CN', 'en', 'fr', 'ru']`; всё остальное откатывается к `en`. `lang` в `/api/ipapicom` и `/api/ipchecking` передаётся вышестоящему сервису без проверки.

Сбои upstream в geo-обработчиках возвращают `500 {"error": "<message>"}`.

## Сетевые инструменты

| Маршрут                               | Параметры                                                                                | Проверка · Кэш                | Назначение                                                                                                                                                                                              |
| ------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /api/whois`                      | `q` (IP или домен)                                                                       | встроенно · 1 день            | WHOIS / RDAP lookup. IPs go to RDAP first with `whoiser` as fallback.                                                                                                                                   |
| `GET /api/dnsresolver`                | `hostname`, `type`                                                                       | встроенно · no-store          | Разрешает hostname через несколько обычных DNS и DoH-резолверов параллельно, для сравнения загрязнения.                                                                                                 |
| `GET /api/dnsleaktest/session/:token` | маршрут `:token` (32 hex chars), `lang` (по умолчанию `zh-CN`)                           | встроенно · no-store          | Получает улучшенный результат сессии DNS leak. Пересылает заголовки запроса (включая `Authorization`) и передаёт статус upstream без изменений. Нужен `IPCHECKING_API_KEY` + `IPCHECKING_API_ENDPOINT`. |
| `GET /api/ooni-blocking`              | `domain`                                                                                 | `requireValidDomain` · 1 день | Агрегированный результат OONI по цензуре за 30-дневное окно UTC; запрашивает и apex, и `www.` вариант и объединяет.                                                                                     |
| `GET /api/globalping-probes`          | —                                                                                        | — · 7 дней                    | Компактный список стран онлайн-проб Globalping для селекторов стран MTR / latency / censorship.                                                                                                         |
| `GET /api/macchecker`                 | `mac` (ровно 12 hex-символов после удаления `:` и `-`)                                   | встроенно · 30 дней           | Поиск поставщика IEEE OUI через maclookup.app. `MAC_LOOKUP_API_KEY` необязателен.                                                                                                                       |
| `GET /api/map`                        | `latitude`, `longitude`, `language` (2 буквы), `CanvasMode` (`Тёмная` для тёмного стиля) | встроенно · 1 год             | Проксирует JPEG из Google Static Maps. **Возвращает бинарные данные**, а не JSON. Нужен `GOOGLE_MAP_API_KEY`.                                                                                           |
| `GET /api/invisibility`               | `id` (28 буквенно-цифровых символов)                                                     | встроенно · no-store          | Опрос результата обнаружения прокси. Upstream 404 переводится в `200 {"status":"pending"}`. Нужен `IPCHECKING_API_KEY` + `IPCHECKING_API_ENDPOINT`.                                                     |

## ASN & BGP

| Маршрут                     | Параметры       | Проверка · Кэш                 | Назначение                                                                                                                                                                                                    |
| --------------------------- | --------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /api/cfradar`          | `asn`           | встроенно · 30 дней            | Сегменты трафика / внедрения Cloudflare Radar для ASN. Частичные сбои сегментов логируются и возвращаются; полный сбой возвращает 500. Нужен `CLOUDFLARE_API_KEY`.                                            |
| `GET /api/asn-history`      | `prefix` (CIDR) | `requireValidPrefix` · 30 дней | Исторические BGP-объявления для префикса из RIPEstat routing-history, с относительными процентами видимости. Upstream не-2xx возвращает `502 {"error":"Upstream error"}`. `RIPESTAT_SOURCE_APP` необязателен. |
| `GET /api/asn-connectivity` | `asn`           | `requireValidASN` · 30 дней    | Граф топологии upstream от ASN к магистральным сетям Tier-1, построенный по локальному снимку CAIDA as-rel.                                                                                                   |

## Состояние сервисов

Оба обработчика читают снимок в памяти, поддерживаемый фоновым опросчиком по фиксированному 5-минутному расписанию. Ни один не обращается к upstream во время запроса, поэтому объём запросов никогда не влияет на нагрузку upstream.

| Маршрут                          | Параметры              | Проверка · Кэш                   | Назначение                                                                         |
| -------------------------------- | ---------------------- | -------------------------------- | ---------------------------------------------------------------------------------- |
| `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`) | — · no-store                                       | Профиль вошедшего пользователя из сопутствующего API. Нужен `IPCHECKING_API_KEY` + `IPCHECKING_API_ENDPOINT`.       |
| `PUT /api/updateuserachievement` | JSON-тело                                              | — · no-store                                       | Записывает получение достижения. Нужен `IPCHECKING_API_KEY` + `IPCHECKING_API_ENDPOINT`.                            |
| `POST /api/report`               | JSON-тело `{ report, ttlDays }`                        | белый список схем + ограничение размера · no-store | Сохраняет общий диагностический отчёт в Workers KV и возвращает его id. Требует все три `CLOUDFLARE_*` переменные.  |
| `GET /api/report/:id`            | маршрут `:id` (22 символа)                             | `requireValidReportId` · no-store                  | Читает сохранённый отчёт для режима только чтения `/r/:id` страница. Требуются те же три `CLOUDFLARE_*` переменные. |
| `POST /api/monitoring`           | сырое тело конверта (макс. 10 МБ)                      | собственный лимитер · 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 КБ.                                              |
| `404` | При `GET`: отчёт не найден или истёк TTL его KV.                                     |

`ttlDays` должно быть `1`, `3` или `7`; любое другое значение молча приводится к `1`.

### `/api/monitoring`

Монтируется только когда `VITE_SENTRY_DSN_FRONTEND` устанавливается в **процессе бэкенда**. У него есть отдельный лимитер на 600 запросов на IP за 20-минутное окно, и он явно пропускается обоими глобальными лимитерами — телеметрия, делящая квоту приложения, — вот как отчётность об ошибках тихо умирает. Тело разбирается с помощью `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 подбираются в соответствии с естественным ритмом обновления каждого upstream. Общие отчёты остаются `no-store` намеренно: edge-кэш может отдать отчёт уже после истечения его KV TTL, а приватные диагностические данные не должны попадать в публичный кэш.

## Не-`/api` маршруты

`frontend-server.js` обслуживает всё остальное: статический `dist/` вывод с поклассовым `Cache-Control`, а также fallback истории 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/ru/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.
