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

# Authentication

> How to get your API key, store it securely, and use it in every request.

## Getting your API key

<Steps>
  <Step title="Create an account">
    Sign up at [oddsstream.io/signup](https://oddsstream.io/signup). A free account gives you immediate access.
  </Step>

  <Step title="Open the Keys page">
    Go to **Dashboard → Keys**. Your production key is listed there.
  </Step>

  <Step title="Copy your key">
    Click **Copy**. The key is shown once in full — after that it's masked in the dashboard. If you lose it, rotate it to get a new one.

    Key format: `os_live_` followed by 32 hex characters.

    ```
    os_live_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4
    ```
  </Step>
</Steps>

***

## Using the key

Every request must include your key in the `X-Api-Key` header:

```bash theme={null}
curl "https://api.oddsstream.io/api/odds" \
  -H "X-Api-Key: os_live_YOUR_KEY"
```

***

## Storing the key securely

**Always use environment variables.** Never hardcode your key in source code.

<Tabs>
  <Tab title="bash / shell">
    ```bash theme={null}
    # Add to your shell profile or .env file
    export ODDSSTREAM_API_KEY=os_live_YOUR_KEY
    ```
  </Tab>

  <Tab title="Node.js">
    ```bash theme={null}
    # .env  (never commit this file)
    ODDSSTREAM_API_KEY=os_live_YOUR_KEY
    ```

    ```javascript theme={null}
    import "dotenv/config"; // npm install dotenv

    const API_KEY = process.env.ODDSSTREAM_API_KEY;
    if (!API_KEY) throw new Error("ODDSSTREAM_API_KEY is not set");

    const resp = await fetch("https://api.oddsstream.io/api/odds", {
      headers: { "X-Api-Key": API_KEY }
    });
    ```
  </Tab>

  <Tab title="Python">
    ```bash theme={null}
    # .env  (never commit this file)
    ODDSSTREAM_API_KEY=os_live_YOUR_KEY
    ```

    ```python theme={null}
    import os
    from dotenv import load_dotenv  # pip install python-dotenv

    load_dotenv()
    API_KEY = os.environ["ODDSSTREAM_API_KEY"]  # raises KeyError if missing

    import requests
    resp = requests.get(
        "https://api.oddsstream.io/api/odds",
        headers={"X-Api-Key": API_KEY}
    )
    ```
  </Tab>

  <Tab title=".gitignore">
    ```bash theme={null}
    # Prevent accidental commits
    .env
    .env.local
    .env.*.local
    ```
  </Tab>
</Tabs>

<Warning>
  **Never expose your API key in client-side JavaScript.** Any key in browser code can be extracted and abused. Always call the API from your server and proxy results to your frontend. Never commit keys to git — use environment variables or a secrets manager.
</Warning>

***

## Key management

From **Dashboard → Keys**:

| Action     | What it does                                                                                           |
| ---------- | ------------------------------------------------------------------------------------------------------ |
| **Copy**   | Re-copies the masked key — save it when you first create it                                            |
| **Rotate** | Generates a new key immediately. The old key stops working instantly. Use this if your key was leaked. |
| **Revoke** | Permanently disables the key. You'll need to create a new one.                                         |

***

## Error responses

| Status | Code                       | What it means                                                      | Fix                                          |
| ------ | -------------------------- | ------------------------------------------------------------------ | -------------------------------------------- |
| `401`  | `unauthorized`             | Key missing, empty, or doesn't exist                               | Check the header name and key value          |
| `403`  | `live_not_on_plan`         | Requested live odds without Pro Live plan                          | Upgrade to Pro+ Live                         |
| `403`  | `websocket_not_on_plan`    | Requested WS token without a plan that includes WebSocket          | Upgrade to Pro+ RT or Pro+ Live              |
| `403`  | `bookmaker_not_configured` | Free plan: no bookmaker selected in dashboard                      | Visit Dashboard → Billing to pick one        |
| `403`  | `bookmaker_forbidden`      | Free plan: requesting a different bookmaker than your selected one | Switch to your selected bookmaker            |
| `429`  | `rate_limit_exceeded`      | Too many requests                                                  | Back off and retry after `X-RateLimit-Reset` |

**401 vs 403 in plain English:**

* `401` = "I don't know who you are" — the key is wrong or missing
* `403` = "I know who you are, but you can't access this" — check the specific code for the fix

***

## Rate limit headers

Every response includes:

```
X-RateLimit-Remaining: 187
X-RateLimit-Reset: 1745160060
```

When `X-RateLimit-Remaining` drops below 20, slow down:

<Tabs>
  <Tab title="JavaScript">
    ```javascript theme={null}
    const resp = await fetch(url, { headers });
    const remaining = Number(resp.headers.get("X-RateLimit-Remaining") ?? 999);
    if (remaining < 20) await new Promise(r => setTimeout(r, 5_000));
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import time
    resp = requests.get(url, headers=headers)
    remaining = int(resp.headers.get("X-RateLimit-Remaining", 999))
    if remaining < 20:
        time.sleep(5)
    ```
  </Tab>
</Tabs>

***

## Plan limits

| Plan      | Rate limit   | WebSocket          | Endpoints                                      |
| --------- | ------------ | ------------------ | ---------------------------------------------- |
| Free      | 200 req/day  | 30 connections/day | Sports, Bookmakers, Events, Odds (1 bookmaker) |
| Pro       | 5,500 req/hr | Not included       | + Value Bets, all bookmakers                   |
| Pro+ RT   | 5,500 req/hr | Unlimited          | + WebSocket Stream                             |
| Pro+ Live | 5,500 req/hr | Unlimited          | + Live in-play odds, WebSocket Stream          |

Upgrade at [oddsstream.io/pricing](https://oddsstream.io/pricing).
