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.1

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.1
File Size Uploaded
dmarc_report-3.0.1.tar.gz 40.8 kB Details

Built distribution (wheel)

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

Total release size: 64.4 kB

Release files / dmarc_report-3.0.1.tar.gz

Download URL dmarc_report-3.0.1.tar.gz
Size 40.8 kB
Tags Source
SHA-256 checksum
How to use checksums
100f389f9ed1027e8e88ce5be17e259545c9a4515e17fff7e905becbc2983b5e
BLAKE2b-256 checksum
How to use checksums
9a3f540d7b7c0ced91dc279d76af441967ba3852ecdb4e7602014c487ddf2912
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via pdm/2.28.2 CPython/3.14.7 Linux/6.17.0-1022-azure

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

Download URL dmarc_report-3.0.1-py3-none-any.whl
Size 23.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
818ff289d54a5818396678eca2877ed15c77720c6af9dd49623c4e5756bf4fed
BLAKE2b-256 checksum
How to use checksums
23e9da23392718641ab6a22959186cac2108377493e08fe29d47b78311e4c4a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via pdm/2.28.2 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

3.0.2

2 release files

This release

3.0.1 This release

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