Публичный API

JSON-эндпоинты, которыми этот сайт пользуется для собственных проверок, описаны так, как они работают сегодня. Только чтение, регистрация не нужна.

Без API-ключей, без SLA, может изменитьсяНет ни API-ключей, ни платных тарифов, ни обязательств по доступности или времени ответа. У API нет версий: поля могут добавляться, переименовываться и удаляться без предупреждения, а эндпоинт при злоупотреблениях может быть ограничен сильнее или отключён. Не стройте на нём то, что серьёзно ломается, когда запрос не удался.

Общие правила

Все эндпоинты отвечают на GET-запросы в формате JSON (UTF-8). Домен передаётся как сегмент пути; интернационализированные имена (в том числе кириллические) можно отправлять в Unicode или в виде A-меток (xn--…). Даты даются в формате ISO 8601 по UTC.

Каждое наблюдение делается из одной серверной локации в момент запроса и сообщает, откуда оно получено: в каждом разделе есть status, source и observedAt. Раздел со статусом "error" или "unsupported" не удалось проверить. Это никогда не доказывает, что записи нет, а состояние регистрации "not-found" никогда не гарантирует, что имя можно зарегистрировать.

Зарезервированные для документации имена (example.com, example.net, example.org и всё в зонах .example, .test, .invalid и .localhost) никогда не проверяются вживую: на них приходит ответ 422. Примеры ответов на этой странице используют example.com и документационные диапазоны IP-адресов только для иллюстрации; чтобы попробовать API, возьмите домен, которым управляете вы.

Отчёт о домене

GET /api/lookup/{domain}

Один отчёт, в котором до пяти независимых разделов: registration (RDAP, а для некоторых реестров без RDAP — WHOIS через порт 43), dns, mail (MX, SPF, DMARC), hosting (от IP-адреса к ASN и сети) и web (один HTTPS-запрос и TLS-сертификат, с которым на него ответили). Сбой в одном разделе не делает неудачным весь отчёт.

Начальное "www." отбрасывается. Для поддомена регистрационные данные проверяются по регистрируемому домену, а остальные разделы — по тому имени хоста, которое вы отправили.

Параметры

ИмяГдеОписание
domainпутьДомен или имя хоста, не более 253 символов.
sectionsстрока запросаПеречисленное через запятую подмножество из registration, dns, mail, hosting, web. Неизвестные имена игнорируются; если ни одно имя не подходит, ответ — 400. Разделы, которые вы не запрашивали, возвращаются со статусом "skipped". По умолчанию: все пять.

Коды состояния

КодЗначение
200{ "report": … }. Отправляется с заголовком Cache-Control: public, max-age=60, s-maxage=300.
400{ "error": "invalid-domain", "reason": … }, если имя не прошло проверку, или { "error": "invalid-sections", "allowed": […] }.
422{ "error": "reserved-name" } для документационных и тестовых имён или { "error": "blocked-target" }, если имя разрешается только в частные или зарезервированные адреса: к ним сервис не подключается.
429{ "error": "rate-limited", "retryAfterSeconds": n } и заголовок Retry-After.
Пример запроса
curl -s "https://www.orbitprobe.com/api/lookup/yourdomain.com?sections=registration,dns"
Пример ответа (значения условные, ответ сокращён)
{
  "report": {
    "input": "example.com",
    "ascii": "example.com",
    "unicode": "example.com",
    "registrableDomain": "example.com",
    "isSubdomain": false,
    "tld": "com",
    "generatedAt": "2026-01-01T12:00:00.000Z",
    "dataMode": "live",
    "cached": false,
    "registration": {
      "status": "ok",
      "source": "RDAP · rdap.registry.example",
      "observedAt": "2026-01-01T12:00:00.000Z",
      "durationMs": 412,
      "state": "registered",
      "registrableDomain": "example.com",
      "registrar": "Example Registrar, Inc.",
      "registrarIanaId": "9999",
      "registrarUrl": null,
      "abuseEmail": "abuse@registrar.example",
      "createdAt": "2015-03-01T00:00:00Z",
      "updatedAt": "2025-02-10T00:00:00Z",
      "expiresAt": "2027-03-01T00:00:00Z",
      "statusCodes": ["client transfer prohibited"],
      "nameservers": ["ns1.example.net", "ns2.example.net"],
      "dnssec": false,
      "registrant": { "organization": null, "country": null, "redacted": true },
      "rdapUrl": "https://rdap.registry.example/domain/example.com"
    },
    "dns": {
      "status": "ok",
      "source": "DNS · public recursive resolvers",
      "observedAt": "2026-01-01T12:00:00.000Z",
      "durationMs": 95,
      "resolver": "1.1.1.1, 8.8.8.8",
      "records": [
        { "type": "A", "name": "example.com", "value": "203.0.113.10", "ttl": 300 },
        { "type": "AAAA", "name": "example.com", "value": "2001:db8::10", "ttl": 300 },
        { "type": "MX", "name": "example.com", "value": "mail.example.com", "priority": 10 },
        { "type": "TXT", "name": "example.com", "value": "v=spf1 mx -all" }
      ],
      "failedTypes": [],
      "nxdomain": false
    },
    "mail": {
      "status": "ok",
      "source": "DNS",
      "observedAt": "2026-01-01T12:00:00.000Z",
      "durationMs": 140,
      "mx": [{ "priority": 10, "host": "mail.example.com", "addresses": ["203.0.113.25"], "provider": null }],
      "spf": "v=spf1 mx -all",
      "dmarc": "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com",
      "dkim": "selector-required",
      "findings": [{ "level": "ok", "code": "…", "title": "…", "detail": "…" }]
    },
    "hosting": {
      "status": "ok",
      "source": "IP lookups",
      "observedAt": "2026-01-01T12:00:00.000Z",
      "durationMs": 310,
      "addresses": [
        { "address": "203.0.113.10", "family": 4, "reverse": null, "asn": 64500, "asName": "EXAMPLE-NET", "prefix": "203.0.113.0/24", "country": null, "networkName": null, "networkOrg": null, "edge": null }
      ],
      "summary": "EXAMPLE-NET (AS64500)"
    },
    "web": {
      "status": "ok",
      "source": "HTTPS request",
      "observedAt": "2026-01-01T12:00:00.000Z",
      "durationMs": 520,
      "finalUrl": "https://example.com/",
      "httpStatus": 200,
      "redirects": [],
      "serverHeader": null,
      "poweredBy": null,
      "hsts": true,
      "responseMs": 180,
      "page": { "title": "Example Domain", "description": null, "keywords": null },
      "headers": [{ "name": "content-type", "value": "text/html; charset=UTF-8" }],
      "tls": {
        "protocol": "TLSv1.3",
        "issuer": "Example CA",
        "subject": "example.com",
        "validFrom": "2025-12-01T00:00:00.000Z",
        "validTo": "2026-03-01T00:00:00.000Z",
        "altNames": ["example.com", "www.example.com"],
        "authorized": true,
        "authorizationError": null
      }
    }
  }
}

Распространение DNS

GET /api/propagation/{domain}

Задаёт один и тот же вопрос каждому резолверу из фиксированного списка публичных рекурсивных резолверов и группирует одинаковые ответы. Читайте результат как «9 из 10 резолверов вернули этот ответ»: он ничего не говорит о резолверах, которых нет в списке, а anycast-резолвер в другом регионе может ответить иначе.

Здесь "www." сохраняется, потому что вопрос задаётся именно о том имени, которое вы отправили.

Параметры

ИмяГдеОписание
domainпутьИмя хоста, о котором задаётся вопрос, не более 253 символов.
typeстрока запросаA, AAAA, NS, MX, TXT или CNAME (регистр не важен). По умолчанию: A.

Коды состояния

КодЗначение
200{ "report": … }: по одной записи на каждый резолвер (статус answer, no-records, nxdomain, timeout или error), группы различающихся ответов и счётчики total, answered и failed. Cache-Control: public, max-age=30.
400{ "error": "invalid-domain" } или { "error": "invalid-type" }.
422{ "error": "reserved-name" }.
429{ "error": "rate-limited" } и заголовок Retry-After.
Пример запроса
curl -s "https://www.orbitprobe.com/api/propagation/www.yourdomain.com?type=A"
Пример ответа (значения условные, ответ сокращён)
{
  "report": {
    "ascii": "www.example.com",
    "unicode": "www.example.com",
    "type": "A",
    "generatedAt": "2026-01-01T12:00:00.000Z",
    "cached": false,
    "answers": [
      {
        "resolver": { "id": "cloudflare", "name": "Cloudflare", "address": "1.1.1.1", "operatorCountry": "US" },
        "status": "answer",
        "values": ["203.0.113.10"],
        "ttl": 300,
        "durationMs": 21,
        "group": 0
      },
      {
        "resolver": { "id": "quad9", "name": "Quad9", "address": "9.9.9.9", "operatorCountry": "CH" },
        "status": "timeout",
        "values": [],
        "ttl": null,
        "durationMs": 2800,
        "group": null
      }
    ],
    "groups": [{ "values": ["203.0.113.10"], "count": 9 }],
    "total": 10,
    "answered": 9,
    "failed": 1,
    "consistent": false
  }
}

Single-purpose checks

GET /api/tools/{tool}/{domain}

The engines behind the tool result pages, one request per check: dkim-checker, subdomain-finder, mta-sts-checker, tls-rpt-checker, bimi-checker, dnssec-checker, reverse-dns and security-txt-checker (the same keys as the site's tool URLs). The answer carries ok, tool, source, observedAt and cached, and the engine's report unchanged.

A query that failed stays visible inside the report as a state such as "failed", "error" or "unknown", or as a finding: it is never turned into "no record". A leading "www." is removed, except for reverse-dns, which keeps it and also accepts an IPv4 or IPv6 address.

Параметры

ИмяГдеОписание
toolпутьOne of the eight tool keys above. Anything else answers 404.
domainпутьDomain or hostname, at most 253 characters; for reverse-dns also an IP address (percent-encode the colons of an IPv6 address).
selectorстрока запросаdkim-checker and bimi-checker only. DKIM: the s= selector of a real message; leave it out to try a fixed list of common provider selectors. BIMI: default "default".

Коды состояния

КодЗначение
200{ "ok": true, "tool": …, "source": …, "observedAt": …, "cached": …, "report": … }. Sent with Cache-Control: public, max-age=60, s-maxage=300.
400{ "ok": false, "code": "invalid-domain", "reason": … } or { "ok": false, "code": "invalid-selector" }.
404{ "ok": false, "code": "unknown-tool", "tools": […] }.
422{ "ok": false, "code": "reserved-name" } for documentation and test names.
429{ "ok": false, "code": "rate-limited", "retryAfterSeconds": n } with a Retry-After header.
Пример запроса
curl -s "https://www.orbitprobe.com/api/tools/tls-rpt-checker/yourdomain.com"
Пример ответа (значения условные, ответ сокращён)
{
  "ok": true,
  "tool": "tls-rpt-checker",
  "source": "Public DNS (1.1.1.1, 8.8.8.8)",
  "observedAt": "2026-01-01T12:00:00.000Z",
  "cached": false,
  "report": {
    "ascii": "example.com",
    "unicode": "example.com",
    "observedAt": "2026-01-01T12:00:00.000Z",
    "durationMs": 38,
    "cached": false,
    "host": "_smtp._tls.example.com",
    "state": "ok",
    "records": ["v=TLSRPTv1; rua=mailto:tls-reports@example.com"],
    "parsed": { "version": "TLSRPTv1", "valid": true, "rua": [{ "raw": "mailto:tls-reports@example.com", "scheme": "mailto", "valid": true, "target": "example.com" }] },
    "findings": [{ "level": "ok", "code": "RPT_OK", "params": { "n": 1 } }]
  }
}

Счётчики активности

GET /api/stats/activity

Числа, которые стоят за глобусом на главной странице: сколько проверок выполнено за последние 24 часа, по стране посетителя и по стране, в которой зарегистрирован проверенный блок адресов. Существуют только коды стран и счётчики; ни IP-адреса, ни доменные имена не сохраняются. Счётчики живут в памяти одного серверного процесса, поэтому обнуляются при перезапуске и различаются между экземплярами: считайте их приблизительным сигналом, а не статистикой.

Коды состояния

КодЗначение
200Снимок счётчиков. Cache-Control: public, max-age=30. Для этого эндпоинта лимита запросов нет.
Пример запроса
curl -s "https://www.orbitprobe.com/api/stats/activity"
Пример ответа (значения условные, ответ сокращён)
{
  "windowHours": 24,
  "total": 3,
  "visitors": [{ "country": "TR", "count": 2 }, { "country": "DE", "count": 1 }],
  "hosting": [{ "country": "US", "count": 3 }],
  "generatedAt": "2026-01-01T12:00:00.000Z"
}

Лимиты запросов

Лимиты считаются по IP-адресу клиента, отдельно для каждого семейства эндпоинтов. Результат, отданный из серверного кэша, в лимит не засчитывается. Когда лимит достигнут, приходит ответ 429 с заголовком Retry-After; подождите указанное время, а не повторяйте запрос в цикле.

Бейдж и инструменты MCP работают на тех же движках, поэтому лимиты у них общие с API.

ЭндпоинтЛимит на IP-адресСерверный кэш
/api/lookup/{domain}30 в минуту · 300 в час300 с (30 с, если один из разделов не удалось проверить)
/api/propagation/{domain}12 в минуту · 120 в час60 с
/api/tools/{tool}/{domain}10 в минуту · 80 в час (per tool; DKIM 10 в минуту · 80 в час, subdomain finder 5 в минуту · 40 в час)300 с / 1800 с
/api/stats/activityнетнет

Кэширование

Отчёты кэшируются на сервере по имени хоста и списку разделов, поэтому повторный запрос тех же разделов в пределах времени кэша возвращает то же наблюдение с "cached": true и исходными значениями observedAt. Перечисленные выше заголовки Cache-Control также позволяют браузерам и CDN короткое время использовать ответ повторно. Параметра, который принудительно запускает новую проверку, нет.

CORS

Эндпоинты на этой странице отправляют Access-Control-Allow-Origin: * и отвечают на предварительные запросы OPTIONS, только для метода GET. Их можно вызывать со страницы другого источника (origin). Cookie они никогда не читают, так что учётные данные не используются.

Добросовестное использование

Каждый запрос запускает реальные обращения к реестрам, DNS-резолверам и самому проверяемому сайту. Используйте API для интерактивных инструментов, панелей мониторинга и эпизодических проверок доменов, на которые у вас есть причина посмотреть. Не обходите списки доменов, не меняйте адреса, чтобы обойти лимиты, и не перепродавайте полученные данные. Политика допустимого использования действует для трафика API точно так же, как для сайта.

Политика допустимого использования

Outbound webhooks

Signed-in members can have watch alerts, expiry reminders and marketplace notices posted to a URL they own: a Slack incoming webhook or a generic https endpoint (workspace → Settings → Notification channels). This section documents what the receiver gets. It is outbound only: OrbitProbe never accepts requests on these URLs, and there is no inbound API.

Receivers must be https on a public host (default port, no credentials; private, local and reserved hosts are refused, and the host is resolved and checked again at send time). Redirects are not followed. A delivery counts as done when the receiver answers 2xx; after five failed deliveries in a row the webhook is switched off and the owner sees a notice in the workspace.

Request

ИмяОписание
POSTJSON body, Content-Type: application/json; charset=utf-8, User-Agent: OrbitProbe-Webhook/1.0
X-OrbitProbe-EventThe kind (see below).
X-OrbitProbe-DeliveryThe delivery id (ntf_<number>). Repeated on a retry, so you can de-duplicate.
X-OrbitProbe-SignatureGeneric endpoints only: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>"> with the signing secret shown in Settings.

Body (generic endpoints)

ИмяОписание
idDelivery id, the same as X-OrbitProbe-Delivery.
kindOne of the kinds below.
categorywatch, expiry, market or test.
title, textThe notification in the account’s language: the same wording the push notification carries. Counts are "N of M resolvers", never percentages.
urlWhere to look in the workspace.
createdAtWhen the event was queued (ISO 8601, UTC).
accountThe recipient’s account id, so one receiver can serve several accounts.
dataThe raw event as stored: domain, counts, listing id, amount in integer minor units and currency, and so on. Fields vary by kind; anything missing was not measured, never "no".
Body (generic endpoints)
{
  "id": "ntf_1042",
  "kind": "watch-changed",
  "category": "watch",
  "title": "Watch: unexpected change",
  "text": "example.com TXT: 9 of 12 resolvers return values that differ from the guarded baseline.",
  "url": "https://www.orbitprobe.com/app/watch/00000000-0000-4000-8000-000000000000",
  "createdAt": "2026-01-01T00:00:00.000Z",
  "account": "00000000-0000-4000-8000-0000000000aa",
  "data": {
    "domain": "example.com",
    "kind": "dns-change",
    "record_type": "TXT",
    "name": "example.com",
    "matched": 3,
    "total": 12,
    "failed": 0,
    "differing": 9,
    "watch_id": "00000000-0000-4000-8000-000000000000",
    "event_id": 77
  }
}

Body (Slack)

Slack incoming webhooks receive {"text": "…"} only: title, text and URL on three lines, no Block Kit and no attachments, so the message renders in every Slack client and in Slack-compatible receivers such as Mattermost or Rocket.Chat.

Body (Slack)
{
  "text": "*Watch: unexpected change*\nexample.com TXT: 9 of 12 resolvers return values that differ from the guarded baseline.\nhttps://www.orbitprobe.com/app/watch/…"
}

Verifying the signature

Recompute the HMAC over "<t>.<raw body>" with your secret, compare in constant time, and reject timestamps older than five minutes. Do not parse and re-serialise the JSON before hashing: sign the bytes as received.

Verifying the signature
// Node.js receiver: verify X-OrbitProbe-Signature before trusting the body
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(secret, rawBody, header, nowSeconds = Math.floor(Date.now() / 1000)) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header ?? '');
  if (!m || Math.abs(nowSeconds - Number(m[1])) > 300) return false;
  const expected = createHmac('sha256', secret).update(`${m[1]}.${rawBody}`).digest('hex');
  return timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(m[2], 'hex'));
}

Kinds

watch-changed, watch-target, watch-failed, watch-recovered, watch-partial · expiry · offer-received, offer-countered, offer-accepted, offer-rejected, offer-withdrawn, offer-expired, listing-submitted, listing-approved, listing-paused, listing-removed, agreement-cancelled · test (the "Send test" button).

Limits

At most 5 webhooks per account and 20 posts per account per hour; beyond that, events still reach the in-app inbox and e-mail but are not posted. Deliveries are retried up to five times over later runs of the five-minute job. An "offer-accepted" event records an agreement only: no payment, escrow or transfer is implied.

Описание OpenAPI

Машиночитаемый документ OpenAPI 3.1 для этих эндпоинтов генерируется из того же кода: /api/openapi.json

Частые вопросы

Нужен ли API-ключ?

Нет. Для API нет ни ключей, ни учётных записей. Вместо этого запросы ограничиваются по IP-адресу.

Можно ли полагаться на API в продакшене?

Только там, где допустим сбой. Нет ни SLA, ни версий; эндпоинты существуют, потому что нужны сайту, и описаны, чтобы ими могли пользоваться и вы. Кэшируйте результаты на своей стороне и обрабатывайте ответы 429 и 5xx.

Почему example.com возвращает 422?

Зарезервированные документационные и тестовые имена обслуживаются заготовленными данными внутри приложения и никогда не проверяются вживую. Чтобы попробовать API, возьмите реальный домен.

Означает ли сбой раздела, что записи не существует?

Нет. "error" и "unsupported" означают, что источник в тот момент не удалось прочитать с нашего сервера. Что было найдено, показывает только раздел со статусом "ok" или "partial", и только для типов, которых нет в списке failedTypes.