# Subdomain Discovery API (`crawland/subdomain-discovery-api`) Actor

Paginated subdomain enumeration for any registered domain — DNS records, registrar, WHOIS, vendor reputation, and category labels per subdomain, served from the Crawland threat-intelligence backend.

- **URL**: https://apify.com/crawland/subdomain-discovery-api.md
- **Developed by:** [Crawland](https://apify.com/crawland) (community)
- **Categories:** Developer tools, Other, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 subdomain discoveries

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Subdomain Discovery API

Paginated subdomain enumeration for any registered domain — DNS records, registrar, WHOIS, vendor reputation, and category labels per subdomain.

### API Overview

Subdomain Discovery API answers a single, high-value question: **"What does this domain's attack surface actually look like?"**

Send a registered domain — `example.com` — and get back the discovered subdomains along with the per-subdomain context you need to triage exposure: DNS records, registrar, WHOIS, popularity ranks, vendor reputation stats, tags, and category labels.

### What you get on every request

- **`subdomains`** — an array of per-subdomain objects, each with:
  - `subdomain` — the FQDN (e.g. `staging-api.example.com`).
  - `dns_records` — A / AAAA / CNAME / MX records with TTLs.
  - `registrar`, `whois` — registrar string and the raw WHOIS block.
  - `popularity_ranks` — Alexa / Cisco Umbrella / Cloudflare Radar / Majestic ranks where available.
  - `reputation`, `security_vendor_analysis_stats` — vendor verdict tally so you can spot the subdomain that flipped malicious without re-enriching.
  - `tags`, `categories`, `tld`, `modification_date`, `dns_records_update_date`.
- **`cursor`** — opaque pagination token. Pass it back as the `cursor` query parameter to fetch the next page. Empty / missing cursor means "no more results".

Page size is fixed at 10 subdomains per request.

### Pagination pattern

1. First request: `GET /scan?query=example.com` — returns the first 10 subdomains plus a `cursor`.
2. Subsequent requests: `GET /scan?query=example.com&cursor=<cursor>` — returns the next 10 subdomains plus the next cursor.
3. Stop when the response carries no `cursor` (or an empty one).

Cursors are tied to the `query` they were issued for — do not mix cursors across different domains.

### What can you do with this API?

- 🎯 **Attack surface in one call** — no juggling between DNS, WHOIS, and reputation APIs. One request, full per-subdomain context.
- 🧠 **Reputation built in** — every subdomain already comes with `security_vendor_analysis_stats`, so you can flag a leaked staging subdomain on the first pass.
- 📚 **Cursor pagination** — bounded payloads, simple to integrate into any ASM crawl pipeline.
- 🔒 **Battle-tested** — used in production by ASM platforms, pentest teams, and bug-bounty hunters.

### Response model

Every successful request returns:

```json
{
  "is_success": true,
  "response_code": 200,
  "message": "Success",
  "data": {
    "search_type": "domain",
    "subdomains": [
      {
        "subdomain": "staging-api.example.com",
        "tld": "com",
        "registrar": "MarkMonitor Inc.",
        "dns_records": [{ "type": "A", "value": "1.2.3.4", "ttl": 300 }],
        "security_vendor_analysis_stats": { "harmless": 0, "malicious": 0, "undetected": 91 }
      }
    ],
    "cursor": "eyJsaW1pdCI6IDEwLCAib2Zmc2V0IjogMTB9"
  }
}
```

Always inspect `is_success` rather than relying on the HTTP status — invalid inputs and lookup misses are also returned with HTTP 200 and `is_success: false`.

### Use cases

- **Attack surface management (ASM)** — continuously enumerate an organisation's external footprint and flag staging / preproduction / forgotten subdomains.
- **Penetration testing reconnaissance** — fast pivot from a single domain to the full subdomain inventory.
- **Subdomain takeover detection** — surface dangling subdomains pointing at unclaimed cloud resources.
- **Brand protection** — catch lookalike subdomains and verify ownership.
- **Bug bounty scoping** — quickly understand what targets are in scope.

### How is this different from IoC Lookup / IoC Enrichment?

Subdomain Discovery takes one domain and returns its subdomains. IoC Lookup takes one indicator and returns reputation + vendor verdicts. IoC Enrichment takes one indicator and returns OSINT context (adversary, malware family, MITRE ATT\&CK). They are complementary — discovery finds the subdomains, then enrichment / lookup tells you which matter.

### Need something custom or need support?

Looking for bulk / streaming / on-prem, a different response format, or help with setup? Send us a DM and we'll be happy to help you find the best setup for your use case.

# Actor input Schema

## `query` (type: `string`):

The registered domain to enumerate subdomains for (no scheme, no path).

## `cursor` (type: `string`):

Opaque pagination cursor from a previous response. Omit on the first request; pass the returned cursor to fetch the next 10 subdomains.

## Actor input object example

```json
{
  "query": "tesla.com"
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "query": "tesla.com"
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawland/subdomain-discovery-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "query": "tesla.com" }

# Run the Actor and wait for it to finish
run = client.actor("crawland/subdomain-discovery-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "tesla.com"
}' |
apify call crawland/subdomain-discovery-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawland/subdomain-discovery-api"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/rPIyVnWfB9Jhxc5Om/builds/a6veH08Bbj6ehPqUI/openapi.json
