SSL Certificate Checker and DNS Lookup: SPF, DMARC, Expiry avatar

SSL Certificate Checker and DNS Lookup: SPF, DMARC, Expiry

Pricing

$2.00 / 1,000 domains

Go to Apify Store
SSL Certificate Checker and DNS Lookup: SPF, DMARC, Expiry

SSL Certificate Checker and DNS Lookup: SPF, DMARC, Expiry

Returns DNS records (A, AAAA, MX, NS, TXT, CAA), parsed SPF and DMARC and the SSL/TLS certificate (issuer, expiry date, days left, SAN) for each domain, with a status and a list of issues. Bulk, no API key, onlyNew for change and expiry monitoring.

Pricing

$2.00 / 1,000 domains

Rating

0.0

(0)

Developer

Viktor Wiberg

Viktor Wiberg

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 hours ago

Last modified

Share

An SSL certificate checker, DNS lookup and SPF/DMARC check for a list of domains in one run. For each domain it returns the DNS records (A, AAAA, MX, NS, TXT and CAA), the SPF and DMARC records parsed into fields, and the SSL/TLS certificate served on port 443: issuer, valid from and to, days left, subject alternative names (SAN), protocol and whether it is trusted and matches the name. Each row ends in a status (ok, warning or error) and a list of issues in plain words, such as "SSL: the certificate expires in 12 days." or "DMARC: Policy p=none only monitors: spoofed mail is not stopped."

No API key, no third-party service. The actor asks DNS directly and opens a TLS handshake to the domain's own server, nothing else. With onlyNew it becomes a monitor: a daily run delivers only the domains where something changed, for example a certificate that has entered its warning window, a renewed certificate, a new MX host or a changed SPF record.

Example from a real run

This is the input and one row of the output from a run on the Apify platform on 3 October 2026 (run YZyLnshoRE6gZ61Lo). The certificate lists 41 names; 37 of them are cut from the san list below to keep it short. Nothing else is edited.

Input (empty input checks these three domains):

{
"domains": ["nightwave.se", "example.com", "wikipedia.org"]
}

Output row for wikipedia.org:

{
"domain": "wikipedia.org",
"status": "ok",
"issues": [],
"sslDaysLeft": 31,
"sslValidTo": "2026-11-03T19:15:40.000Z",
"sslIssuer": "YE2",
"mxHosts": [
"mx-in1001.wikimedia.org",
"mx-in2001.wikimedia.org"
],
"nameservers": [
"ns0.wikimedia.org",
"ns1.wikimedia.org",
"ns2.wikimedia.org"
],
"spfAll": "~",
"dmarcPolicy": "reject",
"dns": {
"a": [
"208.80.154.224"
],
"aaaa": [
"2620:0:861:ed1a::1"
],
"mx": [
{
"priority": 10,
"exchange": "mx-in1001.wikimedia.org"
},
{
"priority": 10,
"exchange": "mx-in2001.wikimedia.org"
}
],
"ns": [
"ns0.wikimedia.org",
"ns1.wikimedia.org",
"ns2.wikimedia.org"
],
"txt": [
"google-site-verification=AMHkgs-4ViEvIJf5znZle-BSE2EPNFqM1nDJGRyn2qk",
"v=spf1 include:_cidrs.wikimedia.org ~all",
"yandex-verification: 35c08d23099dc863"
],
"caa": [
"0 iodef \"mailto:dns-admin@wikimedia.org\"",
"0 issue \"letsencrypt.org\"",
"0 issue \"pki.goog\""
],
"spf": {
"record": "v=spf1 include:_cidrs.wikimedia.org ~all",
"all": "~",
"includes": [
"_cidrs.wikimedia.org"
],
"lookupCount": 1,
"warnings": []
},
"dmarc": {
"record": "v=DMARC1; p=reject; rua=mailto:dmarc-rua@wikimedia.org;",
"policy": "reject",
"subdomainPolicy": null,
"pct": 100,
"reportDomains": [
"wikimedia.org"
],
"alignmentDkim": "relaxed",
"alignmentSpf": "relaxed",
"warnings": []
},
"exists": true,
"errors": []
},
"ssl": {
"issuer": "YE2",
"issuerOrganization": "Let's Encrypt",
"subject": "*.wikipedia.org",
"validFrom": "2026-08-05T19:15:41.000Z",
"validTo": "2026-11-03T19:15:40.000Z",
"daysLeft": 31,
"san": [
"*.m.mediawiki.org",
"*.m.wikibooks.org",
"*.m.wikidata.org",
"*.m.wikimedia.org",
"... 37 more names"
],
"serialNumber": "062387C4D2B6E686767EFF54CE0B7E26BDDF",
"fingerprintSha256": "08:E0:B6:5D:F4:1F:B0:75:EC:91:C1:DC:D2:CA:CC:4E:8C:7D:DA:53:B7:54:80:65:25:11:7B:32:F7:B7:32:53",
"protocol": "TLSv1.3",
"isTrusted": true,
"trustError": null,
"hostnameMatches": true
},
"sslError": null,
"checkedAt": "2026-10-03T13:33:37.133Z",
"changeFingerprint": "aed2d80383644afb"
}```
The other two rows had status `warning`: `nightwave.se` because its DMARC policy is `p=none` and it has no CAA record, and `example.com` because its DMARC record has no `rua` address and it has no CAA record. The run checked three domains in about two seconds and used 0.0002 USD of platform usage.
## Input
| Field | Type | Default | Description |
|---|---|---|---|
| `domains` | array | three examples | Domains or URLs. `https://www.site.se/path` is reduced to `www.site.se`, international names are converted to punycode (`räksmörgås.se` to `xn--rksmrgs-5wao1o.se`), and duplicates are checked once. Invalid entries are skipped with a warning in the log. |
| `checks` | array | `["dns", "ssl"]` | `dns` for the DNS records, SPF and DMARC, `ssl` for the certificate. Use `["ssl"]` for certificate expiry only. |
| `expiryWarningDays` | integer | `30` | A certificate that expires within this many days gives status `warning`. An expired one gives `error`. |
| `maxResults` | integer | `50` | Highest number of domains per run. Raise it for big lists (up to 10 000). |
| `onlyNew` | boolean | `false` | Deliver and charge only domains that changed since the last run with the same input. See "Monitoring and scheduling". |
## Output
One row per domain, in the order given. The table view in Apify Console shows the summary fields; the full row has the details.
| Field | Description |
|---|---|
| `domain` | The host name that was checked, lower case ASCII |
| `status` | `error` when something is broken (the domain does not exist, the certificate has expired, is not valid for the name or is not trusted, no TLS answer), `warning` when something should be fixed (certificate expires soon, missing MX, SPF, DMARC or CAA, weak SPF or DMARC), else `ok` |
| `issues` | Every finding behind the status, errors first |
| `sslDaysLeft` | Whole days until the certificate expires, negative after expiry |
| `sslValidTo` | Certificate expiry time, ISO 8601 in UTC |
| `sslIssuer` | Name of the issuing CA certificate, for example `R11` or `WE1` |
| `mxHosts` | Mail servers, lowest priority number first |
| `nameservers` | NS records |
| `spfAll` | Qualifier of the SPF `all` mechanism: `-` (fail), `~` (softfail), `?` (neutral) or `+` (pass) |
| `dmarcPolicy` | DMARC `p` value: `none`, `quarantine` or `reject` |
| `dns` | All records: `a`, `aaaa`, `mx`, `ns`, `txt`, `caa`, and `spf` (record, all, includes, lookupCount, warnings) and `dmarc` (record, policy, subdomainPolicy, pct, reportDomains, alignmentDkim, alignmentSpf, warnings) |
| `ssl` | `issuer`, `issuerOrganization`, `subject`, `validFrom`, `validTo`, `daysLeft`, `san`, `serialNumber`, `fingerprintSha256`, `protocol`, `isTrusted`, `trustError`, `hostnameMatches` |
| `sslError` | Why no certificate could be read, for example a timeout or a refused connection |
| `changeFingerprint` | Short hash of the domain's state, used by `onlyNew`. It changes when a record, the certificate or the status changes |
| `checkedAt` | Time of the check, ISO 8601 in UTC |
## Monitoring and scheduling
Set `onlyNew` to `true` and run the actor every day on your list of domains. The first run delivers every domain. After that a domain is delivered (and charged) only when its state has changed since the previous run with the same input: a new or renewed certificate, a changed MX, NS, TXT or CAA record, a domain that stops resolving, or a status change, such as a certificate that crosses `expiryWarningDays`. A day passing on its own is not a change, so a quiet day gives an empty dataset.
The state is a list of fingerprints in a named key-value store in your Apify account (`nightwave-state-domain-inspector`), one record per input. `onlyNew` and `maxResults` are not part of the remembered input. The IP addresses of A and AAAA records are left out of the fingerprint, because CDNs and load balancers answer with a different address on each lookup; a domain that loses all its addresses still counts as a change. To start over, delete the record in the key-value store.
A second run straight after the first, with `onlyNew` and the same input, returns 0 rows and is not charged, because nothing has changed in between.
Example: every morning at 07:00, report the domains whose certificate expires within 21 days or whose records changed. In Apify Console, open **Schedules**, create a schedule with the cron expression `0 7 * * *` and add this actor with the input below. Connect a webhook or an integration (Slack, e-mail, Make, Zapier) to the run to get the rows where you work.
```json
{
"domains": ["example.com", "shop.example.com", "example.se"],
"expiryWarningDays": 21,
"maxResults": 500,
"onlyNew": true
}

The same schedule through the Apify API:

curl -X POST "https://api.apify.com/v2/schedules?token=<YOUR_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"name": "daily-domain-check", "cronExpression": "0 7 * * *", "timezone": "Europe/Stockholm", "isEnabled": true, "isExclusive": true,
"actions": [{"type": "RUN_ACTOR", "actorId": "nightwave-owner~domain-inspector",
"runInput": {"contentType": "application/json; charset=utf-8", "body": "<the input above as a JSON string>"}}]}'

Use cases

  • Never miss a certificate renewal: a daily list of every domain whose certificate expires within your warning window, across all your sites, subdomains and customers.
  • Audit e-mail authentication: which of your domains lack SPF or DMARC, use +all, exceed the 10 lookup limit or still sit at p=none.
  • Watch for unexpected DNS changes on important domains (new MX or NS hosts, changed TXT records).
  • Enrich a list of company websites with mail provider (from MX), DNS host (from NS) and certificate authority.
  • Feed an AI agent through the Apify MCP server: a run with one domain answers in a few seconds.

Limitations

  • No WHOIS or RDAP data yet. Registrar, registration and expiry dates of the domain itself are not included in this version. See "Data source and license".
  • One certificate per name. The certificate is read from port 443 of the exact name you give, so example.com and www.example.com are separate checks. Servers behind a load balancer may serve different certificates; you get the one that answered.
  • Trust is checked against Node.js's built-in list of root certificates (Mozilla's CA store). isTrusted: false with trustError explains why, for example DEPTH_ZERO_SELF_SIGNED_CERT or UNABLE_TO_VERIFY_LEAF_SIGNATURE (a missing intermediate certificate).
  • SPF lookups are counted in the record itself (include, a, mx, ptr, exists, redirect). Includes inside includes are not followed, so a record under 10 can still exceed the limit in total.
  • DMARC report addresses are reduced to their domain (reportDomains) so no mailbox names end up in the output.
  • DNS answers come from the resolver of the Apify platform, with a 4 second timeout and one retry. A lookup that times out or fails is asked again at public resolvers (1.1.1.1, 8.8.8.8, 9.9.9.9). A lookup that still fails is listed in issues and gives status error, so check again before acting on it.
  • Polite by design. One TLS handshake and a handful of DNS queries per domain, ten domains at a time. No HTTP request is sent and no page is downloaded.

FAQ

What does 1 000 domains cost? 2 USD (0.002 USD per domain, event domain), plus Apify platform usage, which is small: a test run with 60 well known domains took 5 seconds and used 0.0004 USD.

Why did a domain show up with onlyNew when nothing seemed to change? Something in its state did: compare changeFingerprint, issues and the records with the previous row. A common cause is a certificate that was renewed, or one that crossed the warning window.

Can I check only certificates? Yes, set checks to ["ssl"].

Data source and license

The actor uses only open Internet protocols and asks each domain's own servers, so there is no third-party data source and no terms of use to accept:

  • DNS (RFC 1035) for A, AAAA, MX, NS, TXT and CAA (RFC 8659) records, SPF (RFC 7208) and DMARC (RFC 7489).
  • The TLS handshake (RFC 8446) on port 443, reading the certificate the server presents to every visitor.

The output contains only what the domain owner publishes in DNS and in the certificate. No personal data is looked up. TXT and CAA records are returned as published; DMARC report addresses are reduced to their domain.

RDAP (registration data) is not part of this version. The terms of use of Verisign's RDAP service for .com and .net, read on 3 October 2026 at verisign.com, say that the data may not be used to "enable high volume, automated, electronic processes that send queries or data to the systems of Verisign or an ICANN-accredited registrar, except as reasonably necessary to register domain names or modify existing registrations". Until it is settled how a bulk tool can respect that, the actor does not query RDAP.

Pricing

Pay per result: 0.002 USD per delivered domain (event domain), which is 2 USD per 1 000. Duplicates and invalid entries are not charged, and with onlyNew unchanged domains are not charged. Apify bills platform usage on top as usual.

Contact

Built and maintained by Nightwave AB. Questions, bugs and feature requests: kontakt@nightwave.se

På svenska

Actorn kontrollerar en lista med domäner och ger för varje domän DNS-posterna (A, AAAA, MX, NS, TXT och CAA), SPF- och DMARC-posterna uppdelade i fält, och SSL/TLS-certifikatet på port 443: utfärdare, giltighetstid, dagar kvar, alternativa namn (SAN) och om det är betrott och gäller namnet. Varje rad får status ok, warning eller error och en lista med anmärkningar i klartext.

  • Ingen API-nyckel och ingen tredjepartstjänst: actorn frågar DNS och gör en TLS-handskakning mot domänens egen server. Ingen webbsida hämtas.
  • Med onlyNew: true och en daglig körning levereras och debiteras bara domäner där något har ändrats, till exempel ett certifikat som närmar sig utgångsdatum (expiryWarningDays, standard 30 dagar), ett förnyat certifikat eller en ny MX-post.
  • Inga personuppgifter: bara det domänägaren själv publicerar i DNS och i certifikatet. Adresser för DMARC-rapporter kortas till domänen.
  • RDAP (registrar och registreringsdatum) ingår inte i den här versionen, eftersom Verisigns villkor för .com och .net inte tillåter automatiserade frågor i stor volym.
  • Pris: 0,002 USD per domän (2 USD per 1 000) plus Apifys plattformsanvändning.
  • Kontakt: kontakt@nightwave.se