Skip to main content

DMARC Report

Parse legacy and RFC 9990 DMARC aggregate reports into typed Python objects, or display a report in your terminal.

The parser accepts plain XML and gzip-compressed XML, along with ZIP archives. The type of file passed is detected from magic bytes and XML structure.

Installation

This works best when installed with pipx or uv tool.

# With pipx:
pipx install dmarc-report

# Or with uv:
uv tool install dmarc-report

You can also run the tool without installing it:

# With uvx:
uvx dmarc-report long-dmarc-report-filename.xml

Usage

Run the dmarc-report command-line utility followed by a DMARC report file. The DMARC report will probably have one of the following file extensions:

  • .xml.gz
  • .zip
  • .xml
dmarc-report long-dmarc-report-filename.xml.gz
# or
dmarc-report long-dmarc-report-filename.xml
# or
dmarc-report long-dmarc-report-filename.zip

You should see a nicely formatted report in your terminal:

╭──────────────────────────────── DMARC Report for example.com ────────────────────────────────╮
│                              DMARC Report Metadata                                           │
│ ╭───────────────────────┬──────────────────────────────────────────────────────╮             │
│ │ Format                │ Legacy                                               │             │
│ │ Namespace             │ not reported                                         │             │
│ │ Version               │ 1.0                                                  │             │
│ │ Organization          │ Example Reporting Org                                │             │
│ │ Contact               │ dmarcreport@reporting.com                            │             │
│ │ Report ID             │ 1234567890                                           │             │
│ │ Date range            │ 2026-08-26 00:00:00 UTC to 2026-08-27 00:00:00 UTC   │             │
│ ╰───────────────────────┴──────────────────────────────────────────────────────╯             │
│                               DMARC Policy Details                                           │
│ ╭───────────────────────┬──────────────────────────────────────────────────────╮             │
│ │ Domain                │ example.com                                          │             │
│ │ Policy (p)            │ quarantine                                           │             │
│ │ Subdomain (sp)        │ quarantine                                           │             │
│ │ Sampling (pct)        │ 100%                                                 │             │
│ │ DKIM alignment        │ r                                                    │             │
│ │ SPF alignment         │ r                                                    │             │
│ │ Failure options (fo)  │ 0                                                    │             │
│ ╰───────────────────────┴──────────────────────────────────────────────────────╯             │
│                                     Summary                                                  │
│ ╭───────────────────────┬──────────────────────────────────────────────────────╮             │
│ │ Total messages        │ 2                                                    │             │
│ │ Unique sources        │ 2                                                    │             │
│ │ DMARC pass rate       │ 100.0%                                               │             │
│ │ DKIM aligned          │ 100.0%                                               │             │
│ │ SPF aligned           │ 100.0%                                               │             │
│ ╰───────────────────────┴──────────────────────────────────────────────────────╯             │
│                                           Message Records                                    │
│ ╭───────────────┬───────┬─────────────┬──────┬──────┬──────────────────────────────────────╮ │
│ │ Source IP     │ Count │ Disposition │ DKIM │ SPF  │ Authentication results and overrides │ │
│ ├───────────────┼───────┼─────────────┼──────┼──────┼──────────────────────────────────────┤ │
│ │ 192.100.20.21 │     1 │ none        │ pass │ pass │ DKIM pass example.com selector=fm1   │ │
│ │               │       │             │      │      │ SPF pass example.com scope=mfrom     │ │
│ │ 192.100.22.23 │     1 │ none        │ pass │ pass │ DKIM pass example.com selector=fm1   │ │
│ │               │       │             │      │      │ SPF pass example.com scope=mfrom     │ │
│ ╰───────────────┴───────┴─────────────┴──────┴──────┴──────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────╯

Use --verbose (or -v) when running the CLI to include parser warnings in the output:

dmarc-report --verbose long-dmarc-report-filename.xml

This will add something like this to the end of the report:

│                                       Parser Warnings                                        │
│ ╭──────────────────────────────┬───────────────────────────────────────────────────────────╮ │
│ │ Code                         │ Details                                                   │ │
│ ├──────────────────────────────┼───────────────────────────────────────────────────────────┤ │
│ │ legacy_no_namespace          │ A namespace-free report was accepted as legacy DMARC XML. │ │
│ ╰──────────────────────────────┴───────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────╯

Display the installed package version with dmarc-report --version or dmarc-report -V.

Python API

You can integrate this package into your own Python projects:

from dmarc_report import parser

# If reading bytes:
report = parser.DMARCParser.parse_bytes(attachment_bytes)
# If reading a file:
report = parser.DMARCParser.parse_file("aggregate-report.xml.gz")

print(report.format, report.namespace, report.version)
print(report.policy_published.domain)

The parser creates dataclasses for each report section, with helper methods and properties to access fields. For example, to extract report information about the subdomain policy or the sampling percentage:

policy = report.policy_published
print(policy.sp)  # None if it was absent
print(policy.effective_sp)  # inherits value from the <p> field
print(policy.pct)  # None in RFC 9990 and when omitted from legacy XML
print(policy.effective_pct)  # legacy default 100; None for RFC 9990

Full documentation may be added in the future, but for now the source code is the key resource. The following files are where the bulk of the API logic lives:

  • parser.py this is the main entry point to access the DMARCParser class and the parser limits.
  • schema.py this module defines the data structures used by the parser, including the main Report class.

Resource limits and malformed input

There are a number of configurable limits to protect against bad attachments or rogue reports. If needed, you can customise the limits with the ParserLimits class:

from dmarc_report import parser

limits = parser.ParserLimits(
    max_input_bytes=5 * 1024 * 1024,
    max_decompressed_bytes=25 * 1024 * 1024,
    max_records=25_000,
)
report = parser.DMARCParser.parse_bytes(attachment_bytes, limits=limits)

Default limits are 10 MiB input, 100 MiB decompressed XML, 10 zip members, 100,000 records, 100 DKIM results per record, 10 SPF results per legacy record, and 64 KiB per parsed text field. RFC 9990 itself allows at most one SPF result per record. These configurable ceilings protect parser resources; they are separate from the report format's validity rules.

All custom exceptions derive from DMARCParseError and expose a machine-readable code:

from dmarc_report import exceptions, parser

try:
    report = parser.DMARCParser.parse_bytes(attachment_bytes)
except exceptions.DMARCParseError as error:
    print(error.code.value, str(error))

Filesystem errors from parse_file, invalid ParserLimits configuration, and unexpected package defects are not misreported as malformed attachments.

Issues

This package has been tested as much as possible, but email providers often have quirks in their DMARC reports which are difficult to catch without actually seeing the reports. Please log issues here if you encounter any broken reports or if you notice any weird/unusual output. Please include as much info as possible, and ideally include the actual DMARC report if possible. Or you can forward me your DMARC reports to: dmarc-reports@amanzi.nz

Release files for dmarc-report 3.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dmarc-report 3.0.2
File Size Uploaded
dmarc_report-3.0.2.tar.gz 41.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for dmarc-report 3.0.2
File Interpreter ABI Platform
dmarc_report-3.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 64.8 kB

Release files / dmarc_report-3.0.2.tar.gz

Download URL dmarc_report-3.0.2.tar.gz
Size 41.1 kB
Tags Source
SHA-256 checksum
How to use checksums
63bebec4e5fb3da25cea414257cdd365f1dc9158e855e9ca20e57d3b269fa1a9
BLAKE2b-256 checksum
How to use checksums
193f7c4dbed3fa5b86d2432b94f036de1d19ad6dcd89a3afb609ea7ba1dfcf98
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via pdm/2.29.0 CPython/3.14.7 Linux/6.17.0-1022-azure

Release files / dmarc_report-3.0.2-py3-none-any.whl

Download URL dmarc_report-3.0.2-py3-none-any.whl
Size 23.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f74c7f73db926d3dabca58fa9b0608ee1eafc1b8371cb5d2c5e85cc23e6a69d1
BLAKE2b-256 checksum
How to use checksums
b47440e3d628f196eebd7a48a0c854e9dd0409cdac754a11879202aebafc95d1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via pdm/2.29.0 CPython/3.14.7 Linux/6.17.0-1022-azure

Release history Release notifications | RSS feed

3.0.4

2 release files

3.0.3

2 release files

This release

3.0.2 This release

2 release files

3.0.1

2 release files

3.0.0

2 release files

2.3.0

2 release files

2.2.2

2 release files

2.2.1

2 release files

1.1.0

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page