# Get Sequence Variant Stats

> Compare the A/B variants of a sequence step.

Requires an API key with the `read` scope.

Counts the emails sent for this step by each variant, how many were opened, how many got a reply and how many bounced. Rates are percentages of `sent`. A step with no variants returns an empty array.

## Path Parameters

<ParamField path="campaign_id" type="string" required>The campaign.</ParamField>
<ParamField path="sequence_id" type="string" required>The sequence step.</ParamField>

## Response

<ResponseField name="data" type="array">
  <Expandable title="Variant stats">
    <ResponseField name="variantId" type="string">The variant.</ResponseField>
    <ResponseField name="label" type="string">Its label.</ResponseField>
    <ResponseField name="sent" type="integer">Emails sent.</ResponseField>
    <ResponseField name="opened" type="integer">Emails opened.</ResponseField>
    <ResponseField name="replied" type="integer">Replies received.</ResponseField>
    <ResponseField name="bounced" type="integer">Emails that bounced.</ResponseField>
    <ResponseField name="openRate" type="number">`opened / sent` in percent, one decimal.</ResponseField>
    <ResponseField name="replyRate" type="number">`replied / sent` in percent, one decimal.</ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
```bash cURL
curl -X GET "https://api.foxreach.io/api/v1/campaigns/cmcamp1/sequences/cmseq1/variant-stats" \
  -H "X-API-Key: otr_your_key"
```

```python Python SDK
from foxreach import FoxReach

client = FoxReach(api_key="otr_your_key")
stats = client.campaigns.sequences.variant_stats("cmcamp1", "cmseq1")
```

```typescript TypeScript SDK
import { FoxReach } from "foxreach";

const client = new FoxReach({ apiKey: "otr_your_key" });
const stats = await client.campaigns.sequences.variantStats("cmcamp1", "cmseq1");
```
</RequestExample>

<ResponseExample>
```json
{
  "data": [
    { "variantId": "cmvar1", "label": "A", "sent": 120, "opened": 66, "replied": 9, "bounced": 2, "openRate": 55.0, "replyRate": 7.5 },
    { "variantId": "cmvar2", "label": "B", "sent": 118, "opened": 71, "replied": 14, "bounced": 1, "openRate": 60.2, "replyRate": 11.9 }
  ]
}
```
</ResponseExample>

## Errors

| Status | Description |
|--------|-------------|
| `401` | Missing, invalid, revoked or expired API key |
| `403` | API key does not have the required scope: `read` |
| `404` | Campaign or sequence step not found in this workspace |

## Rate Limit

- **300 requests per minute, shared by every `read` call in your workspace.** Fixed 60-second window.
- Every response includes `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset` (unix epoch seconds) and `X-RateLimit-Bucket` (`read`).
- A `429` response includes a `Retry-After` header (seconds until the bucket resets).

For client-side patterns (`Retry-After`, exponential backoff, monitoring `X-RateLimit-Remaining`), see [Rate Limiting](/rate-limiting).
