Reports API
Access DMARC aggregate report data programmatically: individual reports with their per-source records, rolled-up statistics, file upload, and CSV export.
Retention shapes every response
days parameter you send is silently capped to it. Monitor keeps 30 days and is additionally limited to the 10 most recent reports with no pagination; the paid plans keep 1 year on Protect, Growth, Professional and the Partner plans.Endpoints
/api/v1/reportsList DMARC aggregate reports/api/v1/reports/{report_id}Get report details/api/v1/reports/stats/summaryGet aggregated report statistics/api/v1/reports/uploadUpload a DMARC report file/api/v1/reports/export/csvExport report records as CSVList DMARC reports
Returns reports for your organization, most recently ingested first, each with its full records array.
curl -X GET "https://api.ddmarc.com/api/v1/reports?domain_id=42&limit=50&offset=0" \ -H "X-API-Key: $DDMARC_API_KEY"
| Parameter | Type | Default | Description |
|---|---|---|---|
domain_id | integer | all | Restrict to one domain. |
limit | integer | 50 | Rows to return. Hard ceiling of 200. |
offset | integer | 0 | Rows to skip. |
[
{
"id": 9012,
"domain_id": 42,
"report_id": "16058034545453596399",
"org_name": "google.com",
"date_range_begin": "2026-07-17T00:00:00Z",
"date_range_end": "2026-07-17T23:59:59Z",
"policy_domain": "example.com",
"policy_adkim": "r",
"policy_aspf": "r",
"policy_p": "reject",
"policy_sp": null,
"policy_pct": 100,
"created_at": "2026-07-18T09:04:11Z",
"records": [
{
"id": 551201,
"source_ip": "209.85.220.41",
"count": 843,
"disposition": "none",
"dkim_result": "pass",
"spf_result": "pass",
"header_from": "example.com",
"envelope_from": "example.com",
"envelope_to": null
}
]
}
]Get a single report
Returns one report with all of its records, in the same shape as an element of the list response.
{report_id} here is the integer id field — not the reporter's own report_id string.Summary statistics
Rolls up volume and pass rates over a time window, without paging through every report.
| Parameter | Type | Default | Description |
|---|---|---|---|
domain_id | integer | all | Restrict to one domain. |
days | integer | 30 | Look-back window, 1 to 365. Capped to your plan's retention. |
{
"total_reports": 184,
"total_emails": 412903,
"pass_rate": 98.4,
"dkim_pass_rate": 99.1,
"spf_pass_rate": 97.2,
"top_sources": [
{ "source_ip": "209.85.220.41", "total": 180422 }
]
}Rates are percentages rounded to one decimal place. A message counts as passing only when both DKIM and SPF pass.
Upload a report file
Ingests a DMARC aggregate report you already hold as a file. Accepts raw XML, ZIP or GZIP as a multipart upload, up to 10 MB. Requires a key with write scope, and is limited to 30 uploads per minute.
curl -X POST "https://api.ddmarc.com/api/v1/reports/upload" \ -H "X-API-Key: $DDMARC_API_KEY" \ -F "file=@google.com!example.com!1752710400!1752796799.xml.gz"
{
"message": "1 report(s) uploaded successfully",
"uploaded": [
{
"report_id": "16058034545453596399",
"domain": "example.com",
"org_name": "google.com",
"records_count": 12,
"total_emails": 1843
}
],
"duplicates_skipped": 0,
"no_domain_skipped": 0
}409 when every report in the file was already ingested, and 400 when the report's domain is not in your organization — add the domain first.CSV export
Export is a dedicated endpoint, not a format=csv parameter on other routes. It streams one row per DMARC record with the columns Date, Domain, Reporter, Source IP, Country, Organization, Count, Disposition, DKIM Result, SPF Result, Header From and Envelope From.
curl -X GET "https://api.ddmarc.com/api/v1/reports/export/csv?domain_id=42&days=30" \ -H "X-API-Key: $DDMARC_API_KEY" \ -o dmarc-records.csv
days or domain_id if you are near that. Senders and the activity log have their own exports at /api/v1/senders/export/csv and /api/v1/activity/export/csv.The report object
| Field | Type | Description |
|---|---|---|
id | integer | Report identifier, used as {report_id} in paths |
domain_id | integer | Domain this report belongs to |
report_id | string | The reporter's own report id, from the XML |
org_name | string | Reporting organization, e.g. google.com |
date_range_begin | string | ISO 8601 start of the reporting window |
date_range_end | string | ISO 8601 end of the reporting window |
policy_domain | string | Domain the published policy was found on |
policy_p | string | null | Published policy: none, quarantine or reject |
policy_sp | string | null | Subdomain policy, if published |
policy_adkim | string | null | DKIM alignment mode: r (relaxed) or s (strict) |
policy_aspf | string | null | SPF alignment mode: r (relaxed) or s (strict) |
policy_pct | integer | null | Percentage of mail the policy applied to |
created_at | string | ISO 8601 ingestion timestamp |
records | array | Per-source results; see the record object below |
records[].count yourself, or use the summary endpoint.The record object
| Field | Type | Description |
|---|---|---|
id | integer | Record identifier |
source_ip | string | Sending IP address |
count | integer | Messages this row represents |
disposition | string | null | What the receiver did: none, quarantine or reject |
dkim_result | string | null | DKIM evaluation: pass, fail, none, and so on |
spf_result | string | null | SPF evaluation: pass, fail, none, and so on |
header_from | string | null | Domain in the From header |
envelope_from | string | null | MAIL FROM domain, when the reporter supplies it |
envelope_to | string | null | RCPT TO domain, when the reporter supplies it |
Analyzing failures
Raw reports tell you what happened; the failure-analysis endpoints tell you why. They live under their own prefix and each accept days (1–365, default 30) and an optional domain_id.
/api/v1/failure-analysis/summaryGet failure totals and split by mechanism/api/v1/failure-analysis/root-causesGet classified root causes with fix suggestions/api/v1/failure-analysis/root-causes/by-sourceGet root causes grouped by sending source/api/v1/failure-analysis/sourcesList failing sources/api/v1/failure-analysis/spoof-clustersGet suspected spoofing campaign clusters/api/v1/failure-analysis/by-header-fromGet failures grouped by spoofed From identity/api/v1/failure-analysis/trendGet failure trend over time/api/v1/failure-analysis/resolution-statsGet time-to-resolution statistics{
"total_failures": 6604,
"total_emails": 412903,
"failure_rate": 1.6,
"period_days": 30,
"by_reason": [
{
"reason": "spf_only",
"display_name": "SPF Only Failed",
"count": 4102,
"percentage": 62.1,
"description": "SPF authentication failed but DKIM passed. Check SPF records and authorized senders."
}
]
}Looking for charts and breakdowns?
Trend lines, geographic distribution, volume heatmaps, top sources, protocol breakdown and the compliance scorecard are served from /api/v1/dashboard/*, not from the reports endpoints. Per-source detail lives under /api/v1/senders. Both are listed on the API overview.