---
title: "Domains · API v1"
description: "The domains you monitor and their health."
---
# Domains

The domains you monitor and their health.

## List domains

`GET /domains`

Every domain the organization monitors, added manually or discovered from reports, with its verification state per protocol.

- Permission: `domain:read`
- Key scope: `read`, `full`

### Query parameters

| Name | Type | Description |
| --- | --- | --- |
| `limit` | `integer` | Page size. Values above the maximum are clamped; zero, negative or non-numeric values are ignored and the default applies. Default 50. |
| `offset` | `integer` | Number of rows to skip; combine with limit to page. A negative or non-numeric value is treated as 0. Default 0. |

### Example request

```bash
curl "https://api.dmarcguard.io/api/v1/domains" \
  -H "Authorization: Bearer dg_your_key"
```

### Response

| Field | Type | Notes |
| --- | --- | --- |
| `domains` | `object[]` | The requested page. Never null; an empty page is []. |
| `domains[].id` | `string` (optional) | Identifier, Crockford Base32. Opaque; never parse it as a number. |
| `domains[].domain` | `string` | The monitored domain. |
| `domains[].verified` | `boolean` | Legacy boolean; prefer dmarc_state, which distinguishes no record from a record that reports elsewhere and from an inconclusive check. |
| `domains[].dmarc_state` | `string` | State of the domain's DMARC record: pending before the first check, verified when its own record reports to you, verified_inherited when the organizational parent's record does, not_published when the authoritative nameservers say there is none, published_not_ours when a record exists but reports elsewhere, multiple_records when the name serves more than one (receivers discard them all), and unconfirmed when the check could not reach authoritative DNS. unconfirmed is not a negative. One of "pending", "verified", "verified_inherited", "not_published", "published_not_ours", "unconfirmed", "multiple_records". |
| `domains[].tlsrpt_enabled` | `boolean` | Whether TLS reporting is switched on for this domain. |
| `domains[].tlsrpt_state` | `string` | State of the domain's TLS-RPT record, on the same scale as dmarc_state. One of "pending", "verified", "verified_inherited", "not_published", "published_not_ours", "unconfirmed", "multiple_records". |
| `domains[].forensic_enabled` | `boolean` | Whether failure (forensic) reporting is switched on for this domain. |
| `domains[].forensic_state` | `string` | State of the failure-report address in the domain's DMARC record, on the same scale as dmarc_state. One of "pending", "verified", "verified_inherited", "not_published", "published_not_ours", "unconfirmed", "multiple_records". |
| `domains[].aprf_enabled` | `boolean` | Whether APRF performance reporting is switched on for this domain. |
| `domains[].aprf_state` | `string` | State of the domain's APRF record, on the same scale as dmarc_state. One of "pending", "verified", "verified_inherited", "not_published", "published_not_ours", "unconfirmed", "multiple_records". |
| `domains[].source` | `string` | How the domain got here: manual if you added it, auto if a report introduced it, both when both are true. |
| `domains[].verified_source` | `string` (optional) | Why the domain counts as verified: self for its own record, inherited for the organizational parent's. Empty when it is not verified. |
| `domains[].inherited_from` | `string` (optional) | The organizational parent whose record covers this domain; present only when verified_source is inherited. |
| `domains[].report_count` | `integer` | Aggregate reports held for this domain. |
| `domains[].verified_at` | `string` (nullable, optional) | RFC 3339 UTC instant with whole seconds, e.g. 2026-09-15T16:03:43Z; null when absent. |
| `total` | `integer` | Domains the organization monitors, before paging. |
| `limit` | `integer` | The page size actually applied. |
| `offset` | `integer` | The offset actually applied. |

### Errors

| Status | When |
| --- | --- |
| `401` | Missing, malformed, revoked, expired or not-entitled API key. |
| `403` | The key's scope or its owner's role does not allow this operation. |
| `429` | Hourly quota or an abuse limit exceeded; the body's code says which. Honour Retry-After. |
| `500` | Unexpected server error; retry with backoff. |
| `503` | A transient failure resolving the organization or reading storage; retry after a short pause. |

Plus the errors every request can return: https://dmarcguard.io/docs/api/v1/#errors

## Domain health

`GET /domains/{domain}/health`

Score, grade, DNS record status, compliance, top senders, open action items, a trend sparkline and the latest APRF placement data of each reporting mailbox provider for one domain. The figures may be up to an hour old.

- Permission: `domain:read`
- Key scope: `read`, `full`
- Budget: heavy route (https://dmarcguard.io/docs/rate-limits/#heavy-routes)

### Path parameters

| Name | Type | Description |
| --- | --- | --- |
| `domain` (required) | `string` | A domain monitored by the organization. |

### Example request

```bash
curl "https://api.dmarcguard.io/api/v1/domains/example.com/health" \
  -H "Authorization: Bearer dg_your_key"
```

### Response

| Field | Type | Notes |
| --- | --- | --- |
| `domain` | `string` | The domain this report is about. |
| `overall_score` | `integer` | Composite health score for the domain, 0 to 100. |
| `grade` | `string` | Letter grade for overall_score: A+, A, A-, B+, B, B-, C+, C, C-, D or F, or N/A before the first report has been received. |
| `verdict` | `string` | A ready-to-show sentence summarising the domain's posture. Display it; its wording is not a value to branch on. |
| `last_report_date` | `string` (nullable) | Calendar day, YYYY-MM-DD (UTC); null when absent. |
| `total_messages` | `integer` | Messages reported for this domain in the scoring window. |
| `dns` | `object` | What a live DNS lookup found for each email-authentication record. |
| `dns.dmarc` | `object` | The domain's DMARC record. |
| `dns.dmarc.status` | `string` | How the record stands: ok, warning when it exists but is not doing its job, error when it is missing or malformed, and unknown when nothing could be determined. |
| `dns.dmarc.details` | `string` | A ready-to-show sentence explaining the status. |
| `dns.dmarc.inherited_from` | `string` (optional) | The parent domain the record was found on, when this domain has none of its own. |
| `dns.dmarc.hosted_cta` | `object` (nullable, optional) | Link to host this protocol on DMARCguard, present when the record is missing or failing, or a hosted setup is still in progress. |
| `dns.dmarc.hosted_cta.label` | `string` | Short label for the link, ready to show. |
| `dns.dmarc.hosted_cta.url` | `string` | SPA path of the Hosted DNS page with this domain selected. |
| `dns.spf` | `object` | The domain's SPF record. |
| `dns.spf.status` | `string` | How the record stands: ok, warning when it exists but is not doing its job, error when it is missing or malformed, and unknown when nothing could be determined. |
| `dns.spf.details` | `string` | A ready-to-show sentence explaining the status. |
| `dns.spf.inherited_from` | `string` (optional) | The parent domain the record was found on, when this domain has none of its own. |
| `dns.spf.hosted_cta` | `object` (nullable, optional) | Link to host this protocol on DMARCguard, present when the record is missing or failing, or a hosted setup is still in progress. |
| `dns.spf.hosted_cta.label` | `string` | Short label for the link, ready to show. |
| `dns.spf.hosted_cta.url` | `string` | SPA path of the Hosted DNS page with this domain selected. |
| `dns.dkim` | `object` | The domain's DKIM signing keys. |
| `dns.dkim.status` | `string` | How the record stands: ok, warning when it exists but is not doing its job, error when it is missing or malformed, and unknown when nothing could be determined. |
| `dns.dkim.details` | `string` | A ready-to-show sentence explaining the status. |
| `dns.dkim.inherited_from` | `string` (optional) | The parent domain the record was found on, when this domain has none of its own. |
| `dns.dkim.hosted_cta` | `object` (nullable, optional) | Link to host this protocol on DMARCguard, present when the record is missing or failing, or a hosted setup is still in progress. |
| `dns.dkim.hosted_cta.label` | `string` | Short label for the link, ready to show. |
| `dns.dkim.hosted_cta.url` | `string` | SPA path of the Hosted DNS page with this domain selected. |
| `dns.mta_sts` | `object` | The domain's MTA-STS policy. |
| `dns.mta_sts.status` | `string` | How the record stands: ok, warning when it exists but is not doing its job, error when it is missing or malformed, and unknown when nothing could be determined. |
| `dns.mta_sts.details` | `string` | A ready-to-show sentence explaining the status. |
| `dns.mta_sts.inherited_from` | `string` (optional) | The parent domain the record was found on, when this domain has none of its own. |
| `dns.mta_sts.hosted_cta` | `object` (nullable, optional) | Link to host this protocol on DMARCguard, present when the record is missing or failing, or a hosted setup is still in progress. |
| `dns.mta_sts.hosted_cta.label` | `string` | Short label for the link, ready to show. |
| `dns.mta_sts.hosted_cta.url` | `string` | SPA path of the Hosted DNS page with this domain selected. |
| `dns.tls_rpt` | `object` | The domain's TLS reporting record. |
| `dns.tls_rpt.status` | `string` | How the record stands: ok, warning when it exists but is not doing its job, error when it is missing or malformed, and unknown when nothing could be determined. |
| `dns.tls_rpt.details` | `string` | A ready-to-show sentence explaining the status. |
| `dns.tls_rpt.inherited_from` | `string` (optional) | The parent domain the record was found on, when this domain has none of its own. |
| `dns.tls_rpt.hosted_cta` | `object` (nullable, optional) | Link to host this protocol on DMARCguard, present when the record is missing or failing, or a hosted setup is still in progress. |
| `dns.tls_rpt.hosted_cta.label` | `string` | Short label for the link, ready to show. |
| `dns.tls_rpt.hosted_cta.url` | `string` | SPA path of the Hosted DNS page with this domain selected. |
| `dns.bimi` | `object` | The domain's BIMI record. |
| `dns.bimi.status` | `string` | How the record stands: ok, warning when it exists but is not doing its job, error when it is missing or malformed, and unknown when nothing could be determined. |
| `dns.bimi.details` | `string` | A ready-to-show sentence explaining the status. |
| `dns.bimi.inherited_from` | `string` (optional) | The parent domain the record was found on, when this domain has none of its own. |
| `dns.bimi.hosted_cta` | `object` (nullable, optional) | Link to host this protocol on DMARCguard, present when the record is missing or failing, or a hosted setup is still in progress. |
| `dns.bimi.hosted_cta.label` | `string` | Short label for the link, ready to show. |
| `dns.bimi.hosted_cta.url` | `string` | SPA path of the Hosted DNS page with this domain selected. |
| `dns.dane` | `object` | DANE (TLSA) records for the domain's mail servers. |
| `dns.dane.status` | `string` | How the record stands: ok, warning when it exists but is not doing its job, error when it is missing or malformed, and unknown when nothing could be determined. |
| `dns.dane.details` | `string` | A ready-to-show sentence explaining the status. |
| `dns.dane.inherited_from` | `string` (optional) | The parent domain the record was found on, when this domain has none of its own. |
| `dns.dane.hosted_cta` | `object` (nullable, optional) | Link to host this protocol on DMARCguard, present when the record is missing or failing, or a hosted setup is still in progress. |
| `dns.dane.hosted_cta.label` | `string` | Short label for the link, ready to show. |
| `dns.dane.hosted_cta.url` | `string` | SPA path of the Hosted DNS page with this domain selected. |
| `dns.aprf` | `object` | The domain's APRF performance-reporting record. |
| `dns.aprf.status` | `string` | How the record stands: ok when exactly one APRF record is published, warning when more than one is (providers ignore all of them), and unknown when none was found or it could not be read. Never error: APRF is opt-in. |
| `dns.aprf.details` | `string` | A ready-to-show sentence explaining the status. |
| `compliance` | `object` | How this domain's mail authenticates. |
| `compliance.rate` | `number` | Percentage from 0 to 100 of this domain's messages that passed DMARC. |
| `compliance.spf_pass` | `number` | Percentage from 0 to 100 of this domain's messages with an aligned SPF pass. |
| `compliance.dkim_pass` | `number` | Percentage from 0 to 100 of this domain's messages with an aligned DKIM pass. |
| `compliance.policy` | `string` | The DMARC policy in force for the domain: none, quarantine or reject. |
| `top_senders` | `object[]` | The highest-volume senders for the domain, busiest first. |
| `top_senders[].ip` | `string` | The sending IP. |
| `top_senders[].count` | `integer` | Messages it sent as this domain. |
| `top_senders[].spf_result` | `string` | Whether SPF produced an aligned pass for its mail: pass or fail. |
| `top_senders[].dkim_result` | `string` | Whether DKIM produced an aligned pass for its mail: pass or fail. |
| `top_senders[].service_name` | `string` (optional) | The sending service the IP was identified as, when it is recognised. |
| `top_senders[].service_icon` | `string` (optional) | Icon slug for the identified service. |
| `top_senders[].service_slug` | `string` (optional) | Stable identifier of the identified service. |
| `alert_count` | `integer` | Open attention items for this domain. |
| `action_items` | `object[]` (nullable) | What to do next for this domain, most urgent first. It is a to-do list, not an inventory, so it is capped at a handful. |
| `action_items[].type` | `string` (optional) | What kind of problem this is. A gap the live DNS check found carries a dns_ prefix (dns_dmarc, dns_spf, dns_dkim, dns_mta_sts, dns_bimi); anything DMARCguard flagged carries the same type the events feed uses. |
| `action_items[].severity` | `string` | How urgent the item is: high, medium or low. |
| `action_items[].title` | `string` | One-line statement of the problem. |
| `action_items[].suggested_action` | `string` | What to do about it, ready to show. |
| `action_items[].cta_url` | `string` (optional) | Link to the screen where the item is resolved. |
| `action_items[].cta_label` | `string` (optional) | Label for that link. |
| `trend_sparkline` | `object[]` | One point per day for the trend line, oldest first. |
| `trend_sparkline[].date` | `string` (nullable) | Calendar day, YYYY-MM-DD (UTC); null when absent. |
| `trend_sparkline[].compliance_rate` | `number` | Percentage from 0 to 100 of the day's messages that passed DMARC. |
| `trend_sparkline[].total_messages` | `integer` | Messages reported for the day. |
| `aprf_placement` | `object[]` | APRF placement data mailbox providers sent for this domain: for each provider, DKIM domain and DKIM selector, the rows of its most recent report within the last 7 UTC days (today included), one row per segment, or an empty list when none. Rows are independent measurements: a *.domain roll-up row and a * selector row cover the same mail as the exact rows, and different providers count different mail, so never add rows together. Counts are as reported: providers may round them up to buckets, so read each as an upper bound. |
| `aprf_placement[].day` | `string` (nullable) | The UTC day the report covers, YYYY-MM-DD. |
| `aprf_placement[].source` | `string` | The reporting mailbox provider, by the name it reports under. |
| `aprf_placement[].dkim_domain` | `string` | The DKIM signing domain (d=) the report covers; a leading *. means the provider rolled its subdomains up into one report. |
| `aprf_placement[].dkim_selector` | `string` | The DKIM selector (s=) the report covers; * when the provider rolled every selector up into one report. |
| `aprf_placement[].version` | `string` | The report version the provider declared. A change means the provider's categories may count different actions than before, so do not compare counts across versions. |
| `aprf_placement[].sdi_used` | `string` | Whether the provider applied your signer-defined identifier (SDI) segmentation, as it reported it: N/A when it did not use the one you declared, N/F when it found no valid one; empty when not reported. |
| `aprf_placement[].extra_info` | `string` | The provider's own notes on its report, where it explains what its categories count; empty when it sent none. |
| `aprf_placement[].segment` | `string[]` (nullable) | The signer-defined identifier values this row counts, in order; an empty list when the report is not segmented. |
| `aprf_placement[].classification` | `object[]` (nullable) | Message counts by placement (for example inbox or unwanted), as reported: a provider may round each up to a bucket, so read it as an upper bound. |
| `aprf_placement[].classification[].category` | `string` | The category, exactly as the provider named it. |
| `aprf_placement[].classification[].value` | `integer` | The count as reported; a provider may round it up to a bucket, so read it as an upper bound. |
| `aprf_placement[].engagement` | `object[]` (nullable) | Engagement-action counts (for example positive, neutral or negative), as reported: a provider may round each up to a bucket, so read it as an upper bound. |
| `aprf_placement[].engagement[].category` | `string` | The category, exactly as the provider named it. |
| `aprf_placement[].engagement[].value` | `integer` | The count as reported; a provider may round it up to a bucket, so read it as an upper bound. |
| `empty_state` | `object` (nullable, optional) | What an empty action_items list means. Present only when the list is empty. |
| `empty_state.healthy` | `boolean` | True only when there is genuinely nothing wrong. False means the list is empty for some other reason, which the message gives. |
| `empty_state.message` | `string` | A ready-to-show sentence explaining why there is nothing to do. |
| `empty_state.cta_url` | `string` (optional) | Where to send the reader next, when there is somewhere useful. |
| `empty_state.cta_label` | `string` (optional) | Label for that link. |
| `computed_at` | `string` (nullable) | RFC 3339 UTC instant with whole seconds, e.g. 2026-09-15T16:03:43Z; null when absent. |

### Errors

| Status | When |
| --- | --- |
| `400` | The path segment is not a valid domain name. |
| `401` | Missing, malformed, revoked, expired or not-entitled API key. |
| `403` | The key's scope or its owner's role does not allow this operation. |
| `404` | The domain is not monitored by this organization. |
| `429` | Hourly quota or an abuse limit exceeded; the body's code says which. Honour Retry-After. |
| `500` | Unexpected server error; retry with backoff. |
| `503` | A transient failure resolving the organization or reading storage; retry after a short pause. |

Plus the errors every request can return: https://dmarcguard.io/docs/api/v1/#errors