# Exchange Online Message Trace PowerShell: Get-MessageTraceV2

Source: https://srvscripts.com/guides/exchange-online-message-trace-powershell/
Updated: 2026-10-06
Publisher: srvScripts (https://srvscripts.com/)

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

## 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 "" -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 5000` or use the loop above.

- **Status Pending or GettingStatus.** Delivery is still in progress or retrying. Look at `Get-MessageTraceDetailV2` for deferral reasons, and check the recipient domain’s MX with our [MX lookup](/tools/mx-lookup/).

- **FilteredAsSpam on legitimate mail.** Check the sender’s SPF, DKIM and DMARC; see [Microsoft 365 SPF, DKIM and DMARC](/guides/microsoft-365-spf-dkim-dmarc-exchange-online/).

**Official documentation:** [Get-MessageTraceV2](https://learn.microsoft.com/en-us/powershell/module/exchangepowershell/get-messagetracev2?view=exchange-ps) · [Get-MessageTraceDetailV2](https://learn.microsoft.com/en-us/powershell/module/exchangepowershell/get-messagetracedetailv2?view=exchange-ps) · [Message trace in the Exchange admin center](https://learn.microsoft.com/en-us/exchange/monitoring/trace-an-email-message/message-trace-modern-eac) · [Exchange team: GA of the new message trace](https://techcommunity.microsoft.com/blog/exchange/announcing-general-availability-ga-of-the-new-message-trace-in-exchange-online/4420243)

**Related:** [Microsoft 365 SPF DKIM DMARC: Secure Exchange Online Setup](/guides/microsoft-365-spf-dkim-dmarc-exchange-online/) · [Email Header Analyzer](/tools/email-header-analyzer/) · [Microsoft 365 shared mailbox vs distribution group vs Microsoft 365 Group](/guides/shared-mailbox-vs-distribution-group/) · [Employee Offboarding Checklist: Secure AD, M365 and Workspace Steps](/guides/employee-offboarding-checklist/) · [MX Lookup](/tools/mx-lookup/)

**See also:** [Exchange Online Message Trace to CSV: Get-MessageTraceV2 Script](/scripts/exo-message-trace-report/) · [Exchange Online Mailbox Permissions Report: FullAccess, SendAs](/scripts/exo-mailbox-permissions-report/) · [Exchange Server SE Upgrade from 2019 CU15: In-Place Steps](/guides/exchange-server-se-upgrade/)

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