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

# Authenticate API Requests

> Every StratAlerts API request requires a scoped API key. Pass it as a Bearer token or X-API-Key header. Covers key creation, scopes, and auth error codes.

Every request to the StratAlerts Partner API must include an API key. Keys are scoped — each key grants access only to the specific endpoints and channels listed in its scope set. You manage keys from your account settings after subscribing to the API Access plan.

## Getting an API key

<Steps>
  <Step title="Subscribe to API Access">
    API access is a separate subscription from your standard StratAlerts plan. Visit the [API Access subscription page](https://app.stratalerts.com/billing/subscribe/?plan=market_api) and complete checkout.
  </Step>

  <Step title="Generate a key">
    After subscribing, go to your [account settings](https://app.stratalerts.com/users/redirect/) and generate a new API key. Give it a descriptive label so you can identify it later.
  </Step>

  <Step title="Copy the raw key immediately">
    The full key value is only shown once at creation time. Copy it to a secure location before closing the dialog — you cannot retrieve the raw key afterward, only revoke and regenerate it.
  </Step>
</Steps>

## Passing your key

You can pass your API key in either of two request headers. Both are accepted on every REST endpoint and on the WebSocket handshake.

<Tabs>
  <Tab title="Authorization header">
    Use the standard HTTP `Authorization` header with the `Bearer` scheme:

    ```text theme={null}
    Authorization: Bearer YOUR_API_KEY
    ```
  </Tab>

  <Tab title="X-API-Key header">
    Alternatively, pass the raw key in the `X-API-Key` header:

    ```text theme={null}
    X-API-Key: YOUR_API_KEY
    ```
  </Tab>
</Tabs>

### Code examples

<CodeGroup>
  ```bash curl theme={null}
  curl -s \
    -H "Authorization: Bearer YOUR_API_KEY" \
    https://app.stratalerts.com/api/market/v1/market-status
  ```

  ```python Python theme={null}
  import httpx

  API_KEY = "YOUR_API_KEY"
  BASE_URL = "https://app.stratalerts.com/api/market/v1"

  response = httpx.get(
      f"{BASE_URL}/market-status",
      headers={"Authorization": f"Bearer {API_KEY}"},
  )
  response.raise_for_status()
  print(response.json())
  ```

  ```javascript JavaScript theme={null}
  const API_KEY = "YOUR_API_KEY";
  const BASE_URL = "https://app.stratalerts.com/api/market/v1";

  const response = await fetch(`${BASE_URL}/market-status`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });

  if (!response.ok) {
    const err = await response.json();
    throw new Error(err.error.message);
  }

  const data = await response.json();
  console.log(data);
  ```
</CodeGroup>

## Key scopes

When you generate a key, you choose which scopes to grant. A request to an endpoint whose scope is not on the key returns a `403 missing_scope` error. The table below lists all available scopes.

| Scope           | Grants access to                                                                                                                 |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `metadata:read` | `GET /instruments`, `GET /instruments/{symbol}`, `GET /market-status`                                                            |
| `prices:read`   | `GET /prices/latest`, WebSocket `quotes` channel                                                                                 |
| `candles:read`  | `GET /candles/{symbol}`                                                                                                          |
| `states:read`   | `GET /states/{symbol}`, `GET /setups/current`, WebSocket `states` channel                                                        |
| `alerts:read`   | `GET /alerts/in-force`, `GET /alerts/simultaneous-breaks`, WebSocket `alerts.in_force` and `alerts.simultaneous_breaks` channels |
| `ws:connect`    | Establish a WebSocket connection (required in addition to channel-specific scopes)                                               |

<Tip>
  Grant only the scopes your integration actually uses. A key scoped to `metadata:read` and `prices:read` cannot accidentally be used to pull alert data if it is ever leaked.
</Tip>

## Error responses

Authentication failures return a JSON error envelope. The HTTP status code and `code` field tell you exactly what went wrong.

| HTTP status | Error code             | Meaning                                                                                     |
| ----------- | ---------------------- | ------------------------------------------------------------------------------------------- |
| `401`       | `missing_api_key`      | No key was found in the request headers — either the header is absent or the value is empty |
| `403`       | `inactive_entitlement` | Your API Access subscription has lapsed or been cancelled                                   |
| `403`       | `missing_scope`        | The key is valid but does not have the scope required by this endpoint                      |

A `401` response looks like this:

```json theme={null}
{
  "error": {
    "code": "missing_api_key",
    "message": "missing api key"
  }
}
```

A `403` scope error looks like this:

```json theme={null}
{
  "error": {
    "code": "missing_scope",
    "message": "missing scope"
  }
}
```

## Security best practices

<Warning>
  Never embed your API key in client-side code, browser JavaScript, or a public repository. If a key is exposed, revoke it immediately from your account settings and generate a new one.
</Warning>

* Store your key in an environment variable or secrets manager, not in source code.
* Use a separate key per integration so you can revoke one without affecting others.
* Grant only the scopes each key requires — avoid creating full-access keys for read-only integrations.
* Rotate keys on a regular schedule or immediately if you suspect exposure.

## WebSocket authentication

The WebSocket connection handshake also requires your API key. Pass it in the same headers (`Authorization: Bearer` or `X-API-Key`) during the initial HTTP upgrade request. Your key must include both `ws:connect` and the scope for every channel you plan to subscribe to.

If authentication fails during the WebSocket handshake, the connection is closed with one of these close codes before any messages are exchanged:

| Close code | Meaning                                                        |
| ---------- | -------------------------------------------------------------- |
| `4401`     | No valid API key found in the handshake headers                |
| `4403`     | Active subscription not found, or key lacks `ws:connect` scope |

```python theme={null}
import asyncio
import json
import websockets

API_KEY = "YOUR_API_KEY"
WS_URL = "wss://app.stratalerts.com/ws/market/v1"

async def stream_quotes():
    headers = {"Authorization": f"Bearer {API_KEY}"}
    async with websockets.connect(WS_URL, additional_headers=headers) as ws:
        await ws.send(json.dumps({
            "op": "subscribe",
            "topics": [{"channel": "quotes", "symbols": ["AAPL", "TSLA"]}],
        }))
        async for message in ws:
            print(json.loads(message))

asyncio.run(stream_quotes())
```
