Short answer: Use Get-MessageTraceV2 and Get-MessageTraceDetailV2 in Exchange Online PowerShell. They replaced Get-MessageTrace and Get-MessageTraceDetail, which Microsoft began deprecating on September 1, 2025. A query can search the last 90 days but only 10 days per query, returns 1,000 rows by default and 5,000 at most (-ResultSize), has no paging (continue with -StartingRecipientAddress and -EndDate), and is throttled at 100 queries per 5 minutes. Example: Get-MessageTraceV2 -SenderAddress bob@contoso.com -StartDate (Get-Date).AddDays(-2) -EndDate (Get-Date) | Export-Csv trace.csv -NoTypeInformation.
Commands checked against the official documentation (linked below) on 6 October 2026; not yet run on our lab servers.
Table of Contents
What changed: V2 cmdlets and the old ones
| Item | Status (per Microsoft) |
|---|---|
Get-MessageTraceV2, Get-MessageTraceDetailV2 | Generally available since June 2025; the supported cmdlets |
Get-MessageTrace, Get-MessageTraceDetail | Deprecation began September 1, 2025 for worldwide tenants; the reference page says they are replaced by the V2 cmdlets |
| Message trace via Reporting Webservice | Deprecation scheduled for April 8, 2026 |
| Message trace via Microsoft Graph | Generally available (Exchange team update of January 22, 2026) |
If old scripts call Get-MessageTrace with -Page and -PageSize, they need rewriting: V2 has no paging and -ResultSize replaces -PageSize.
Limits that shape every V2 query:
- Data for the last 90 days; each query covers at most 10 days. With no date parameters you get the last 48 hours.
- Default 1,000 results, maximum 5,000 per query.
- 100 queries per 5 minutes per tenant, for each of the two cmdlets.
- Timestamps in the output are UTC, even if you passed local dates.
- Delivery status can lag the real state by five to ten minutes.
Connect and permissions
Message trace needs membership in the Organization Management role group in Exchange Online, or the Exchange Administrator role in Microsoft Entra (Global Administrator also works but is far more privilege than needed). Install the Exchange Online module once, then connect:
Install-Module ExchangeOnlineManagement -Scope CurrentUser
Connect-ExchangeOnline -UserPrincipalName admin@contoso.com
Date parameters use the short date format of the machine running the command. To avoid MM/dd versus dd/MM confusion, pass [datetime] objects such as (Get-Date).AddDays(-2) instead of strings.
Common message trace queries
Everything a user sent in the last two days:
Get-MessageTraceV2 -SenderAddress bob@contoso.com -StartDate (Get-Date).AddDays(-2) -EndDate (Get-Date) |
Select-Object Received, SenderAddress, RecipientAddress, Subject, Status |
Sort-Object Received
Did an external sender’s message reach a mailbox?
Get-MessageTraceV2 -SenderAddress invoices@example.com -RecipientAddress bob@contoso.com `
-StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date)
Only failures, spam and quarantined mail to one recipient (Status accepts several values):
Get-MessageTraceV2 -RecipientAddress bob@contoso.com -Status Failed, FilteredAsSpam, Quarantined `
-StartDate (Get-Date).AddDays(-3) -EndDate (Get-Date)
Valid -Status values are Delivered, Expanded, Failed, FilteredAsSpam, GettingStatus, Pending and Quarantined.
Search by subject (Contains, StartsWith or EndsWith):
Get-MessageTraceV2 -Subject "Purchase order" -SubjectFilterType Contains `
-StartDate (Get-Date).AddDays(-1) -EndDate (Get-Date) -ResultSize 5000
Find one message by its Message-ID header (include the angle brackets if the header has them, and quote the value):
Get-MessageTraceV2 -MessageId "<d9683b4c-127b-413a-ae2e-fa7dfb32c69d@example.com>" -StartDate (Get-Date).AddDays(-10) -EndDate (Get-Date)
Mail from a specific sending server IP, useful when investigating a compromised account or a misconfigured relay:
Get-MessageTraceV2 -FromIP 203.0.113.10 -StartDate (Get-Date).AddDays(-1) -EndDate (Get-Date)
See what happened to a message: Get-MessageTraceDetailV2
The summary row tells you the final status. The detail shows each event: receive, transport rule, spam filter verdict, deferral, delivery or failure reason. Pipe the summary into the detail cmdlet:
Get-MessageTraceV2 -SenderAddress invoices@example.com -RecipientAddress bob@contoso.com `
-StartDate (Get-Date).AddDays(-2) -EndDate (Get-Date) |
Get-MessageTraceDetailV2 |
Select-Object Date, Event, Action, Detail | Format-List
Or query one message directly with its MessageTraceId (Microsoft’s example):
Get-MessageTraceDetailV2 -MessageTraceId ae5c1219-4c90-41bf-fef5-08d837917e7c -RecipientAddress robert@contoso.com
Microsoft recommends using -MessageTraceId where possible; it is required for messages sent to more than 1,000 recipients.
Export to CSV, including more than 5,000 rows
A single query is capped at 5,000 rows. Because V2 has no paging, Microsoft’s method is to run the next query with -StartingRecipientAddress and -EndDate taken from the last row of the previous result. This loop does that until a query returns fewer rows than requested, then exports everything:
$start = (Get-Date).AddDays(-2)
$end = Get-Date
$size = 5000
$all = [System.Collections.Generic.List[object]]::new()
$params = @{ SenderAddress = 'bob@contoso.com'; StartDate = $start; EndDate = $end; ResultSize = $size }
do {
$batch = @(Get-MessageTraceV2 @params)
$all.AddRange($batch)
if ($batch.Count -eq $size) {
$last = $batch[-1]
$params['EndDate'] = $last.Received
$params['StartingRecipientAddress'] = $last.RecipientAddress
Start-Sleep -Seconds 3 # stay well under 100 queries per 5 minutes
}
} while ($batch.Count -eq $size)
$all | Sort-Object MessageTraceId, RecipientAddress -Unique |
Select-Object Received, SenderAddress, RecipientAddress, Subject, Status, FromIP, ToIP, Size, MessageId, MessageTraceId |
Export-Csv .\message-trace.csv -NoTypeInformation -Encoding UTF8
The Sort-Object -Unique step removes the boundary row that can appear in two consecutive batches. For ranges longer than 10 days, loop over 10-day windows and run the same code for each window.
Older than 10 days at a time, or bigger reports
For an investigation across a longer period, or when you need extra columns such as direction and original client IP, use an asynchronous historical search. Results arrive as a downloadable CSV, often after several hours:
Start-HistoricalSearch -ReportTitle "Bob outbound Sept" -ReportType MessageTrace `
-SenderAddress bob@contoso.com -StartDate 09/01/2026 -EndDate 09/30/2026 `
-NotifyAddress admin@contoso.com
Get-HistoricalSearch | Format-List
Start-HistoricalSearch needs at least one of -MessageID, -RecipientAddress or -SenderAddress. Use -ReportType MessageTraceDetail for the event-level version. The Exchange admin center’s message trace page offers the same Enhanced summary and Extended reports.
Check that it worked and common problems
- No results but the user insists mail was sent. Check the date range is UTC-aware and within 90 days, the address is the primary SMTP address, and that 5-10 minutes have passed.
- “The term ‘Get-MessageTraceV2’ is not recognized”. Update the ExchangeOnlineManagement module and reconnect; the cmdlet only exists in Exchange Online PowerShell.
- Throttling errors in scripts. More than 100 calls in 5 minutes. Narrow the filters and add a pause between calls.
- Exactly 1,000 rows. You hit the default result size; add
-ResultSize 5000or use the loop above. - Status Pending or GettingStatus. Delivery is still in progress or retrying. Look at
Get-MessageTraceDetailV2for deferral reasons, and check the recipient domain’s MX with our MX lookup. - FilteredAsSpam on legitimate mail. Check the sender’s SPF, DKIM and DMARC; see Microsoft 365 SPF, DKIM and DMARC.
Official documentation: Get-MessageTraceV2 · Get-MessageTraceDetailV2 · Message trace in the Exchange admin center · Exchange team: GA of the new message trace
Related: Microsoft 365 SPF DKIM DMARC: Secure Exchange Online Setup · Email Header Analyzer · Microsoft 365 shared mailbox vs distribution group vs Microsoft 365 Group · Employee Offboarding Checklist: Secure AD, M365 and Workspace Steps · MX Lookup
See also: Exchange Online Message Trace to CSV: Get-MessageTraceV2 Script · Exchange Online Mailbox Permissions Report: FullAccess, SendAs · Exchange Server SE Upgrade from 2019 CU15: In-Place Steps
Frequently asked questions
Is Get-MessageTrace still supported?
No. Microsoft began deprecating Get-MessageTrace and Get-MessageTraceDetail on September 1, 2025. Use Get-MessageTraceV2 and Get-MessageTraceDetailV2.
How far back can message trace go in PowerShell?
90 days, but each Get-MessageTraceV2 query can cover at most 10 days. Use several 10-day queries or Start-HistoricalSearch for longer periods.
How do I get more than 5,000 results?
Run the next query with -StartingRecipientAddress and -EndDate set from the last row of the previous result, as in the loop in this guide.
Why are the times in my CSV wrong?
Message trace output is in UTC. Convert with ToLocalTime() if you need local time.
What permissions do I need for message trace?
Organization Management in Exchange Online or the Exchange Administrator role in Microsoft Entra. Avoid Global Administrator for routine tracing.