Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Domain Lab Domains API
version: 1.0.0
description: 'REST API for Domain Lab, a domain-monitoring service. Query monitored domains, plan limits and usage, per-domain alert settings, AI reviews, registrar connections, WHOIS/registration data, SSL certificates, DNS records, email security (SPF, DKIM, DMARC, MTA-STS, TLS reporting, BIMI, DNSSEC) with grades and history, change events, and alert history; add, update, and remove monitored domains. Requires an active subscription or trial. Limits: 120 requests/minute, 50,000 requests/month (adding a domain, rescanning, running an AI review and syncing a registrar connection each count as 5). Every response carries X-RateLimit-Remaining-Minute and X-RateLimit-Remaining-Month headers.'
contact:
url: https://www.domainlab.app/help
servers:
- url: https://www.domainlab.app/api/v1
security:
- apiKey: []
tags:
- name: Domains
paths:
/domains:
get:
operationId: listDomains
summary: List all monitored domains with their latest scan snapshot
parameters:
- name: limit
in: query
required: false
schema:
type: integer
default: 50
maximum: 100
minimum: 1
description: Maximum number of items to return
- name: offset
in: query
required: false
schema:
type: integer
default: 0
minimum: 0
description: Number of items to skip
responses:
'200':
description: Paginated domain list
content:
application/json:
schema:
type: object
properties:
domains:
type: array
items:
$ref: '#/components/schemas/DomainSummary'
total:
type: integer
limit:
type: integer
offset:
type: integer
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
post:
operationId: addDomain
summary: Start monitoring a new domain (counts as 5 requests against the monthly quota)
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- domain_name
properties:
domain_name:
type: string
description: Bare hostname; scheme/path/port are stripped and it is lowercased
frequency_interval:
type: string
description: Defaults to the fastest frequency your plan allows
enum:
- always
- hourly
- daily
- weekly
- monthly
- quarterly
- biannually
- annually
notification_enabled:
type: boolean
default: true
responses:
'201':
description: Domain created; first scan queued automatically
'400':
description: 'Invalid input. data.code is INVALID_BODY, MISSING_DOMAIN_NAME, INVALID_DOMAIN_NAME, INVALID_FREQUENCY, or FREQUENCY_NOT_IN_PLAN (data: { plan, allowed }).'
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'409':
description: Domain already exists for this account
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
/domains/{id}:
get:
operationId: getDomain
summary: Get one domain with its full latest scan, DNS records, and latest AI review
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
responses:
'200':
description: Domain detail. Includes latest_scan (Scan) and latest_ai_review ({ id, status, summary, completed_at, created_at } or null).
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: Not found or not owned by this account
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
patch:
operationId: updateDomain
summary: Update monitoring settings for a domain
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
requestBody:
required: true
content:
application/json:
schema:
type: object
description: Send at least one field
properties:
frequency_interval:
type: string
enum:
- always
- hourly
- daily
- weekly
- monthly
- quarterly
- biannually
- annually
notification_enabled:
type: boolean
monitoring_enabled:
type: boolean
responses:
'200':
description: Updated domain settings
'400':
description: 'Invalid input or no updatable fields provided. data.code includes FREQUENCY_NOT_IN_PLAN (data: { plan, allowed }).'
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: Not found or not owned by this account
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
delete:
operationId: deleteDomain
summary: Stop monitoring and permanently delete a domain and all of its history
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
responses:
'200':
description: Deleted
content:
application/json:
schema:
type: object
properties:
deleted:
type: boolean
id:
type: integer
domain_name:
type: string
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: Not found or not owned by this account
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
/domains/{id}/scan:
get:
operationId: getScanHistory
summary: Paginated scan history for one domain, newest first
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
- name: limit
in: query
required: false
schema:
type: integer
default: 10
maximum: 50
minimum: 1
description: Maximum number of items to return
- name: offset
in: query
required: false
schema:
type: integer
default: 0
minimum: 0
description: Number of items to skip
responses:
'200':
description: Paginated scans
content:
application/json:
schema:
type: object
properties:
scans:
type: array
items:
$ref: '#/components/schemas/Scan'
total:
type: integer
limit:
type: integer
offset:
type: integer
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: Not found or not owned by this account
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
/domains/{id}/events:
get:
operationId: getDomainEvents
summary: Change events for one domain, newest first
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
- name: limit
in: query
required: false
schema:
type: integer
default: 50
maximum: 100
minimum: 1
description: Maximum number of items to return
- name: offset
in: query
required: false
schema:
type: integer
default: 0
minimum: 0
description: Number of items to skip
responses:
'200':
description: Paginated events
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: Not found or not owned by this account
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
/domains/{id}/email:
get:
operationId: getDomainEmailSecurity
summary: 'Current email security for a domain: grade, SPF, DKIM, DMARC, mail servers…'
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
- name: full
in: query
required: false
schema:
type: boolean
default: false
description: Include the raw per-protocol results
responses:
'200':
description: '{ domain_id, domain_name, checked_at, next_check_at, scan: { scan_id, grade, rubric, grade_reasons, first_seen_at, last_checked_at, protocols: [{ protocol, label, status, record, verdict, issues }], fixes, details? } | null }'
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: NOT_FOUND
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
/domains/{id}/email/history:
get:
operationId: getDomainEmailSecurityHistory
summary: Every distinct email security state for a domain, newest first, with what…
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
- name: limit
in: query
required: false
schema:
type: integer
default: 10
maximum: 50
minimum: 1
description: Maximum number of items to return
- name: offset
in: query
required: false
schema:
type: integer
default: 0
minimum: 0
description: Number of items to skip
responses:
'200':
description: '{ domain_id, domain_name, states: [{ scan_id, first_seen_at, last_checked_at, confirmed, grade, rubric, statuses, changes: [{ protocol, label, before, after, before_status, after_status }] }], total, limit, offset }'
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: NOT_FOUND
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
/domains/{id}/rescan:
post:
operationId: rescanDomain
summary: Queue an immediate scan of a domain (counts as 5 requests)
description: 10-minute cooldown between scans.
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
responses:
'202':
description: 'Scan queued: { ok, domain_id, domain_name, queued_at }'
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: NOT_FOUND
'409':
description: 'MONITORING_DISABLED: enable monitoring first'
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
'502':
description: SCAN_START_FAILED
tags:
- Domains
/domains/{id}/ai-review:
post:
operationId: runAiReview
summary: Run a manual AI review of a domain (counts as 5 requests; uses monthly plan…
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
responses:
'200':
description: 'Review result: { review_id, status, degraded, summary, recommendations, quota }. degraded is true when AI enhancement was unavailable.'
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: NOT_FOUND
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
'502':
description: AI_REVIEW_FAILED
tags:
- Domains
/domains/{id}/ai-reviews:
get:
operationId: listAiReviews
summary: Past AI reviews for a domain, newest first (degraded reviews excluded)
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
- name: limit
in: query
required: false
schema:
type: integer
default: 10
maximum: 25
minimum: 1
description: Maximum reviews to return
responses:
'200':
description: Reviews and manual-review quota
content:
application/json:
schema:
type: object
properties:
reviews:
type: array
items:
$ref: '#/components/schemas/AiReview'
quota:
$ref: '#/components/schemas/Quota'
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: NOT_FOUND
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
/domains/{id}/alerts:
get:
operationId: getDomainAlerts
summary: 'Alert settings for one domain: master switch and per-event-type mute state'
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
responses:
'200':
description: Domain alert settings
content:
application/json:
schema:
$ref: '#/components/schemas/DomainAlerts'
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: NOT_FOUND
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
put:
operationId: updateDomainAlerts
summary: Change alert settings for one domain
description: 'threshold_days has no per-domain storage: it updates the account-wide threshold for that event type (response has thresholds_scope "account").'
parameters:
- name: id
in: path
required: true
schema:
type: integer
description: Domain id (find it via GET /domains)
requestBody:
required: true
content:
application/json:
schema:
type: object
description: Send notification_enabled and/or rules
properties:
notification_enabled:
type: boolean
rules:
type: array
items:
type: object
required:
- event_type_id
properties:
event_type_id:
type: integer
is_enabled:
type: boolean
description: false mutes this alert type for the domain
threshold_days:
type: array
items:
type: integer
minimum: 0
maximum: 365
minItems: 1
maxItems: 10
description: Days-before reminders (account-wide)
responses:
'200':
description: Updated domain alert settings
content:
application/json:
schema:
$ref: '#/components/schemas/DomainAlerts'
'400':
description: INVALID_BODY, NO_UPDATES, INVALID_THRESHOLD, or INVALID_EVENT_TYPE (data.invalid_ids)
'401':
description: Missing or invalid API key
'403':
description: Account lacks API access (no active subscription or trial)
'404':
description: NOT_FOUND
'429':
description: Rate limit or monthly quota exceeded. Respect the Retry-After header. code is RATE_LIMITED (short-lived) or MONTHLY_QUOTA_EXCEEDED (resets next period).
tags:
- Domains
components:
schemas:
Quota:
type: object
properties:
used:
type: integer
limit:
type: integer
resets_at:
type: string
format: date-time
DomainAlerts:
type: object
properties:
domain_id:
type: integer
domain_name:
type: string
notification_enabled:
type: boolean
rules:
type: array
items:
$ref: '#/components/schemas/AlertRule'
thresholds_scope:
type: string
enum:
- account
description: Present on PUT when threshold_days changed; thresholds are account-wide
Scan:
type: object
description: One monitoring result (scan snapshot) for a domain
properties:
id:
type: integer
checked_at:
type: string
format: date-time
registrar:
type:
- string
- 'null'
expiration_date:
type:
- string
- 'null'
format: date-time
creation_date:
type:
- string
- 'null'
format: date-time
updated_date:
type:
- string
- 'null'
format: date-time
domain_status:
type:
- string
- 'null'
description: Registry EPP status codes, normalized (sorted, lowercase), or "available"
site_status:
type:
- string
- 'null'
enum:
- available
- parked
- for-sale
- dead
- down
- active
- null
description: Web-layer status computed each scan (down = the site answers with a server error); null when classification was unavailable
ssl_issuer:
type:
- string
- 'null'
ssl_valid_from:
type:
- string
- 'null'
format: date-time
ssl_valid_to:
type:
- string
- 'null'
format: date-time
ssl_cipher:
type:
- string
- 'null'
ssl_protocol:
type:
- string
- 'null'
subject:
type:
- string
- 'null'
serial_number:
type:
- string
- 'null'
fingerprint:
type:
- string
- 'null'
security_score:
type:
- integer
- 'null'
nameservers:
type:
- string
- 'null'
error:
type:
- string
- 'null'
description: Non-null when this scan failed
dns_records:
type: array
items:
$ref: '#/components/schemas/DnsRecord'
Error:
type: object
properties:
statusCode:
type: integer
message:
type: string
DnsRecord:
type: object
properties:
id:
type: integer
record_type:
type: string
description: A, AAAA, CNAME, MX, TXT, or NS
name:
type: string
value:
type: string
ttl:
type:
- integer
- 'null'
priority:
type:
- integer
- 'null'
description: MX priority
AiReview:
type: object
properties:
id:
type: integer
review_type:
type: string
status:
type: string
summary:
type:
- string
- 'null'
recommendations:
type:
- array
- object
- 'null'
ai_confidence_score:
type:
- number
- 'null'
completed_at:
type:
- string
- 'null'
format: date-time
created_at:
type: string
format: date-time
AlertRule:
type: object
properties:
event_type_id:
type: integer
event_type_name:
type: string
muted:
type: boolean
is_enabled:
type: boolean
DomainSummary:
type: object
properties:
id:
type: integer
domain_name:
type: string
notification_enabled:
type: boolean
monitoring_enabled:
type: boolean
frequency_interval:
type: string
enum:
- always
- hourly
- daily
- weekly
- monthly
- quarterly
- biannually
- annually
last_checked_at:
type:
- string
- 'null'
format: date-time
created_at:
type: string
format: date-time
registrar:
type:
- string
- 'null'
ssl_issuer:
type:
- string
- 'null'
ssl_valid_to:
type:
- string
- 'null'
format: date-time
security_score:
type:
- integer
- 'null'
nameservers:
type:
- string
- 'null'
description: Comma-separated nameserver hostnames
days_until_domain_expires:
type:
- integer
- 'null'
description: Negative means already expired
days_until_ssl_expires:
type:
- integer
- 'null'
description: Negative means already expired
has_error:
type: boolean
description: True when the most recent scan failed
securitySchemes:
apiKey:
type: http
scheme: bearer
description: 'Domain Lab API key (dlb_...), created in Preferences → API. Send as: Authorization: Bearer dlb_...'