Building a Domain Monitor¶
This tutorial walks through building an automated domain monitoring system that checks DNS records, tracks WHOIS expiration dates, and monitors SSL certificates — all using Toolkit API endpoints.
What you'll build¶
A script that monitors a list of domains and alerts you when: - DNS records change (A, MX, NS, TXT) - A domain's WHOIS registration is near expiry - An SSL certificate is about to expire (within 30 days)
Prerequisites¶
pip install httpx
npm install node-fetch # if Node.js < 18
Step 1: Check DNS records¶
Start by resolving all DNS records for a domain and comparing against a baseline:
import httpx
import json
from datetime import datetime
API_KEY = "YOUR_KEY"
BASE_URL = "https://dns.toolkitapi.io/v1"
def get_dns_snapshot(domain: str) -> dict:
"""Get all DNS records for a domain."""
r = httpx.get(
f"{BASE_URL}/lookup/all",
headers={"X-API-Key": API_KEY},
params={"domain": domain},
)
r.raise_for_status()
data = r.json()
# Flatten records into a comparable set
snapshot = {}
for group in data["results"]:
records = []
for rec in group["records"]:
records.append((rec["value"], rec.get("priority")))
snapshot[group["type"]] = sorted(records)
return snapshot
# Take a baseline snapshot
domain = "toolkitapi.io"
baseline = get_dns_snapshot(domain)
# Save baseline to compare later
with open(f"{domain}_baseline.json", "w") as f:
json.dump(baseline, f, default=str)
print(f"Baseline saved for {domain}")
const API_KEY = "YOUR_KEY";
const BASE_URL = "https://dns.toolkitapi.io/v1";
async function getDnsSnapshot(domain) {
const params = new URLSearchParams({ domain });
const r = await fetch(`${BASE_URL}/lookup/all?${params}`, {
headers: { "X-API-Key": API_KEY },
});
const data = await r.json();
const snapshot = {};
for (const group of data.results) {
snapshot[group.type] = group.records.map(rec => ({
value: rec.value,
priority: rec.priority,
}));
}
return snapshot;
}
// Compare baseline against current state
async function checkForChanges(domain, baseline) {
const current = await getDnsSnapshot(domain);
const changes = [];
for (const [type, records] of Object.entries(current)) {
if (!baseline[type]) {
changes.push(`NEW: ${type} records added`);
continue;
}
const currentValues = JSON.stringify(records.map(r => [r.value, r.priority]));
const baselineValues = JSON.stringify(baseline[type].map(r => [r.value, r.priority]));
if (currentValues !== baselineValues) {
changes.push(`CHANGED: ${type} records modified`);
}
}
for (const type of Object.keys(baseline)) {
if (!current[type]) {
changes.push(`REMOVED: ${type} records deleted`);
}
}
return changes;
}
Step 2: Monitor WHOIS expiry¶
Check domain registration dates and alert when expiry is approaching:
def check_domain_expiry(domain: str, warn_days: int = 30) -> dict | None:
"""Check if a domain is nearing expiry."""
r = httpx.get(
f"{BASE_URL}/whois",
headers={"X-API-Key": API_KEY},
params={"domain": domain},
)
r.raise_for_status()
data = r.json()
if not data.get("expires"):
return None
expiry = datetime.fromisoformat(data["expires"].replace("Z", "+00:00"))
days_left = (expiry - datetime.now(expiry.tzinfo)).days
return {
"domain": domain,
"expires": data["expires"],
"days_left": days_left,
"registrar": data.get("registrar"),
"needs_renewal": days_left <= warn_days,
}
async function checkDomainExpiry(domain, warnDays = 30) {
const params = new URLSearchParams({ domain });
const r = await fetch(`${BASE_URL}/whois?${params}`, {
headers: { "X-API-Key": API_KEY },
});
const data = await r.json();
if (!data.expires) return null;
const expiry = new Date(data.expires);
const daysLeft = Math.ceil((expiry - new Date()) / (1000 * 60 * 60 * 24));
return {
domain,
expires: data.expires,
daysLeft,
registrar: data.registrar,
needsRenewal: daysLeft <= warnDays,
};
}
Step 3: Monitor SSL certificates¶
Check when TLS certificates expire:
def check_ssl_expiry(domain: str, warn_days: int = 30) -> dict:
"""Check SSL certificate expiry."""
r = httpx.get(
f"{BASE_URL}/certificate",
headers={"X-API-Key": API_KEY},
params={"domain": domain},
)
r.raise_for_status()
data = r.json()
return {
"domain": domain,
"issuer": data.get("issuer"),
"days_remaining": data.get("days_remaining", 0),
"expired": data.get("expired", False),
"needs_renewal": data.get("days_remaining", 0) <= warn_days and not data.get("expired", False),
}
Step 4: Put it all together¶
Combine all checks into a monitoring script:
import sys
domains_to_monitor = [
"toolkitapi.io",
"github.com",
"your-company.com",
]
print(f"Domain Monitor — {datetime.now().isoformat()}\n")
for domain in domains_to_monitor:
print(f"--- {domain} ---")
# DNS changes (requires a baseline from Step 1)
try:
snapshot = get_dns_snapshot(domain)
print(f" DNS: {sum(len(v) for v in snapshot.values())} records across "
f"{len(snapshot)} types")
except Exception as e:
print(f" DNS: ERROR — {e}")
# WHOIS expiry
try:
whois = check_domain_expiry(domain)
if whois:
status = "⚠ EXPIRING" if whois["needs_renewal"] else "✓ OK"
print(f" WHOIS: {status} — {whois['days_left']} days left "
f"(expires {whois['expires'][:10]})")
except Exception as e:
print(f" WHOIS: ERROR — {e}")
# SSL expiry
try:
ssl = check_ssl_expiry(domain)
if ssl:
status = "⚠ EXPIRING" if ssl["needs_renewal"] else "✓ OK"
print(f" SSL: {status} — {ssl['days_remaining']} days remaining "
f"(issuer: {ssl['issuer']})")
except Exception as e:
print(f" SSL: ERROR — {e}")
print()
Taking it further¶
- Schedule with cron — Run the script daily and send alerts via email or Slack webhook when
needs_renewalistrue. - Track DNS drift — Store baselines in a database and compare daily. Use the
propagationendpoint to verify DNS changes have rolled out globally. - Add blacklist monitoring — Use the
blacklistendpoint to check if your mail server IPs are flagged on any DNSBL. - Monitor subdomains — Use the
subdomainsendpoint to discover new subdomains that appear over time.