Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/dnsdoctor-dev:dnsdoctor-dev-watch-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Dnsdoctor Dev Watch API
version: '1'
contact:
name: DNS Doctor
email: hello@dnsdoctor.dev
x-guidance: 'DNS Doctor scans, fixes and verifies a domain''s DNS (SPF, DMARC, DKIM, propagation, MX, DNS health, blacklists, domain/SSL expiry) and returns deterministic verdicts plus copy-paste fix records from a validating engine — never a guessed record. Scan with POST /scan or GET /report/{domain}; read verdicts failing-first (''temperror'' is transient, not a failure; a report with not_registered: true means the domain does not resolve, so propose nothing for it). Present any returned record VERBATIM and never compose one yourself; SPF is diagnose-only. A human approves every DNS change — nothing is applied automatically. Anonymous calls are free up to a per-caller limit; past it the API answers 402 (x402, USDC on Base, $0.01 per call). POST /bulk-scan is paid on every call at $0.005 per domain; POST /bulk-registration (RDAP) at $0.002 per domain; POST /bulk-propagation at $0.008 per check; POST /bulk-dmarc-reports parses DMARC aggregate reports at $0.005 per report; POST /watch/record watches one DNS record for up to 24 hours at $0.10; POST /watch/lookalikes watches a domain''s lookalike names hourly for one fixed 24-hour day at $0.50. The five token-gated reads (GET /domains, /alerts, /readiness, /lookalikes and /email-tests) need a DNS Doctor API token the account owner mints at /dashboard/settings — never ask a user to paste one.'
description: 'Operations tagged watch across 2 of this provider''s published API definitions: dnsdoctor-dev-openapi.json, dnsdoctor-dev-openapi.json. Each path carries the servers of the definition it was published in.'
servers:
- url: https://dnsdoctor.dev/api/v1
tags:
- name: watch
paths:
/watch/propagation:
post:
tags:
- watch
summary: Create Propagation Watch
description: 'Buy a propagation watch: one name, six vantage points, up to 24 hours.
Anonymous; **402 on every call**. Gate order is the contract — inert 503,
then the 422 on a malformed target, then the fleet-wide caps'' 429, and only
then the lane. Everything that can refuse refuses before any money moves, and
the payment is settled only once the row is committed.'
operationId: create_propagation_watch_watch_propagation_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/WatchPropagationIn'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WatchCreated'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
'402':
description: Payment required — retry with an x402 payment header.
x-payment-info:
payTo: '0x6fDc1f22520151DfBc6273d6C48E413D434C7916'
price:
mode: dynamic
currency: USD
min: '0.004167'
max: '0.1'
protocols:
- x402: {}
network: base
servers:
- url: https://dnsdoctor.dev/api/v1
/watch/ping:
post:
tags:
- watch
summary: Create Ping Watch
description: 'Buy an HTTP ping watch: one URL, five regions on four continents, up to 24 hours.
Same gate order as the propagation create, with one gate more: the URL is put
through the SSRF seam (structural rules, then resolve-and-pin) BEFORE the lane
opens, so a target we would never dispatch five hosts at is a free 422. The
per-check re-resolve in `watch/ping.py` is what closes the rebinding window
afterwards — this pre-flight only keeps an unbuyable target from being sold.'
operationId: create_ping_watch_watch_ping_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/WatchPingIn'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WatchCreated'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
'402':
description: Payment required — retry with an x402 payment header.
x-payment-info:
payTo: '0x6fDc1f22520151DfBc6273d6C48E413D434C7916'
price:
mode: dynamic
currency: USD
min: '0.004167'
max: '0.25'
protocols:
- x402: {}
network: base
servers:
- url: https://dnsdoctor.dev/api/v1
/watch/record:
post:
tags:
- watch
summary: Create Record Watch
description: 'Buy a DNS record watch: one record, read authoritatively, up to 24 hours.
Anonymous; **402 on every call**. The propagation create''s gate order: inert
503, the 422 on a malformed target, the fleet-wide caps'' 429, then the lane —
everything that can refuse refuses before any money moves.'
operationId: create_record_watch_watch_record_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/WatchRecordIn'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WatchCreated'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
'402':
description: Payment required — retry with an x402 payment header.
x-payment-info:
payTo: '0x6fDc1f22520151DfBc6273d6C48E413D434C7916'
price:
mode: dynamic
currency: USD
min: '0.004167'
max: '0.1'
protocols:
- x402: {}
network: base
servers:
- url: https://dnsdoctor.dev/api/v1
/watch/lookalikes:
post:
tags:
- watch
summary: Create Lookalikes Watch
description: 'Buy one day of hourly lookalike checks on a domain: a baseline, then the changes.
Anonymous; **402 on every call**, $0.50 for the fixed 24-hour day. The record
create''s gate order: inert 503, the 422 on a malformed domain, the caps'' 429
(the lookalike fleet cap included), then the lane — everything that can
refuse refuses before any money moves.'
operationId: create_lookalikes_watch_watch_lookalikes_post
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/WatchLookalikesIn'
required: true
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WatchCreated'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
'402':
description: Payment required — retry with an x402 payment header.
x-payment-info:
payTo: '0x6fDc1f22520151DfBc6273d6C48E413D434C7916'
price:
mode: dynamic
currency: USD
min: '0.5'
max: '0.5'
protocols:
- x402: {}
network: base
servers:
- url: https://dnsdoctor.dev/api/v1
/watch/{watch_id}:
get:
tags:
- watch
summary: Poll Watch
description: 'Read one bought watch. Free, per-handle rate-limited, 404 on anything else.
`before` pages backwards through the observations (newest first, 50 a page);
the response''s `next_before` is non-null exactly when older ones remain.'
operationId: poll_watch_watch__watch_id__get
parameters:
- name: watch_id
in: path
required: true
schema:
type: string
title: Watch Id
- name: before
in: query
required: false
schema:
anyOf:
- type: string
maxLength: 40
- type: 'null'
title: Before
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/WatchStatus'
'422':
description: Validation Error
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPValidationError'
security: []
servers:
- url: https://dnsdoctor.dev/api/v1
components:
schemas:
HTTPValidationError:
properties:
detail:
items:
$ref: '#/components/schemas/ValidationError'
type: array
title: Detail
type: object
title: HTTPValidationError
WatchRecordIn:
properties:
domain:
type: string
maxLength: 253
title: Domain
kind:
type: string
maxLength: 16
title: Kind
description: 'Which record to watch: spf, dmarc, txt, mx, cname, a or aaaa. The watch reports `met` when the authoritative answer set differs from the first one recorded — a name whose A/AAAA answers legitimately rotate (an ELB/ALB or GSLB hostname) meets on the rotation, not on your change.'
host:
anyOf:
- type: string
maxLength: 200
- type: 'null'
title: Host
description: A label under the domain (txt, cname, a and aaaa only), e.g. `www`.
window_hours:
type: number
maximum: 24.0
minimum: 1.0
title: Window Hours
default: 24
type: object
required:
- domain
- kind
title: WatchRecordIn
description: 'The record watch''s terms. Target validation is `resolve_query`''s.
`window_hours` is bounded at the MODEL, the propagation watch''s reasoning:
the published `input_schema` says 1 to 24, and a sub-hour window would
pro-rate toward a `$0` offer on an always-paid surface.'
WatchLookalikesIn:
properties:
domain:
type: string
maxLength: 253
title: Domain
description: The domain whose lookalikes to watch.
type: object
required:
- domain
title: WatchLookalikesIn
description: The lookalike day's one term. Validation is `normalize_domain`'s.
ValidationError:
properties:
loc:
items:
anyOf:
- type: string
- type: integer
type: array
title: Location
msg:
type: string
title: Message
type:
type: string
title: Error Type
input:
title: Input
ctx:
type: object
title: Context
type: object
required:
- loc
- msg
- type
title: ValidationError
WatchPingIn:
properties:
url:
type: string
maxLength: 2000
title: Url
description: The URL to check. It is fetched over the shared probe fleet and stored for the life of the watch, so it must not carry a secret — no token, key or signature in the path or query.
cadence_seconds:
type: integer
title: Cadence Seconds
default: 300
window_hours:
type: number
maximum: 24.0
minimum: 1.0
title: Window Hours
default: 24
type: object
required:
- url
title: WatchPingIn
description: 'The ping watch''s terms. The URL itself is validated in `watch/targets.py`.
`cadence_seconds` is bounded to the two cadences we sell at the MODEL, so an
unsold cadence is refused during parse rather than silently priced as the
cheap one. The HTTP method is deliberately NOT a buyer knob: the probe fleet
issues a `HEAD`, which is the smallest request that answers "does this URL
still respond" and the least useful thing to aim at a third party.'
WatchCreated:
properties:
watch_id:
type: string
title: Watch Id
kind:
type: string
title: Kind
target:
type: string
title: Target
record_type:
anyOf:
- type: string
- type: 'null'
title: Record Type
expected_value:
anyOf:
- type: string
- type: 'null'
title: Expected Value
state:
type: string
title: State
cadence_seconds:
type: integer
title: Cadence Seconds
window_hours:
type: number
title: Window Hours
price_usd:
type: string
title: Price Usd
created_at:
type: string
title: Created At
expires_at:
type: string
title: Expires At
results_expire_at:
type: string
title: Results Expire At
poll_url:
type: string
title: Poll Url
type: object
required:
- watch_id
- kind
- target
- record_type
- expected_value
- state
- cadence_seconds
- window_hours
- price_usd
- created_at
- expires_at
- results_expire_at
- poll_url
title: WatchCreated
description: 'The minted handle plus the terms it was sold on.
Deterministic throughout: an echo of what the buyer asked for, the price that
was settled, and where to poll. No prose, no record, no prescription — the
D94 invariant carries onto every watch surface.'
WatchStatus:
properties:
kind:
type: string
title: Kind
target:
type: string
title: Target
state:
type: string
title: State
params:
additionalProperties: true
type: object
title: Params
created_at:
type: string
title: Created At
expires_at:
type: string
title: Expires At
results_expire_at:
type: string
title: Results Expire At
last_checked_at:
anyOf:
- type: string
- type: 'null'
title: Last Checked At
checks:
type: integer
title: Checks
verdict:
anyOf:
- type: string
- type: 'null'
title: Verdict
observations:
items:
additionalProperties: true
type: object
type: array
title: Observations
next_before:
anyOf:
- type: string
- type: 'null'
title: Next Before
type: object
required:
- kind
- target
- state
- params
- created_at
- expires_at
- results_expire_at
- last_checked_at
- checks
- verdict
- observations
title: WatchStatus
description: 'A watch''s state and everything observed so far.
Deterministic throughout, and a **relay**: `verdict` is the verdict the last
observation was recorded with, never re-derived here — the runner writes it
from D94''s own `derive_verdict`, so the poll surface cannot drift from the
on-demand check''s rules (a faulted probe is an unreached vantage, never a
negative reading, and fewer than three contributing vantage points degrade
the verdict to `unknown`).
`observations` are relayed verbatim as the runner recorded them, minus the
`_`-prefixed internal state `observation_page` strips. No prose, no
composed record, no prescription-shaped field at any depth — the D94
invariant carries onto every watch surface.'
WatchPropagationIn:
properties:
name:
type: string
maxLength: 1012
title: Name
record_type:
type: string
title: Record Type
default: A
expected_value:
anyOf:
- type: string
maxLength: 1024
- type: 'null'
title: Expected Value
description: The value every vantage point must agree on for the watch to report `met`. Without it, `met` means the grid agreed on an answer set DIFFERENT from the first one recorded — a name whose answers legitimately rotate (an ELB/ALB or GSLB hostname) meets on the rotation, not on your change; pass the value you are waiting for.
window_hours:
type: number
maximum: 24.0
minimum: 1.0
title: Window Hours
default: 24
type: object
required:
- name
title: WatchPropagationIn
description: 'The propagation watch''s terms. Validation is `normalize_probe_name`''s.
`window_hours` is bounded at the MODEL so an out-of-range window is refused
during parse — the metered-422 handler covers that leg, and a bound checked
later would price a window we do not sell. The bound is the one the surface''s
published `input_schema` advertises (1 to 24), not a wider one: a machine
contract that under-states what it accepts is drift, and a sub-hour window
would pro-rate toward a `$0` offer on an always-paid surface.'
securitySchemes:
apiKey:
type: http
scheme: bearer
description: 'A DNS Doctor API token: `Authorization: Bearer dnsd_…`.'
x-refined-from:
- dnsdoctor-dev-openapi.json
- dnsdoctor-dev-openapi.json
x-discovery:
ownershipProofs:
- '0xeb49cc7d9ac04add7ee0fbe51bc806b684ebdb6bfcdc8c2d7aa7eebc02032b93403764b0986d036ed68ad7e5914edc5403209f19806f4a87e15b1a5bdb73e9ab1b'