API reference
One apex domain in, an owned-and-enriched subdomain dataset out — certificate transparency and passive DNS from several providers, merged, de-duplicated, resolved, scored and persisted, so repeat lookups get richer rather than merely faster.
Coming soon. The public API is not open yet — this reference documents the contract it will ship with, so the shapes and parameters below are stable to build against, but the endpoints do not answer external calls today. Everything described here already backs the UI, so use the site in the meantime.
Every endpoint lives under /v1 and answers JSON. Reads are three-tier — Redis, then Postgres, then a live provider fan-out — and the X-Cache response header reports which tier served the request: HIT, DB, DB-STALE or MISS. The same value appears in the body as served_from.
The dataset records observations, not liveness. A returned hostname was seen on a certificate or in a passive-DNS record at some point; whether it resolves today is a separate question, answered by the dns field and the resolve endpoint.
Reads — exports included — need no credentials, and nothing here is paid for. Signing in or presenting an API key raises the rate limit and unlocks what spends work on your say-so: forcing a re-scan, on-demand DNS resolution and security audits.
Dates. Timestamps render as DD-MM-YYYY for date-granular values and DD-MM-YYYY HH:MM:SS (UTC) for instants. These are deliberately not RFC 3339 — Date.parse will not read them. Request parameters run the other way: since and until accept RFC 3339 or YYYY-MM-DD.
"first_seen_at": "12-05-2026 10:05:50""not_before": "09-08-2026"Errors. Every failure is the same envelope — a stable code to switch on, a generic message safe to show a user, and a request_id that matches the server log line. Driver text, SQL and stack traces never appear.
{ "code": "invalid_apex", "message": "apex is not a valid domain name", "request_id": "abc/def-000001"}Provider disclosure. Non-admin callers do not see raw provider names: a sources array becomes a source_count, and per-source payloads are withheld. Build against source_count and treat sources as a bonus.
Pagination. List endpoints take page (1-based) and page_size and echo both back with a total. Filters narrow the total as well as the page.
/v1/domains/{apex}no authComing soonThe one call behind the dashboard: counts, a first page of hostnames, certificates to the inline cap, complete stats and tree, data_freshness, and the hygiene summary.
| Parameter | Required | Description |
|---|---|---|
| apex | yes | Path segment. Validated, IDNA-normalised, then collapsed to the registrable domain — api.example.co.uk resolves to example.co.uk. A bare public suffix is rejected with 400 invalid_apex. |
| refresh | no | Set to true to skip the cache and DB tiers and re-aggregate from the providers. Authenticated callers only, and honoured at most once per apex per force floor — otherwise the response carries X-Refresh: throttled or auth-required. |
Inline arrays are capped. hostnames and certificates are truncated for a large apex — check hostnames_truncated / certificates_truncated and page the rest. The counts, stats and tree are always the true totals.
A cold apex that takes longer than the inline budget answers 202 with a retry_after_seconds while the first fan-out runs in the background. That is a normal path, not an error — poll it.
/v1/domains/{apex}/hostnamesno authComing soonThe paginated, filterable, sortable subdomain list. Filters combine with AND and narrow the reported total as well as the page.
| Parameter | Required | Description |
|---|---|---|
| page · page_size | no | 1-based paging. page_size caps at 500, defaults to 100. |
| q | no | Case-insensitive substring on the hostname, up to 128 characters. Matched as a bound parameter, never interpolated into SQL. |
| branch | no | Exact first-label branch — api for api.*.domainlens.devbit.in. |
| dns | no | Last-known resolution state: up, down or unknown. |
| sort | no | name, first_seen, last_seen or depth, with a - prefix for descending. Defaults to -first_seen. Applied server-side, so a csv/txt export matches the page. |
| format | no | json (default), csv, or txt (one hostname per line). csv and txt stream the full filtered set as a download, pagination ignored. |
/v1/hostnames/{hostname}no authComing soonEverything known about a single name: identity, the sources that reported it with a corroboration confidence, the current DNS snapshot and full resolution timeline, the certificates it has appeared on, and its slice of the changefeed and the hygiene score.
/v1/domains/{apex}/hostnames/{hostname}/resolveauth requiredComing soonResolves A / AAAA / CNAME for one hostname synchronously. Expensive, so it is gated — anonymous callers get 401.
/v1/domains/{apex}/certificatesno authComing soonDistinct certificates, newest expiry first, with a normalised issuer, the lifecycle flags is_expired / expires_soon, days_to_expiry (negative once expired), validity_days for the full lifetime, and the SAN group — every name that shared the certificate, which is usually one deployment.
/v1/domains/{apex}/graphno authComing soonThe same relationship as a bipartite graph of hostnames linked by shared certificates. Clusters reveal infrastructure groupings.
/v1/domains/{apex}/securityno authComing soonA 0–100 score and letter grade computed from data already held — dangling CNAMEs at takeover-prone providers, sensitive-looking subdomains that resolve, expired or fragmented certificate operations, attack-surface shape, and single-source corroboration. It is recomputed on every refresh and stored, so ?history=N returns a series.
This is a passive heuristic, not a penetration test. Nothing is probed beyond DNS resolution; the response carries the same disclaimer inside factors.
/v1/domains/{apex}/auditauth requiredComing soonThe active counterpart to the score above: a scoped audit of the hostnames you name, running a deep TLS check, HTTP security headers, mail and DNS-auth posture, DNSSEC / CAA / AXFR checks, Shodan InternetDB, an Internet Archive sweep for forgotten endpoints and a passive technology fingerprint against each one — plus nuclei templates where the deployment enables them. The body must carry authorized: true — a claim that you may test these hosts, stored on the audit row and logged against your account.
/v1/domains/{apex}/audit/{id}auth requiredComing soonThe poll target. While the run is queued or in flight it answers 202 with a live progress block naming the host and tool in hand, and 200once it is done — findings, a per-severity summary, and the raw tool output per target. Someone else's audit is a 404, not a 403.
/v1/domains/{apex}/auditsauth requiredComing soonYour runs against this apex, with the limits block that governs the next one: hostnames per audit, audits per rolling 24 hours, and the tools this deployment permits.
Point-in-time OSINT plus light-active probing — not a penetration test. Nothing is exploited, and an anonymous caller can never start a run. Use it against hosts you are authorised to assess.
/v1/domains/{apex}/changesno authComing soonWhat the dataset gained or lost on each refresh — added, dns_up, dns_down, cert_new, security_changed — newest first, filterable by since and type.
/v1/domains/{apex}/timelineno authComing soonCumulative distinct hostnames over time, bucketed by day, week or month, with per-bucket DNS and certificate event counts. The changefeed is only populated from the second refresh onward — the first has nothing to diff against.
/v1/domains/{apex}/refreshesno authComing soonRecent refresh-log entries: what each pass discovered and how many providers succeeded.
/v1/search?q=<substring>no authComing soonSubstring match across every indexed hostname, any apex. Wildcard metacharacters are escaped and matched literally. total is capped at 10 000 (it reports 10001 when there are more) so a huge match set never forces an unbounded scan — show 10000+.
/v1/search/breakdown?q=<substring>no authComing soonThe same term seen across organisations: per-apex match counts plus the earliest first-seen date for each.
/v1/top?n=<number>no authComing soonApexes ranked by indexed hostname count, largest first.
/v1/stream/pulseno authComing soonA public text/event-stream: dataset totals every few seconds, and apex names as they land in the seed queue. Open it with EventSource — the handler ends itself after about 40 seconds and the browser reconnects with Last-Event-ID, so the feed carries on without a gap. It is the counter on the front page.
Stand-alone lookups that touch no stored data. Resolver and provider hosts are constants, and a caller-named host is dialled through a guard that rejects non-public addresses and re-checks every redirect — so there is no SSRF surface. Every one of these has a form on the utilities page, and each is linkable with its input prefilled, e.g. /utils?tool=tls&host=domainlens.devbit.in.
| Endpoint | What it does |
|---|---|
| /v1/utils/dig | dig over DNS-over-HTTPS, any record type |
| /v1/utils/propagation | the same query at every resolver, with an in_sync verdict |
| /v1/utils/rdap | WHOIS-over-RDAP registration data |
| /v1/utils/tls | live chain, negotiated parameters and a per-version support matrix |
| /v1/utils/http | fetch, trace redirects, grade security headers |
| /v1/utils/mail | SPF / DMARC / DKIM analysis and a rough grade |
| /v1/utils/cron | explain a cron expression and its next runs |
| /v1/utils/convert | YAML to JSON and back (POST) |
/v1/auth/signup · /v1/auth/loginno authComing soonEmail and password, setting an HttpOnly dl_session cookie that lasts 30 days from sign-in and is never renewed — when it runs out, sign in again. The login error is deliberately vague — no user enumeration.
/v1/auth/oauth/{provider}no authComing soonGoogle or GitHub sign-in, wherever the deployment has them set up — GET /v1/auth/providers lists which. Navigate to it rather than fetching it: it redirects to the provider, and the callback sets the same session cookie. A verified email that already has an account signs into that account.
/v1/session/keysauth requiredComing soonMints an API key. The raw dlk_… secret is returned once and never again; only its hash is stored. Three live keys per account — revoke one to mint another — and no more than five minted in any 24 hours, revoked ones included. Send it as Authorization: Bearer dlk_….
curl -s -H "Authorization: Bearer dlk_..." \ "https://<api-host>/v1/domains/domainlens.devbit.in"Reading and exporting need neither. An account — free, like everything else here — raises the rate limit and unlocks ?refresh=true, the on-demand resolve and security audits, plus the keys themselves.
~1/s
anonymous
burst 10
~3/s
signed in
burst 30
~4/s
API key
burst 40
~6/s
per account
burst 60
Rate limiting is a token bucket per caller, not per route — one bucket covers the whole /v1 surface. A signed-in request must also fit its accountbucket, which the session and every one of that user's keys share, so a second key adds no allowance. Every non-admin response carries X-RateLimit-Limit, -Remaining and -Reset so a client can self-regulate before it trips a 429, which adds Retry-After — between 1 and 60 seconds. /v1/health, /v1/openapi.yaml and /v1/auth/* are exempt. The figures above are server defaults; read the headers rather than hardcoding them.
This app serves the API from its own origin, on the same /v1/*path. That keeps the session cookie first-party, lets Google and GitHub sign-in land back here, and passes each visitor's own address through, so nobody spends a stranger's allowance.
| Purpose | Endpoint | Copy |
|---|---|---|
| Full enriched dataset | /v1/domains/domainlens.devbit.in | |
| Force a re-scan | /v1/domains/domainlens.devbit.in?refresh=true | |
| Paginated hostnames | /v1/domains/domainlens.devbit.in/hostnames?page_size=100 | |
| Filtered + sorted | /v1/domains/domainlens.devbit.in/hostnames?q=api&dns=up&sort=-last_seen | |
| Export every name | /v1/domains/domainlens.devbit.in/hostnames?format=txt | |
| Statistics | /v1/domains/domainlens.devbit.in/stats | |
| Hierarchy | /v1/domains/domainlens.devbit.in/tree?min_count=2 | |
| Certificates | /v1/domains/domainlens.devbit.in/certificates | |
| Hygiene score | /v1/domains/domainlens.devbit.in/security | |
| Changefeed | /v1/domains/domainlens.devbit.in/changes | |
| Growth over time | /v1/domains/domainlens.devbit.in/timeline | |
| Source corroboration | /v1/domains/domainlens.devbit.in/sources/breakdown | |
| One hostname | /v1/hostnames/www.domainlens.devbit.in | |
| Cross-apex search | /v1/search?q=staging | |
| Ranked apexes | /v1/top?n=100 | |
| DNS lookup | /v1/utils/dig?name=domainlens.devbit.in&type=MX | |
| TLS inspection | /v1/utils/tls?host=domainlens.devbit.in | |
| Mail authentication | /v1/utils/mail?domain=domainlens.devbit.in | |
| Liveness | /v1/health | |
| This contract | /v1/openapi.yaml |
curl -s "https://<api-host>/v1/domains/stripe.com" \ | jq '{apex, hostname_count, certificate_count, served_from}'curl -s "https://<api-host>/v1/domains/stripe.com/hostnames?format=txt" \ -o stripe-subdomains.txtcurl -s "https://<api-host>/v1/domains/stripe.com/hostnames?dns=up&page_size=500" \ | jq -r '.hostnames[].name'curl -s "https://<api-host>/v1/domains/stripe.com/security" \ | jq '{score, grade, findings: .factors.findings}'curl -s "https://<api-host>/v1/domains/stripe.com/changes?type=added" \ | jq -r '.changes[] | "\(.detected_at) \(.hostname)"'curl -s -H "Authorization: Bearer dlk_..." \ "https://<api-host>/v1/domains/stripe.com?refresh=true"curl -s "https://<api-host>/v1/utils/mail?domain=stripe.com" \ | jq '{grade, spf: .spf.all_qualifier, dmarc: .dmarc.policy}'curl -s "https://<api-host>/v1/utils/tls?host=stripe.com" \ | jq '{grade, tls_version, protocols}'