API Docs
Every diagnostic tool on this site is also a plain HTTP API — call it from a script, CI pipeline, or your own tooling.
Authentication
Send your API key as a bearer token, the same way a logged-in session would. A key works for every endpoint your account can reach in the browser — there's no separate permission model:
Authorization: Bearer ntk_your_api_key_here
Response format
Every endpoint replies with the same envelope. On success:
{
"success": true,
"data": { ... },
"meta": { "request_id": "..." }
}On failure — a non-2xx HTTP status, with details in error:
{
"success": false,
"error": { "code": "INVALID_REQUEST", "message": "..." },
"meta": { "request_id": "..." }
}Every DNS/Email/Website/SSL/Security lookup below returns the same shape inside data — referred to in this reference as ScanResult:
{
"tool": "dns_a",
"status": "complete",
"severity": "pass" | "info" | "warning" | "error" | "critical",
"score": 100,
"summary": "A short human-readable summary of what was found.",
"data": { /* tool-specific raw fields */ },
"duration_ms": 214,
"findings": [
{
"code": "MISSING_SPF",
"severity": "warning",
"title": "...",
"description": "What was checked and what was found.",
"recommendation": "How to fix it, or null.",
"documentation_url": null
}
]
}Usage limits
Lookups are free and unlimited by default. If a usage cap is active, you'll get an HTTP 402 once a per-IP free limit is reached, with a message explaining how to unlock unlimited access — this doesn't apply to endpoints called with an API key tied to a logged-in account.
Example: a public DNS lookup
curl "https://backend.sysadmin.lk/api/v1/dns/a?target=example.com"
Example: list your monitors (authenticated)
curl "https://backend.sysadmin.lk/api/v1/monitors" \ -H "Authorization: Bearer ntk_your_api_key_here"
Example: run a Super Scan and poll for results
# Start the scan
curl -X POST "https://backend.sysadmin.lk/api/v1/scanner/super-scan" \
-H "Content-Type: application/json" \
-d '{"target": "example.com"}'
# Poll using the returned scan_id
curl "https://backend.sysadmin.lk/api/v1/scanner/super-scan/<scan_id>"Full endpoint reference
Grouped by category. Click any endpoint to expand its parameters and response shape.
DNS
Record lookups, WHOIS, DNSSEC validation, and propagation checks across multiple public resolvers. All GET, all public, all return the standard result envelope.
GET/api/v1/dns/a›
A record lookup
Resolves the target's A record(s) and scores the result (e.g. missing MX, or an SPF-looking TXT record flagged for the wrong tool).
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/dns/aaaa›
AAAA record lookup
Resolves the target's AAAA record(s) and scores the result (e.g. missing MX, or an SPF-looking TXT record flagged for the wrong tool).
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/dns/cname›
CNAME record lookup
Resolves the target's CNAME record(s) and scores the result (e.g. missing MX, or an SPF-looking TXT record flagged for the wrong tool).
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/dns/mx›
MX record lookup
Resolves the target's MX record(s) and scores the result (e.g. missing MX, or an SPF-looking TXT record flagged for the wrong tool).
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/dns/ns›
NS record lookup
Resolves the target's NS record(s) and scores the result (e.g. missing MX, or an SPF-looking TXT record flagged for the wrong tool).
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/dns/txt›
TXT record lookup
Resolves the target's TXT record(s) and scores the result (e.g. missing MX, or an SPF-looking TXT record flagged for the wrong tool).
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/dns/soa›
SOA record lookup
Resolves the target's SOA record(s) and scores the result (e.g. missing MX, or an SPF-looking TXT record flagged for the wrong tool).
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/dns/caa›
CAA record lookup
Resolves the target's CAA record(s) and scores the result (e.g. missing MX, or an SPF-looking TXT record flagged for the wrong tool).
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/dns/whois›
Domain WHOIS lookup
Registrar, registration/expiry dates, and nameservers from WHOIS.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/dns/dnssec›
DNSSEC checker
Queries Cloudflare (1.1.1.1) and Google (8.8.8.8) independently and compares their AD (authenticated data) bits — a domain is only reported as validating if every signed resolver agrees. Flags a resolver mismatch as its own finding.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/dns/propagation›
DNS propagation checker
Resolves one record type against a fixed panel of resolvers and reports whether they all agree — useful right after a DNS change, before it's fully propagated everywhere.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
| record_type | query | No | One of A, AAAA, CNAME, MX, NS, TXT, SOA, CAA. Defaults to A. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
Authentication and deliverability posture: SPF, DKIM, DMARC, BIMI, MTA-STS, TLS-RPT.
GET/api/v1/email/spf›
SPF checker
Parses the domain's SPF record, follows includes, and flags overly permissive (+all) or missing policies.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/email/dmarc›
DMARC checker
Reads the domain's DMARC policy (p=none/quarantine/reject) and reporting addresses, and explains what enforcement level actually means.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/email/dkim›
DKIM checker
DKIM keys live at a selector-specific subdomain, so unlike the others this needs a selector you already know (check your mail provider's docs, e.g. "google" for Google Workspace).
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
| selector | query | Yes | The DKIM selector, e.g. "google" or "selector1". |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/email/bimi›
BIMI checker
Brand Indicators for Message Identification — the logo shown next to authenticated email in supporting inboxes. Requires DMARC enforcement to be meaningful.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/email/mta-sts›
MTA-STS checker
Checks the MTA-STS policy file and its DNS TXT record, which lets receiving mail servers enforce TLS for inbound mail to this domain.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/email/tls-rpt›
TLS-RPT checker
Checks for a TLS reporting DNS record, which asks other mail servers to send back reports about TLS connection failures to this domain.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
Website & SSL/TLS
HTTP behavior, response headers, and certificate health.
GET/api/v1/http/check›
HTTP check
Fetches the site, follows redirects (capped, and every hop re-validated against SSRF rules), and reports the final status, redirect chain, and response time.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | A hostname or full URL. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/http/security-headers›
Security headers checker
Grades the presence and correctness of HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, and Permissions-Policy.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | A hostname or full URL. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/tls/certificate›
SSL/TLS certificate checker
Connects on 443, reports the certificate chain, expiry, key strength, and protocol/cipher, and flags self-signed or soon-to-expire certificates.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
Security & Reputation
Blacklist/reputation lookups and dangling-DNS takeover detection.
GET/api/v1/security/blacklist›
Blacklist checker
Resolves the target to an IP and queries it against Spamhaus ZEN, SORBS, Barracuda, and SpamCop. One CRITICAL finding per listing provider.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
GET/api/v1/security/takeover›
Subdomain takeover checker
Resolves the domain's CNAME; if it points at a known vulnerable-service pattern (GitHub Pages, S3, Heroku, Azure, etc.) and the page fingerprint matches that service's "unclaimed" page, flags a possible takeover.
| Param | In | Required | Description |
|---|---|---|---|
| target | query | Yes | The domain, hostname, or IP to check. |
Response
SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.
Network
Ping, traceroute, port scanning, and IP lookups run as background jobs — POST to start one, then poll its scan_id for the result, same pattern as the Super Scanner.
POST/api/v1/network/ping›
Start a ping
Queues an ICMP ping job on a background worker.
Request body
{ "target": "example.com" }Response
ScanCreated: { "scan_id": "...", "tool_id": "network_ping", "target": "...", "status": "queued" }
POST/api/v1/network/traceroute›
Start a traceroute
Queues a traceroute job on a background worker.
Request body
{ "target": "example.com" }Response
ScanCreated (see ping above)
POST/api/v1/network/port-checker›
Start a port scan
Queues a TCP port-connect check. Omit "ports" to scan a default common-port list.
Request body
{ "target": "example.com", "ports": [22, 80, 443] }Response
ScanCreated (see ping above)
POST/api/v1/network/ip-lookup›
Start an IP lookup
Queues a reverse-DNS/PTR + IP geolocation lookup.
Request body
{ "target": "1.1.1.1" }Response
ScanCreated (see ping above)
GET/api/v1/network/scans/{scan_id}›
Poll a network job
Poll until status is "complete" or "failed". The result field is null until then.
| Param | In | Required | Description |
|---|---|---|---|
| scan_id | path | Yes | The scan_id returned by the POST above. |
Response
ScanStatusOut: { "scan_id", "tool_id", "target", "status", "error_message", "result": ScanResult | null }
Super Scanner
Runs every DNS, email, website, and security check for a domain in one pass, with an aggregate score. Same start-then-poll pattern as the Network tools.
POST/api/v1/scanner/super-scan›
Start a Super Scan
Queues a full multi-tool scan. "region" is optional — omit it to use the default regional worker pool.
Request body
{ "target": "example.com", "region": null }Response
ScanCreated, with "region" populated: { "scan_id", "tool_id": "super_scan", "target", "status", "region" }
GET/api/v1/scanner/super-scan/{scan_id}›
Poll a Super Scan
Poll until status is "complete". "results" fills in incrementally as each sub-tool finishes; "score"/"severity" are the aggregate across all of them, set once the whole scan completes.
| Param | In | Required | Description |
|---|---|---|---|
| scan_id | path | Yes | The scan_id returned by the POST above. |
Response
SuperScanStatusOut: { "scan_id", "tool_id", "target", "status", "region", "score", "severity", "results": ScanResult[] }
Email Deliverability Test
Generates a one-time receiving address; send it a real email from whatever you're testing, then poll for the SPF/DKIM/DMARC verdict that email actually arrived with.
POST/api/v1/mailtest/generate›
Generate a test address
No request body. Returns a unique address that expires after a fixed window if nothing arrives.
Response
MailTestCreated: { "token", "test_address", "expires_at" }
GET/api/v1/mailtest/{token}›
Poll a test's result
Status is "pending" until a message arrives, then "received" (or "expired" if the window passed first).
| Param | In | Required | Description |
|---|---|---|---|
| token | path | Yes | The token from the generate call. |
Response
MailTestStatusOut: { "token", "status", "score", "findings": Finding[], "sender_ip", "from_domain", "spf_result", "dkim_valid", "dmarc_aligned", "created_at", "received_at", "expires_at" }
CSR & SMTP Utilities
Synchronous one-shot tools — no polling needed.
POST/api/v1/csr/generate›
Generate a CSR + private key
Generates an RSA key pair and a PKCS#10 certificate signing request server-side, entirely in-memory — nothing is stored. The private key is returned once, in the response; download and keep it, it isn't recoverable afterward.
Request body
{ "common_name": "example.com", "organization": null, "san_domains": ["www.example.com"], "key_size": 2048 }Response
CsrOut: { "csr_pem", "private_key_pem" }
POST/api/v1/smtp-test›
Test an SMTP connection
Connects to a mail server you specify and walks through a real SMTP handshake (optionally with STARTTLS/implicit TLS and AUTH), logging each step — useful for debugging a mail server's own outbound config, not for testing this site's deliverability (use the Email Deliverability Test above for that).
Request body
{ "host": "smtp.example.com", "port": 587, "tls_mode": "starttls", "receiver_email": "[email protected]" }Response
SmtpTestOut: { "success", "log": string[] }
Misc
Small standalone endpoints.
GET/api/v1/whoami›
Your public IP and geolocation
Reports the caller's public IP (respecting Cloudflare/X-Forwarded-For) plus best-effort geolocation from ipinfo.io.
Response
SuccessEnvelope<{ ip, country, country_code, city, region, org }>
GET/api/v1/regions›
List available scan regions
Which regional worker pools scans can be routed to — lets a client build a region picker without hardcoding the list.
Response
RegionOut[]: [{ "id": "lk", "is_default": true }]
POST/api/v1/contact›
Contact form submission
Backs the site's own /contact page. Not really intended for third-party API use, but it's a plain endpoint like any other.
Request body
{ "name": "...", "email": "...", "message": "..." }Response
SuccessEnvelope<{ sent: true }>
Authentication & Account
Email/password and Google sign-in both return the same access_token — use it as the bearer token for every authenticated endpoint below, exactly like an API key.
POST/api/v1/auth/signup›
Create an account
Sends a 6-digit email verification code in the background — the account is usable immediately, but email_verified stays false until verify-email succeeds.
Request body
{ "email": "[email protected]", "password": "at least 8 characters" }Response
TokenOut: { "access_token", "token_type": "bearer", "user": User }
POST/api/v1/auth/login›
Log in
Returns the same generic "invalid email or password" error whether the email doesn't exist or the password is wrong, so a response never confirms which emails are registered.
Request body
{ "email": "[email protected]", "password": "..." }Response
TokenOut (see signup above)
POST/api/v1/auth/google›
Sign in with Google
Verifies a Google ID token credential (from Google's own sign-in button on the frontend). Links to an existing password account with the same email if one exists, otherwise creates a new, pre-verified account.
Request body
{ "credential": "<Google ID token>" }Response
TokenOut (see signup above)
GET/api/v1/auth/me›
Get the current user
Also the simplest way to check whether a token/key is still valid.
Requires an API key
Response
User: { "id", "email", "name", "created_at", "email_verified", "has_password" }
PATCH/api/v1/auth/me›
Update your name
Requires an API key
Request body
{ "name": "..." }Response
User (see GET /auth/me above)
POST/api/v1/auth/verify-email›
Submit an email verification code
Codes expire after a fixed window and lock out after 5 wrong attempts — request a fresh one via resend-verification if either happens.
Requires an API key
Request body
{ "code": "123456" }Response
User (see GET /auth/me above)
POST/api/v1/auth/resend-verification›
Resend the verification code
Requires an API key
Response
MessageOut: { "message": "Verification code sent" }
POST/api/v1/auth/change-password›
Change (or set) your password
current_password is only required if the account already has one — a Google-only account has none yet, so this sets its first password instead of changing one.
Requires an API key
Request body
{ "current_password": "...", "new_password": "at least 8 characters" }Response
MessageOut: { "message": "Password updated" }
API Keys
Manage the keys used to authenticate everything in this reference marked "Requires an API key."
POST/api/v1/api-keys›
Create an API key
The plaintext key is returned exactly once, in this response — copy it now, it can't be retrieved again afterward (only its prefix and creation date are kept on record).
Requires an API key
Request body
{ "name": "CI pipeline" }Response
ApiKeyCreatedOut: { "id", "name", "key_prefix", "created_at", "last_used_at", "key": "ntk_..." }
GET/api/v1/api-keys›
List your API keys
Revoked keys don't appear here. The plaintext key is never included.
Requires an API key
Response
ApiKeyOut[]: [{ "id", "name", "key_prefix", "created_at", "last_used_at" }]
DELETE/api/v1/api-keys/{key_id}›
Revoke an API key
Immediate and permanent — any script using it starts getting 401s right away.
Requires an API key
| Param | In | Required | Description |
|---|---|---|---|
| key_id | path | Yes |
Response
204 No Content
Monitors
Recurring Super Scans on a schedule, with score history over time. A monitor is either personal (visible only to you) or belongs to a team — see Teams below.
POST/api/v1/monitors›
Create a monitor
Kicks off an immediate first scan in addition to scheduling future ones. Set organization_id to create it inside a team instead of personally — you must already be a member.
Requires an API key
Request body
{ "target": "example.com", "interval_minutes": 1440, "region": null, "organization_id": null }Response
MonitorOut: { "id", "target", "interval_minutes", "is_active", "next_run_at", "region", "organization_id", "created_at", "latest_score", "latest_severity", "latest_scan_id" }
GET/api/v1/monitors›
List monitors
Without organization_id, lists your personal monitors only. Pass it to list a team's monitors instead — you must be a member.
Requires an API key
| Param | In | Required | Description |
|---|---|---|---|
| organization_id | query | No | List a team's shared monitors instead of your personal ones. |
Response
MonitorOut[]
GET/api/v1/monitors/{monitor_id}›
Get a monitor
Requires an API key
| Param | In | Required | Description |
|---|---|---|---|
| monitor_id | path | Yes |
Response
MonitorOut
GET/api/v1/monitors/{monitor_id}/history›
Get a monitor's scan history
Most recent 20 runs, newest first.
Requires an API key
| Param | In | Required | Description |
|---|---|---|---|
| monitor_id | path | Yes |
Response
MonitorRunOut[]: [{ "scan_id", "status", "score", "severity", "created_at" }]
PATCH/api/v1/monitors/{monitor_id}›
Pause or resume a monitor
Requires an API key
| Param | In | Required | Description |
|---|---|---|---|
| monitor_id | path | Yes |
Request body
{ "is_active": false }Response
MonitorOut
DELETE/api/v1/monitors/{monitor_id}›
Delete a monitor
Also deletes its scan history.
Requires an API key
| Param | In | Required | Description |
|---|---|---|---|
| monitor_id | path | Yes |
Response
204 No Content
Teams
Share monitors with colleagues. Every member can see and manage a team's monitors; only the owner (the creator) can add or remove members or delete the team.
POST/api/v1/organizations›
Create a team
You become its owner.
Requires an API key
Request body
{ "name": "Acme Ops" }Response
OrganizationOut: { "id", "name", "role", "created_at", "member_count" }
GET/api/v1/organizations›
List your teams
Every team you're a member of, owner or not.
Requires an API key
Response
OrganizationOut[]
GET/api/v1/organizations/{organization_id}›
Get a team, with its member list
404s for non-members, the same as if the team didn't exist — membership isn't leaked.
Requires an API key
| Param | In | Required | Description |
|---|---|---|---|
| organization_id | path | Yes |
Response
OrganizationDetailOut: OrganizationOut & { "members": [{ "user_id", "email", "name", "role" }] }
POST/api/v1/organizations/{organization_id}/members›
Add a member by email
The email must already belong to a NET Tools account — there's no separate invite-by-email-for-someone-who-hasn't-signed-up flow.
Requires an API key — team owner only
| Param | In | Required | Description |
|---|---|---|---|
| organization_id | path | Yes |
Request body
{ "email": "[email protected]" }Response
OrganizationMemberOut: { "user_id", "email", "name", "role": "member" }
DELETE/api/v1/organizations/{organization_id}/members/{member_user_id}›
Remove a member
The owner can't remove themselves this way — delete the team instead.
Requires an API key — team owner only
| Param | In | Required | Description |
|---|---|---|---|
| organization_id | path | Yes | |
| member_user_id | path | Yes |
Response
204 No Content
DELETE/api/v1/organizations/{organization_id}›
Delete a team
Removes every member and permanently deletes the team's shared monitors (and their scan history).
Requires an API key — team owner only
| Param | In | Required | Description |
|---|---|---|---|
| organization_id | path | Yes |
Response
204 No Content
Dashboard
Aggregate stats across all of your personal monitors.
GET/api/v1/dashboard/summary›
Get your dashboard summary
average_score and worst_severity are computed from each monitor's latest completed scan.
Requires an API key
Response
DashboardSummaryOut: { "monitor_count", "active_monitor_count", "average_score", "worst_severity", "recent_scans": [{ "scan_id", "monitor_id", "target", "status", "score", "severity", "created_at" }] }
Audit Log
A security-relevant activity trail for your own account.
GET/api/v1/audit-log›
List your recent activity
Most recent 100 entries, newest first — logins, password changes, monitor and API key create/pause/delete, team create/delete and member add/remove.
Requires an API key
Response
AuditLogOut[]: [{ "id", "action", "detail", "created_at" }]
This page covers every endpoint by hand, with the reasoning behind how they fit together. For the exact machine-readable schema (types, validation rules, all fields), the auto-generated interactive reference is at https://backend.sysadmin.lk/docs.