# Public DNS Email Policy Evidence API (`soilair/public-dns-email-policy-evidence-api`) Actor

Observe MX, SPF and exact-domain DMARC records via Google Public DNS, with source queries, stable policy hashes and explicit limitations. No SMTP or mailbox tests.

- **URL**: https://apify.com/soilair/public-dns-email-policy-evidence-api.md
- **Developed by:** [Salih Can Kurnaz](https://apify.com/soilair) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 domain observations

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Public DNS Email Policy Evidence API

Observe public MX, SPF TXT and exact-domain DMARC TXT records with query URLs, resolver DNS status, stable policy hashes and documented limitations. Use it to monitor a domain's published email configuration or enrich source-backed business-domain records. This is not an email verifier or deliverability guarantee.

### Quick start

```json
{"domains": ["python.org", "example.com"]}
```

Download the dataset as JSON, CSV or Excel. Each row covers three completed DNS queries for one normalized domain. Read OUTPUT.errors before using completeness. Up to ten domains; duplicate normalized inputs are queried once. Bare domains only, IDNA-normalized, at most 246 ASCII characters. No URL, mailbox, IP, wildcard or private/reserved suffix inputs.

### Evidence and policy observations

Uses the official, unauthenticated Google Public DNS JSON resolver endpoint. Sends MX and TXT queries for the domain, and TXT for \_dmarc.domain. Disables EDNS client-subnet forwarding with 0.0.0.0/0 and keeps resolver DNSSEC validation enabled. Resolver-seen AD is evidence of the response flag, not independent DNSSEC verification. Query responses must match the requested question, name and type. Answer ownership is bound to the query or a bounded returned CNAME chain; no alias is separately fetched. Resolver errors, truncation and inconsistent answers are source errors, not missing policies. NOERROR without policy records and NXDOMAIN are billable completed observations.

MX priority/target and TTL are preserved. A singleton priority-0 exchange '.' is flagged nullMxObserved. Current unquoted TXT values are preserved; presentation-format quoted segments are concatenated with DNS string escaping, while rawData remains available as evidence. Unrelated TXT verification records are not exported. Multiple SPF or DMARC records and duplicate/unrecognized DMARC p tags are warned. declaredExactDomainDmarcPolicy is only the observed p value from one recognized, nonduplicate-tag exact-name DMARC record; it is not an enforcement or authentication result. A missing exact-domain DMARC record does not establish absence of inherited policy.

No SMTP connections, emails, mailbox checks, sender-IP SPF evaluation, include/redirect expansion, SPF lookup-budget calculation, DKIM selector discovery, organizational-domain/DMARC inheritance, report authorization, reputation, blacklist or message alignment checks. Public DNS cache contents are point-in-time observations; no sender authenticity, delivery, domain-registration availability or security certification is inferred. CNAME/data are resolver-supplied evidence. DNS targets and policy strings are not contacted or executed.

### Change monitoring

Pass SNAPSHOT.previousHashes as previousHashes in the next input. NO_BASELINE/FIRST_SEEN/UNCHANGED/CHANGED compare normalized records, statuses and warnings. TTL, record ordering, quoting segmentation, AD flag and observedAt do not change the hash. FIRST_SEEN means absent from the supplied baseline, not a newly registered domain. Snapshot includes only emitted rows; missing rows cannot establish deletion. No hidden persistent state or schedules.

### Planned pricing

Intended publication price: $0.003 per completed domain observation ($3 per 1,000), plus $0.005 Actor-start for the default 256 MB. Higher memory can multiply startup events. Event-only pricing includes platform usage; no paid upstream, proxy or browser. All three DNS query responses must succeed consistently before a result event. Missing policies can still be billable. Failed domains incur no result event; startup may still apply. Budget truncation reports PARTIAL_BUDGET and complete=false. Check effective Store pricing before running: a private draft has no published paid pricing.

### Requests and data

Only the fixed Google Public DNS service and its HTTP redirects are network targets; queried domain/MX/SPF/DMARC addresses are not contacted. HTTP public-IP validation and IP pinning, TLS hostname checks, five-redirect limit, 2 MiB decoded responses and approximately 95 seconds fetch budget. Three queries per domain are paced with 0.1-second gaps. Published resolver API requests are distinct from website HTML crawling and do not fetch a robots page. No credential, cookie, proxy or blocking workaround. Google sees the queried domains and the Actor request's source IP; EDNS forwarding is disabled. Inputs/results stay in your Apify run storage under retention settings; no separate analytics endpoint. Do not submit private names or secrets.

### Integration and support

After publication: POST https://api.apify.com/v2/actors/soilair~public-dns-email-policy-evidence-api/runs with your Apify authorization header and input JSON. Poll the run ID and retrieve the dataset and OUTPUT/SNAPSHOT in its key-value store. Developed with AI assistance; fixtures, public-source checks and a private cloud smoke qualify documented behavior, not universal DNS reachability. Report a run ID and expected behavior without credentials.

References: https://developers.google.com/speed/public-dns/docs/doh/json · https://www.rfc-editor.org/rfc/rfc7208.html · https://www.rfc-editor.org/rfc/rfc7489.html

# Actor input Schema

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

1..10 public bare domains; no email addresses, URLs, IPs, wildcard or private/reserved suffixes. IDNA-normalized, at most 246 ASCII characters.

## `previousHashes` (type: `object`):

At most 2000 domain to SHA256 mappings from previous SNAPSHOT. TTL and observation-time differences do not change the policy hash.

## Actor input object example

```json
{
  "domains": [
    "python.org",
    "example.com"
  ],
  "previousHashes": {}
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `snapshot` (type: `string`):

No description

# 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": [
        "python.org",
        "example.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("soilair/public-dns-email-policy-evidence-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 = { "domains": [
        "python.org",
        "example.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("soilair/public-dns-email-policy-evidence-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 '{
  "domains": [
    "python.org",
    "example.com"
  ]
}' |
apify call soilair/public-dns-email-policy-evidence-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,soilair/public-dns-email-policy-evidence-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/CCTcm9jfamiIl2o8j/builds/cs2oVPXOwtqDuFO6c/openapi.json
