{"openapi":"3.1.0","info":{"title":"Domain Lab API","version":"1.0.0","description":"REST API for Domain Lab, a domain-monitoring service. Query monitored domains, WHOIS/registration data, SSL certificates, DNS records, 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 counts 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":[]}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Domain Lab API key (dlb_...), created in Preferences → API. Send as: Authorization: Bearer dlb_..."}},"schemas":{"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"}}},"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"}}},"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"]},"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"}}}},"Event":{"type":"object","description":"A detected change event","properties":{"id":{"type":"integer"},"detected_at":{"type":"string","format":"date-time"},"description":{"type":"string"},"status":{"type":["string","null"]},"payload":{"type":["object","null"],"description":"Before/after detail of the change"},"created_at":{"type":"string","format":"date-time"},"event_type":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"description":{"type":["string","null"]}}},"domain":{"type":"object","description":"Present on GET /events (cross-domain listing) only","properties":{"id":{"type":"integer"},"domain_name":{"type":"string"}}}}},"Alert":{"type":"object","description":"A notification that was sent","properties":{"id":{"type":"integer"},"status":{"type":"string"},"sent_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"},"domain":{"type":"object","properties":{"id":{"type":"integer"},"domain_name":{"type":"string"}}},"provider":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}}},"event":{"type":"object","properties":{"id":{"type":"integer"},"description":{"type":"string"},"event_type":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}}}}}}},"Error":{"type":"object","properties":{"statusCode":{"type":"integer"},"message":{"type":"string"}}}}},"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)."}}},"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","default":"daily","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","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)."}}}},"/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)."}}},"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"},"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)."}}},"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)."}}}},"/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)."}}}},"/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)."}}}},"/events":{"get":{"operationId":"listEvents","summary":"Change events across all domains, newest first","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"},{"name":"domain_id","in":"query","required":false,"schema":{"type":"integer"},"description":"Filter to one domain"},{"name":"event_type_id","in":"query","required":false,"schema":{"type":"integer"},"description":"Filter by event type"}],"responses":{"200":{"description":"Paginated events, each including domain: { id, domain_name }","content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"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)."}}}},"/alerts":{"get":{"operationId":"listAlerts","summary":"Notification history (alerts that were sent), newest first","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"},{"name":"domain_id","in":"query","required":false,"schema":{"type":"integer"}},{"name":"status","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Paginated alerts","content":{"application/json":{"schema":{"type":"object","properties":{"alerts":{"type":"array","items":{"$ref":"#/components/schemas/Alert"}},"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)."}}}}}}