# Bulk DNSSEC Validation Checker (`automation-lab/bulk-dnssec-validation-checker`) Actor

Check up to 5,000 domains for DNSSEC delegation and cryptographic validation, with DS/DNSKEY details, validated, bogus, unsigned, and error statuses.

- **URL**: https://apify.com/automation-lab/bulk-dnssec-validation-checker.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.65 / 1,000 domain extracteds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Bulk DNSSEC Validation Checker

Bulk DNSSEC Validation Checker is a focused **DNSSEC checker** for domain portfolios. Supply up to 5,000 domains or website URLs and receive one audit-ready row per unique domain with DS delegation records, DNSKEY records, calculated key tags, algorithms, digest types, cryptographic validation status, diagnostics, resolver identity, and an observation timestamp.

The Actor asks public validating DNS-over-HTTPS resolvers to verify the chain of trust. It distinguishes `validated`, `bogus`, `unsigned`, `not_found`, and resolver `error` outcomes instead of treating every existing DNS record as secure.

### What does this DNSSEC checker do?

For each unique normalized domain, the Actor:

1. Queries the parent-facing DS record with DNSSEC validation enabled.
2. Queries the domain's DNSKEY record with validation enabled.
3. Reads the resolver's authenticated-data result.
4. Repeats queries with checking disabled only when validation returns SERVFAIL.
5. Uses that unchecked evidence to identify a broken signed chain.
6. Parses DS key tags, algorithms, digest types, digests, and TTLs.
7. Parses DNSKEY flags, protocol, algorithm, public key, and TTL.
8. Calculates each DNSKEY key tag according to RFC 4034.
9. Emits stable diagnostics explaining the classification.
10. Saves one typed dataset row even for unsigned, missing, and resolver-error cases.

Automatic mode uses Cloudflare first. A transport, timeout, malformed-response, or HTTP failure falls back once to Google's independent public resolver. It does not repeatedly retry deterministic DNS responses.

### Who is it for?

- Security teams auditing managed domain portfolios.
- DNS and infrastructure engineers checking deployments and key rollovers.
- Managed service providers reviewing customer zones.
- Compliance teams collecting timestamped security-control evidence.
- Domain registrars and hosting platforms validating onboarding changes.
- Developers adding DNSSEC posture to an asset inventory.
- Data teams exporting normalized DS and DNSKEY details.

The Actor reports the resolver's current observation. Schedule it when you need recurring snapshots, and compare datasets in your own workflow.

### Why use a validating DNSSEC checker tool?

A DNS record lookup can show that DS or DNSKEY data exists without proving that the records form a valid chain. This Actor combines record extraction with resolver-backed cryptographic validation.

Useful differences include:

- explicit chain status rather than record presence alone;
- DS and DNSKEY evidence in the same row;
- calculated DNSKEY key tags for matching and rollover review;
- stable finding codes for filters and alerts;
- rows for unsigned and broken domains, not silent omissions;
- deterministic input normalization and deduplication;
- public resolvers with no API key, browser, login, or proxy;
- bounded independent transport fallback;
- up to 5,000 supplied domains per run.

### What data is returned?

| Field | Meaning |
| --- | --- |
| `input` | Original supplied domain or URL |
| `domain` | Normalized lowercase ASCII hostname |
| `status` | `validated`, `bogus`, `unsigned`, `not_found`, or `error` |
| `isSecure` | `true` only for a cryptographically validated chain |
| `hasDs` | Whether DS delegation records were found |
| `hasDnskey` | Whether DNSKEY records were found |
| `authenticatedData` | Whether both validating responses were authenticated |
| `dsRecords` | DS key tag, algorithm, digest type, digest, and TTL |
| `dnskeyRecords` | Flags, protocol, algorithm, calculated key tag, key type, public key, and TTL |
| `algorithms` | Distinct numeric algorithms found |
| `algorithmNames` | Readable names for those algorithms |
| `keyTags` | Distinct DS and calculated DNSKEY key tags |
| `diagnostics` | Severity, stable code, and explanation |
| `resolver` | Resolver or resolver combination used |
| `errorCode` | Error class after all configured resolver routes fail |
| `errorMessage` | Bounded resolver error detail |
| `checkedAt` | ISO 8601 observation timestamp |
| `durationMs` | Per-domain wall-clock duration |

### Understanding DNSSEC statuses

#### `validated`

A DS delegation and DNSKEY records exist, and the validating resolver authenticated both responses. `isSecure` is `true`.

#### `bogus`

A signed chain cannot be authenticated. Typical causes include a stale parent DS record, missing DNSKEY data, or a broken signature chain. The diagnostics explain the observed shape.

#### `unsigned`

No DS record links the domain into the DNSSEC chain of trust. A domain can still resolve normally while remaining unsigned. If DNSKEY records exist without a DS delegation, the Actor adds `dnskey_without_ds`.

#### `not_found`

The resolver returned NXDOMAIN for the supplied hostname.

#### `error`

Every allowed DNS-over-HTTPS transport failed. This is different from a cryptographically bogus response and is not reported as unsigned.

### How to run your first DNSSEC check

1. Open the Actor in Apify Console.
2. Enter one or more real domains in **Domains**.
3. Keep **Automatic** resolver selection for independent transport fallback.
4. Keep concurrency at 10 for a first run.
5. Click **Start**.
6. Open the **Dataset** tab.
7. Filter `status`, `isSecure`, or a diagnostic code.
8. Export JSON, CSV, Excel, XML, or RSS as needed.

Example input:

```json
{
  "domains": [
    "cloudflare.com",
    "wikipedia.org",
    "github.com",
    "https://neverssl.com/"
  ],
  "resolver": "automatic",
  "concurrency": 10,
  "timeoutMs": 8000
}
```

A repeated hostname is normalized and checked once, so it creates one row and one domain charge.

### Input parameters

#### `domains`

Required list of 1–5,000 domains or website URLs. URLs are reduced to their hostname. Unicode domains are normalized to IDNA ASCII. IP literals, single-label names, empty strings, and malformed labels fail input validation.

The exact normalized hostname is checked. Supplying `www.domain.tld` does not automatically switch to `domain.tld`.

#### `resolver`

- `automatic`: Cloudflare first, Google only after a transport or HTTP failure;
- `cloudflare`: use only Cloudflare DNS-over-HTTPS;
- `google`: use only Google DNS-over-HTTPS.

A resolver-specific mode is useful when comparing observations. Different recursive resolvers can briefly return different cached data during DNS changes.

#### `concurrency`

Integer from 1 to 50, default 10. This controls domains in flight, while the DS and DNSKEY queries for one domain run together.

#### `timeoutMs`

Timeout from 1,000 to 30,000 milliseconds for each request, default 8,000. In automatic mode, the fallback has its own bounded timeout.

### Example output

A validated domain produces this shape; record values and timestamps change with live DNS:

```json
{
  "input": "cloudflare.com",
  "domain": "cloudflare.com",
  "status": "validated",
  "isSecure": true,
  "hasDs": true,
  "hasDnskey": true,
  "authenticatedData": true,
  "dsRecords": [
    {
      "keyTag": 2371,
      "algorithm": 13,
      "algorithmName": "ECDSAP256SHA256",
      "digestType": 2,
      "digestTypeName": "SHA-256",
      "digest": "32996839A6D808AFE3EB4A795A0E6A7A39A76FC52FF228B22B76F6D63826F2B9",
      "ttl": 86400
    }
  ],
  "dnskeyRecords": [
    {
      "flags": 257,
      "protocol": 3,
      "algorithm": 13,
      "algorithmName": "ECDSAP256SHA256",
      "keyTag": 2371,
      "keyType": "KSK",
      "publicKey": "base64-key-material",
      "ttl": 3600
    }
  ],
  "algorithms": [13],
  "algorithmNames": ["ECDSAP256SHA256"],
  "keyTags": [2371],
  "diagnostics": [
    {
      "severity": "info",
      "code": "dnssec_validated",
      "message": "The resolver cryptographically validated the DS delegation and DNSKEY response."
    }
  ],
  "resolver": "cloudflare",
  "errorCode": null,
  "errorMessage": null,
  "checkedAt": "2026-01-15T12:00:00.000Z",
  "durationMs": 84
}
```

### Diagnostic codes

- `dnssec_validated`: the validating resolver authenticated the chain;
- `dnssec_validation_failed`: validating resolution failed while unchecked queries exposed DNSSEC material;
- `validation_not_authenticated`: DS and DNSKEY exist but both answers were not authenticated;
- `no_ds_delegation`: no parent DS record was found;
- `dnskey_without_ds`: zone keys exist without a parent trust link;
- `ds_without_dnskey`: delegation exists without usable DNSKEY data;
- `domain_not_found`: resolver returned NXDOMAIN;
- `resolver_error`: configured DNS-over-HTTPS routes failed.

Codes are designed for downstream filters. Read the message as context, but automate on `status` and `code`.

### How much does it cost to validate DNSSEC domains?

The Actor uses pay-per-event pricing:

- one `start` event per run: **$0.001**;
- one `domain` event per unique domain row.

| Apify plan | Price per unique domain |
| --- | ---: |
| Free | $0.001242 |
| Bronze | $0.00108 |
| Silver | $0.0008424 |
| Gold | $0.000648 |
| Platinum | $0.000648 |
| Diamond | $0.000648 |

On Bronze, 10 unique domains cost $0.0118 including the start event. A 100-domain audit costs $0.109. Duplicate normalized inputs do not add rows or domain charges.

DNS responses are public. Charges pay for normalized bulk execution, validation classification, DS/DNSKEY parsing, diagnostics, typed storage, and Apify integrations.

### Recurring security-audit workflow

Create an Apify Task with a stable portfolio list, then schedule it daily, weekly, or monthly. Each run creates a timestamped dataset snapshot.

Common patterns:

1. Filter new rows whose status is not `validated`.
2. Compare `status`, DS digests, key tags, or algorithms with the previous dataset.
3. Send a webhook after completion.
4. Create a ticket when a production domain changes to `bogus` or `unsigned`.
5. Keep snapshots in a warehouse for compliance evidence.
6. Review expected key-rollover changes separately from unexpected delegation drift.

The Actor does not retain history or send alerts itself. Apify schedules, webhooks, and your downstream system provide those workflow layers.

### Export and integrations

Results are stored in the default dataset. You can connect them to:

- Google Sheets for portfolio review;
- Make or Zapier for status-based workflows;
- Slack, email, or ticketing through webhooks;
- BigQuery, Snowflake, PostgreSQL, or another warehouse;
- SIEM and asset-management systems;
- CI/CD checks before DNS migrations;
- registrar or hosting onboarding flows.

### Run with the Apify API

Keep your token in `APIFY_TOKEN`; do not place it in source code.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~bulk-dnssec-validation-checker/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domains":["cloudflare.com","wikipedia.org","github.com"]}'
```

#### JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/bulk-dnssec-validation-checker').call({
  domains: ['cloudflare.com', 'wikipedia.org', 'github.com'],
  resolver: 'automatic',
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map(({ domain, status, keyTags }) => ({ domain, status, keyTags })));
```

#### Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/bulk-dnssec-validation-checker').call(run_input={
    'domains': ['cloudflare.com', 'wikipedia.org', 'github.com'],
    'resolver': 'automatic',
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print([(item['domain'], item['status']) for item in items])
```

### MCP setup for Claude Code, Claude Desktop, Cursor, and VS Code

Add this Actor as an Apify MCP tool in Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/bulk-dnssec-validation-checker"
```

For **Claude Desktop**, **Cursor**, or **VS Code**, add this equivalent HTTP server configuration in the client's MCP settings:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/bulk-dnssec-validation-checker"
    }
  }
}
```

Example prompts:

- "Check these domains for DNSSEC and return only unsigned or bogus results."
- "Extract DS algorithms, digest types, and DNSKEY key tags for this portfolio."
- "Run the DNSSEC audit Task and summarize changes from my previous dataset."

The last prompt requires the client or another tool to retrieve the previous dataset; this Actor returns the current snapshot.

### Accuracy and limitations

- Results reflect the selected recursive resolver and its cache at `checkedAt`.
- DNS changes can take time to propagate through TTLs.
- Validation is delegated to a public validating resolver; the Actor is not a full independent iterative resolver.
- A `bogus` result is a strong operational signal, but inspect diagnostics and compare another resolver before emergency remediation.
- `unsigned` means no DS trust link was observed, not that the website or domain is malicious.
- DS and DNSKEY output includes current answers, not historical records.
- The Actor does not test TLS certificates, DMARC, SPF, DKIM, nameserver diversity, or website security headers.
- Public resolver availability and rate limits are outside the Actor's control.
- Only public domain names are supported; private split-horizon zones are not.

### Legality and responsible use

DNS delegation and key records are public infrastructure data. Use the Actor for domains you are authorized to manage, assess, or inventory, follow applicable policies, and avoid using bulk results to misrepresent a domain owner's security posture.

Do not treat one resolver snapshot as legal, compliance, or incident-response proof by itself. Preserve the timestamp, resolver identity, input, and relevant operational context.

### Troubleshooting

#### Why is a domain `unsigned` even though it has DNSKEY records?

Publishing DNSKEY records inside a zone does not create a chain of trust. The parent zone must publish a matching DS record. The Actor reports `dnskey_without_ds` for this shape.

#### Why did a known domain return `bogus`?

A stale DS during a key rollover, expired or invalid signatures, unavailable keys, or temporary propagation can break validation. Re-run against the other fixed resolver and inspect the DS/DNSKEY key tags before changing production DNS.

#### Why do two runs differ?

Recursive resolver caches can observe a rollout at different times. Compare `resolver`, `checkedAt`, and record TTLs. Use a fixed resolver when you need like-for-like snapshots.

#### Why is the run slower for broken domains?

A validation SERVFAIL triggers bounded checking-disabled DS and DNSKEY queries so the output can distinguish broken signed material from missing records.

#### Does an error row cost a domain event?

Yes. Every valid unique input produces a structured row and domain charge, including resolver-error evidence. Invalid input fails before processing.

### Related automation-lab Actors

- [Bulk DNS AAAA Record Checker](https://apify.com/automation-lab/bulk-dns-aaaa-record-checker) for IPv6 readiness and AAAA TTLs.
- [Bulk DMARC Record Checker](https://apify.com/automation-lab/bulk-dmarc-record-checker) for email-domain policy parsing and findings.
- [Domain Registration & RDAP Lookup](https://apify.com/automation-lab/domain-rdap-lookup) for registrar, lifecycle, status, and nameserver metadata.
- [theHarvester Domain OSINT Collector](https://apify.com/automation-lab/theharvester-domain-osint-collector) for broader public attack-surface findings.

Choose this Actor when the specific job is DNSSEC delegation and cryptographic validation with DS/DNSKEY audit evidence.

### FAQ

#### Does it require an API key for DNS?

No. It uses public Cloudflare and Google DNS-over-HTTPS endpoints. An Apify token is required only when invoking the Actor through Apify's API.

#### Can I check URLs?

Yes. The Actor extracts and validates the URL hostname. Paths, query strings, and fragments are ignored.

#### Are duplicate domains charged twice?

No. Inputs are normalized and deduplicated before lookups and charging.

#### Can I check internationalized domains?

Yes. Unicode hostnames are converted to their ASCII IDNA form and returned in `domain`.

#### Can I choose a custom recursive resolver?

No. This release supports Cloudflare, Google, or automatic Cloudflare-to-Google transport fallback because their JSON APIs expose the validation controls required by the output contract.

#### Does it prove a website is secure?

No. DNSSEC protects DNS authenticity. It does not replace HTTPS, application security, email security, malware scanning, or access controls.

#### Can it monitor changes automatically?

Use an Apify Task and schedule. The Actor emits timestamped current snapshots; your integration compares them or creates alerts.

#### How many domains can one run check?

Up to 5,000 supplied entries. Normalization and deduplication can reduce the number of emitted rows.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/bulk-dnssec-validation-checker/changelog.md

# Actor input Schema

## `domains` (type: `array`):

Domains or website URLs to validate. Duplicate normalized hostnames are checked once (maximum 5,000).

## `resolver` (type: `string`):

Automatic uses Cloudflare first and falls back to Google after transport or HTTP errors. A fixed resolver is useful for resolver-specific comparisons.

## `concurrency` (type: `integer`):

Number of domains processed concurrently. Reduce this for very large scheduled audits if an upstream resolver throttles requests.

## `timeoutMs` (type: `integer`):

Maximum time for each DNS-over-HTTPS request before the automatic resolver can fall back.

## Actor input object example

```json
{
  "domains": [
    "cloudflare.com",
    "wikipedia.org",
    "github.com"
  ],
  "resolver": "automatic",
  "concurrency": 10,
  "timeoutMs": 8000
}
```

# Actor output Schema

## `overview` (type: `string`):

Open the default dataset using the DNSSEC audit overview.

## `dataset` (type: `string`):

Retrieve complete DS, DNSKEY, diagnostic, and timing records through the dataset API.

# 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 = {
    "domains": [
        "cloudflare.com",
        "wikipedia.org",
        "github.com"
    ],
    "resolver": "automatic",
    "concurrency": 10,
    "timeoutMs": 8000
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/bulk-dnssec-validation-checker").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 = {
    "domains": [
        "cloudflare.com",
        "wikipedia.org",
        "github.com",
    ],
    "resolver": "automatic",
    "concurrency": 10,
    "timeoutMs": 8000,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/bulk-dnssec-validation-checker").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 '{
  "domains": [
    "cloudflare.com",
    "wikipedia.org",
    "github.com"
  ],
  "resolver": "automatic",
  "concurrency": 10,
  "timeoutMs": 8000
}' |
apify call automation-lab/bulk-dnssec-validation-checker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/bulk-dnssec-validation-checker"
        }
    }
}
```

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/Wzu3ZTzsfTNm27jIS/builds/78OqEsp6tdTLLJtfZ/openapi.json
