Skip to content

API documentation

One route returns every known subdomain of a domain, as JSON or plain text. Base URL: https://api.assetdiscovery.io

Quickstart

  1. Create a free account and verify your email.
  2. Create an API key under API keys. The full key is shown once, so store it right away.
  3. Export it as ASSETDISCOVERY_API_KEY and send your first request:
curl "https://api.assetdiscovery.io/api/domain/example.com" \
  -H "Api-Key: $ASSETDISCOVERY_API_KEY"

Authentication

Send your key in the Api-Key header on every request. Keys start with ad_. Keep keys on the server; never ship them in browser or mobile code. Revoking a key in the app stops it immediately.

Api-Key: ad_8f3c…

Endpoints

All formats share one route, GET /api/domain/{domain}. The format query parameter is optional: json (default), txt or full. The total field is always the real number of known subdomains, even when your plan returns fewer.

Subdomains

GET/api/domain/{domain}

Returns hostnames as a JSON array. This is the default and works on every plan, up to your plan's results-per-query limit. Results are plain hostnames: wildcard certificate names are folded into their base name unless you pass wildcards=true.

domainpath
example.comRegistrable domain or subdomain, without scheme or path.
wildcardsquery
trueOptional. Return wildcard names such as *.cloud.example.com as stored. By default they are folded into cloud.example.com.
Api-Keyheader
ad_…Your API key. Required on every request.
curl "https://api.assetdiscovery.io/api/domain/example.com" \
  -H "Api-Key: $ASSETDISCOVERY_API_KEY"
200 OK · application/json
{
  "domain": "example.com",
  "total": 4521,
  "subdomains": [
    "api.example.com",
    "auth.example.com",
    "cdn.example.com",
    "dev.api.example.com",
    "mail.example.com"
  ]
}

Plain text

GET/api/domain/{domain}?format=txt

Returns one hostname per line, ready to pipe into other tools. Needs a plan that includes export; other plans get plan_required. Errors are still returned as JSON.

domainpath
example.comRegistrable domain or subdomain, without scheme or path.
formatquery
txtOptional. Return text/plain instead of JSON.
Api-Keyheader
ad_…Your API key. Required on every request.
curl "https://api.assetdiscovery.io/api/domain/example.com?format=txt" \
  -H "Api-Key: $ASSETDISCOVERY_API_KEY" \
  -o example.com.txt
200 OK · text/plain
api.example.com
auth.example.com
cdn.example.com
dev.api.example.com
mail.example.com

Full records

GET/api/domain/{domain}?format=full

Returns each host with first_seen and last_seen, plus total and a meta block with your plan and quota.

domainpath
example.comRegistrable domain or subdomain, without scheme or path.
formatquery
fullOptional. Return records with timestamps and meta.
Api-Keyheader
ad_…Your API key. Required on every request.
curl "https://api.assetdiscovery.io/api/domain/example.com?format=full" \
  -H "Api-Key: $ASSETDISCOVERY_API_KEY"
200 OK · application/json
{
  "domain": "example.com",
  "total": 4521,
  "returned": 100,
  "truncated": true,
  "next_cursor": null,
  "subdomains": [
    { "host": "api.example.com", "first_seen": "2023-11-04T00:00:00Z", "last_seen": "2026-10-17T00:00:00Z" }
  ],
  "meta": {
    "query_ms": 38,
    "plan": "free",
    "results_limit": 100,
    "quota": { "used": 2, "limit": 10, "period": "day", "resets_at": "2026-10-21T00:00:00Z" },
    "upgrade_url": "https://assetdiscovery.io/pricing"
  }
}

Large results

No pagination is needed. One call is one query, and every format returns all the subdomains your plan allows in a single response; format only changes the shape. When truncated is true, the rest is beyond your plan's results-per-query limit and meta.upgrade_url points to the plans that return it.

total_estimated is true for very large domains; total is then an upper-bound estimate. It is omitted otherwise.

next_cursor is null on every response except on Enterprise, for domains with more than 1,000,000 subdomains. Pass it as ?cursor= to continue the same query at no extra query cost. A cursor only works for the same domain and API key and expires after 1 hour; after that, a request without a cursor starts a new, charged query.

Past your plan's limit (Pro and Team). When a domain has more subdomains than your plan returns per query, the response also carries continuation_cursor (for format=txt, theX-Continuation-Cursor header). Pass it as ?cursor= to get the next block of up to the same number of results, continuing alphabetically where the last one ended, with nothing skipped or repeated. Each block is a new query and uses one from your quota; requesting the same block again the same day is free. It is never in next_cursor, so a loop that follows next_cursor never spends queries you did not ask for. It expires after 24 hours.

# Pass next_cursor from the previous response while it is not null
curl "https://api.assetdiscovery.io/api/domain/example.com?format=full&cursor=$NEXT_CURSOR" \
  -H "Api-Key: $ASSETDISCOVERY_API_KEY"

Data

We collect names from certificate transparency logs, passive DNS and our own passive discovery. first_seen and last_seen are when we first and last observed a name, not whether it resolves today.

GET /v1/stats/top-domains?limit=100 lists the domains with the most known subdomains. It needs no key and uses no searches; limit is 1 to 1,000 and the list is rebuilt weekly. Counts are approximate, within about 1%, and domains under a shared cloud or hosting suffix are counted with the domain they belong to (registrable_domains says how many were merged).

Quota headers

Every response, including errors, carries your current quota so you can pace requests without an extra call. A 429 also includes Retry-After in seconds.

A search uses one from your quota the first time your workspace queries a domain on a given day (UTC). Querying the same domain again that day, and following its next_cursor, is free. So is a search that finds nothing, and a domain that is refused as invalid.

HeaderExampleDescription
X-Quota-Limit5Queries allowed in the current period, or "unlimited" on plans without a cap.
X-Quota-Remaining3Queries left in the current period, after this request.
X-Quota-Reset2026-10-21T00:00:00ZWhen the period resets, as an ISO 8601 UTC timestamp.
curl -s -D - -o /dev/null "https://api.assetdiscovery.io/api/domain/example.com" \
  -H "Api-Key: $ASSETDISCOVERY_API_KEY" | grep -i '^x-quota'
Response headers
HTTP/2 200
content-type: application/json
x-quota-limit: 10
x-quota-remaining: 8
x-quota-reset: 2026-10-21T00:00:00Z

Errors

Errors return a JSON body with a stable code to branch on. The message is for people and may change.

Every response carries an X-Request-ID header, and error bodies repeat it as error.request_id (req_…). Include it when you contact support and we can find exactly what happened.

429 Too Many Requests · application/json
{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Slow down and retry shortly.",
    "retry_after_seconds": 42,
    "docs_url": "https://assetdiscovery.io/docs#errors",
    "request_id": "req_8f3a1c9e2b7d4f60"
  }
}
CodeHTTPMeaningWhat to do
unauthorized401The Api-Key header is missing, malformed or the key was revoked.Check the header name and value. Create a new key under API keys if this one was revoked.
email_not_verified403The account email is not verified, so its keys are inactive.Open the verification email and confirm. You can resend it from the app.
quota_exceeded429The daily or monthly query allowance for your plan is used up. On Free, also when the free searches of every account on your network are used up for the day.Wait until resets_at or X-Quota-Reset, or upgrade your plan.
rate_limited429Requests per second for this key went over the plan's burst limit.Retry after retry_after_seconds (also sent as Retry-After). Back off instead of retrying in a loop.
plan_required402The request uses a feature your plan does not include, such as format=txt.Remove the parameter, or compare plans to find the one that includes it.
invalid_domain422The domain is not a valid hostname, its top-level domain does not exist, or it is a subdomain.Send a registrable domain such as example.com or example.co.uk, without https:// or a path. For www.example.com, search example.com: its results include every subdomain.
invalid_request422A parameter the endpoint doesn't accept, such as format=csv.Check the parameter against this page: format is json, full or txt.
not_found404There is no such endpoint.Check the path, for example /api/domain/example.com.
service_unavailable500 / 502 / 503A temporary problem on our side.Retry with exponential backoff. Failed requests do not count against your quota. If it persists, contact support with the request_id.
role_required403The caller's role in the workspace can't do this (billing needs owner or admin).Ask an owner or admin of the workspace. API keys never manage billing.
two_factor_enrollment_required403The workspace requires two-factor authentication and this member has not turned it on.Turn on two-factor in Settings. Dashboard requests only: API keys keep working.
turnstile_failed400The captcha token is missing, expired or already used.The public search on the home page only. Reload the page and search again.
impersonation_read_only403Staff are viewing the account as its user, which is read-only: no searches or changes.Only seen by staff. Stop viewing as the user to act as yourself.

Rate limits

Burst limits and query allowances apply per workspace, so all your keys and the dashboard share them. Both come from your plan. The quota headers and meta.quota show where you stand.