> ## Documentation Index
> Fetch the complete documentation index at: https://lifters.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch Ingestion

> Send multiple events in a single API call — up to 500 events per request for high-volume scenarios.

<Snippet file="snippets/api-key-auth.mdx" />

<Snippet file="snippets/rate-limits-note.mdx" />

## When to Use Batch

Use batch endpoints when:

* **Settling multiple bets** after a game concludes (e.g., 100 bets on a football match)
* **Importing historical data** from a legacy system
* **Catch-up ingestion** after downtime or network issues
* **High-frequency scenarios** where individual calls would exceed rate limits

<Info>
  Batch endpoints accept up to **500 events per request**. For more than 500 events,
  split them into multiple batch calls.
</Info>

## Batch Endpoints

Each domain has a batch variant:

| Domain       | Batch Endpoint                   |
| ------------ | -------------------------------- |
| Users        | `POST /api/v1/user/batch`        |
| Transactions | `POST /api/v1/transaction/batch` |
| Casino       | `POST /api/v1/casino/batch`      |
| Sports       | `POST /api/v1/sport/batch`       |
| Generic      | `POST /v1/events/batch`          |

## Batch Request Format

All batch endpoints use the same envelope:

```json theme={null}
{
  "events": [
    { /* event 1 */ },
    { /* event 2 */ },
    { /* ... up to 500 */ }
  ]
}
```

Each event in the array has the same schema as the single-event endpoint.

## Example — Batch Sport Updates

Sending one open and two results (win and lose):

```bash theme={null}
curl -X POST https://events.atlas.io/api/v1/sport/batch \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "org_id": "org_abc123",
        "brand_id": "brand_xyz",
        "event": "open",
        "user_id": "user_001",
        "currency": "BRL",
        "bet_id": "bet_001",
        "bet_dt": "2026-03-11T14:30:00Z",
        "ticket_status": "open",
        "bet_type": "SINGLE",
        "bet_timing": "PREMATCH",
        "bet_platform": "MOBILE",
        "bet_virtual": false,
        "bet_amount": 50.00,
        "bet_odds": 2.90,
        "selections": [{ "selection_id": "sel_001", "selection_status": "open" }]
      },
      {
        "org_id": "org_abc123",
        "brand_id": "brand_xyz",
        "event": "lose",
        "user_id": "user_002",
        "currency": "BRL",
        "bet_id": "bet_002",
        "bet_dt": "2026-03-11T16:00:00Z",
        "ticket_status": "lose",
        "bet_amount": 30.00,
        "win_amount": 0.00
      },
      {
        "org_id": "org_abc123",
        "brand_id": "brand_xyz",
        "event": "win",
        "user_id": "user_003",
        "currency": "BRL",
        "bet_id": "bet_003",
        "bet_dt": "2026-03-11T16:00:00Z",
        "ticket_status": "win",
        "bet_amount": 20.00,
        "win_amount": 58.00
      }
    ]
  }'
```

## Batch Response

### Full Success — `202 Accepted`

All events were accepted and published to Kafka:

```json theme={null}
{
  "data": {
    "accepted": 3,
    "failed": 0,
    "events": [
      { "eid": "evt_001", "status": "accepted" },
      { "eid": "evt_002", "status": "accepted" },
      { "eid": "evt_003", "status": "accepted" }
    ]
  }
}
```

### Partial Failure — `207 Multi-Status`

Some events failed (e.g., Kafka publish error for specific events):

```json theme={null}
{
  "data": {
    "accepted": 2,
    "failed": 1,
    "events": [
      { "eid": "evt_001", "status": "accepted" },
      { "eid": "evt_002", "status": "accepted" },
      { "eid": null,      "status": "failed", "error": "kafka: publish timeout" }
    ]
  }
}
```

### Validation Failure — `422 Unprocessable Entity`

The **entire batch** is rejected if any event fails struct validation:

```json theme={null}
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "events[2].bet_amount: required field missing"
  }
}
```

<Warning>
  A `422` error means **no events were processed**. Fix the validation error and resubmit
  the entire batch. This is different from a `207` where partial events succeed.
</Warning>

## Error Handling Pattern

```javascript theme={null}
async function sendBatchWithRetry(events, apiKey, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    const response = await fetch('https://events.atlas.io/api/v1/sport/batch', {
      method: 'POST',
      headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
      body: JSON.stringify({ events }),
    });

    if (response.status === 202) {
      return await response.json(); // Full success
    }

    if (response.status === 207) {
      const result = await response.json();
      // Retry only the failed events
      const failedIndices = result.data.events
        .map((e, i) => e.status === 'failed' ? i : null)
        .filter(i => i !== null);
      events = failedIndices.map(i => events[i]);
      continue;
    }

    if (response.status === 422) {
      throw new Error('Validation error — fix payload before retrying');
    }

    if (response.status === 429) {
      // Exponential backoff for rate limits
      await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1000));
      continue;
    }
  }
  throw new Error(`Failed after ${maxRetries} attempts`);
}
```

## Performance Tips

<Tip>
  **Optimal batch size:** 100–200 events per call balances throughput and failure isolation.
  Larger batches (up to 500) increase efficiency but mean more events to retry on partial failure.
</Tip>

| Scenario                      | Recommendation                                                                           |
| ----------------------------- | ---------------------------------------------------------------------------------------- |
| Real-time settlement          | Single events (`/api/v1/sport`)                                                          |
| Bulk settlement (\< 500 bets) | One batch call                                                                           |
| Bulk settlement (> 500 bets)  | Split into 500-event chunks, process in parallel                                         |
| Historical import             | Batch with event timestamps set in payload (`registered_at`, `transaction_dt`, `bet_dt`) |

## Deduplication

Atlas deduplicates events using the `bet_id` / `round_id` / `transaction_id` fields within each domain. Sending the same event twice will result in only one being processed.

<Note>
  Deduplication is eventual (within a few seconds). In high-throughput scenarios,
  avoid sending the same event in rapid succession as the dedup window may not have closed.
</Note>
