Server--:--:--You--:--:--

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.

Public lookups (DNS, email, SSL, etc.) need no authentication. Anything tied to your account — monitors, teams, dashboard, API keys — needs an API key.

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).

ParamInRequiredDescription
targetqueryYesThe 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).

ParamInRequiredDescription
targetqueryYesThe 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).

ParamInRequiredDescription
targetqueryYesThe 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).

ParamInRequiredDescription
targetqueryYesThe 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).

ParamInRequiredDescription
targetqueryYesThe 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).

ParamInRequiredDescription
targetqueryYesThe 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).

ParamInRequiredDescription
targetqueryYesThe 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).

ParamInRequiredDescription
targetqueryYesThe 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.

ParamInRequiredDescription
targetqueryYesThe 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.

ParamInRequiredDescription
targetqueryYesThe 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.

ParamInRequiredDescription
targetqueryYesThe domain, hostname, or IP to check.
record_typequeryNoOne of A, AAAA, CNAME, MX, NS, TXT, SOA, CAA. Defaults to A.

Response

SuccessEnvelope<ScanResult> — see "Response format" above for the shared shape.

Email

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.

ParamInRequiredDescription
targetqueryYesThe 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.

ParamInRequiredDescription
targetqueryYesThe 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).

ParamInRequiredDescription
targetqueryYesThe domain, hostname, or IP to check.
selectorqueryYesThe 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.

ParamInRequiredDescription
targetqueryYesThe 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.

ParamInRequiredDescription
targetqueryYesThe 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.

ParamInRequiredDescription
targetqueryYesThe 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.

ParamInRequiredDescription
targetqueryYesA 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.

ParamInRequiredDescription
targetqueryYesA 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.

ParamInRequiredDescription
targetqueryYesThe 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.

ParamInRequiredDescription
targetqueryYesThe 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.

ParamInRequiredDescription
targetqueryYesThe 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.

ParamInRequiredDescription
scan_idpathYesThe 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.

ParamInRequiredDescription
scan_idpathYesThe 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).

ParamInRequiredDescription
tokenpathYesThe 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

ParamInRequiredDescription
key_idpathYes

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

ParamInRequiredDescription
organization_idqueryNoList 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

ParamInRequiredDescription
monitor_idpathYes

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

ParamInRequiredDescription
monitor_idpathYes

Response

MonitorRunOut[]: [{ "scan_id", "status", "score", "severity", "created_at" }]

PATCH/api/v1/monitors/{monitor_id}

Pause or resume a monitor

Requires an API key

ParamInRequiredDescription
monitor_idpathYes

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

ParamInRequiredDescription
monitor_idpathYes

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

ParamInRequiredDescription
organization_idpathYes

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

ParamInRequiredDescription
organization_idpathYes

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

ParamInRequiredDescription
organization_idpathYes
member_user_idpathYes

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

ParamInRequiredDescription
organization_idpathYes

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.