---
title: "Authentication"
description: "Set up your Horizon API key to use the SDK."
icon: "key"
---

Horizon requires an API key to use. You can get one by signing up at [api.mathematicalcompany.com](https://api.mathematicalcompany.com), or programmatically via the SDK.

## Setup

### Option A: Website (Manual)

<Steps>
  <Step title="Get your API key">
    Sign up at [api.mathematicalcompany.com](https://api.mathematicalcompany.com) and copy your API key.
    Keys start with `hz_live_`.
  </Step>
  <Step title="Set the environment variable">
    Add this to your shell profile (`~/.zshrc`, `~/.bashrc`, etc.):

    ```bash
    export HORIZON_API_KEY="hz_live_abc123..."
    ```

    Then reload your shell or run `source ~/.zshrc`.
  </Step>
  <Step title="Run your strategy">
    ```bash
    python my_strategy.py
    ```

    That's it. The SDK validates your key once at startup and caches the result locally. There is **zero latency impact** on trading.
  </Step>
</Steps>

### Option B: Programmatic (Agentic)

Create an account and generate an SDK key entirely from the command line or through an AI agent. No browser required.

<Steps>
  <Step title="Install the SDK">
    ```bash
    pip install horizon-sdk
    # or
    uv pip install horizon-sdk
    ```
  </Step>
  <Step title="Run setup">
    Pass your password via the `HORIZON_PASSWORD` environment variable to avoid shell history exposure:

    ```bash
    HORIZON_PASSWORD="your-password" python -c "
    from horizon.auth import setup
    result = setup('you@example.com', name='Your Name')
    print(result)
    "
    ```

    This will:
    1. Try to log in (if you already have an account)
    2. Sign up if the account does not exist
    3. Generate a new SDK key
    4. Save the encrypted key to `~/.horizon/credentials.json`

    On success you will see:

    ```json
    {
      "status": "ok",
      "prefix": "hz_sdk_a1b2c3d",
      "credentials_path": "/home/you/.horizon/credentials.json",
      "message": "SDK key saved (encrypted) to ..."
    }
    ```

    <Warning>
      If Supabase email confirmation is enabled, you will see `"status": "confirmation_required"`. Check your email and confirm before running setup again.
    </Warning>
  </Step>
  <Step title="Run your strategy">
    The engine auto-loads the key from `~/.horizon/credentials.json`:

    ```bash
    python my_strategy.py
    ```

    Or set the env var explicitly:

    ```bash
    export HORIZON_API_KEY="hz_sdk_..."
    ```
  </Step>
</Steps>

#### MCP / AI Agent Setup

AI agents using the [MCP server](/integrations/mcp-server) or [OpenClaw skill](/integrations/openclaw) can call the auth tools directly:

| Tool | Description |
|------|-------------|
| `horizon_setup` | Create account and generate SDK key (full flow) |
| `horizon_login` | Login to existing account and generate new key |
| `horizon_key_status` | Check if credentials are configured |

The agent asks the user for their email, sets `HORIZON_PASSWORD`, and calls `horizon_setup`. The key is saved encrypted to disk and never returned in full in tool responses (only the prefix is shown).

#### Python API

```python
from horizon.auth import (
    setup,           # Full flow: signup/login + key gen + save
    login,           # Login only
    signup,          # Signup only
    generate_sdk_key,  # Generate hz_sdk_ key
    hash_api_key,    # SHA-256 hash
    create_sdk_key,  # Insert key into Supabase
    save_credentials,  # Save encrypted to disk
    load_credentials,  # Load from disk
    resolve_password,  # Resolve from env var or argument
)
```

#### Security

- **Passwords**: Always use the `HORIZON_PASSWORD` env var instead of passing passwords as CLI arguments. CLI arguments are visible in shell history and process listings.
- **Key storage**: Credentials are encrypted at rest using a machine-derived key (hostname + username). The plaintext key is never written to disk.
- **Key exposure**: Tool responses only include the key prefix, never the full key.
- **Rate limiting**: Client-side rate limiting caps auth attempts to 5 per 60 seconds.

## How It Works

1. **Startup only** - validation happens once when `Engine` is created (inside `hz.run()`). After that, no network calls are made during trading.
2. **Local cache** - a successful validation is cached at `~/.horizon/license.json` for 72 hours. The raw key is never stored, only a SHA-256 hash.
3. **Offline grace period** - if the network is unavailable but a valid cache exists for your key, the SDK allows startup. No internet dependency during cached periods.
4. **Compiled in Rust** - the validation logic is in the compiled binary, not in bypassable Python code.

## Configuration

There are two ways to provide your API key:

### Environment Variable (Recommended)

```bash
export HORIZON_API_KEY="hz_live_abc123..."
```

This is the recommended approach. Set it once in your shell profile and forget about it.

### Explicit Parameter

You can pass the key directly in code - useful for notebooks, testing, or when you don't want to set environment variables.

**With `hz.run()`:**

```python
hz.run(
    name="my_strategy",
    api_key="hz_live_abc123...",
    markets=["test-market"],
    pipeline=[fair_value, quoter],
)
```

**With `Engine()` directly:**

```python
from horizon import Engine, RiskConfig

engine = Engine(RiskConfig(), api_key="hz_live_abc123...")
```

The explicit parameter takes priority over the environment variable.

### Resolution Order

| Priority | Source |
|----------|--------|
| 1 | `api_key` param in `hz.run()` / `api_key` param in `Engine()` |
| 2 | `HORIZON_API_KEY` environment variable |
| 3 | `~/.horizon/credentials.json` (saved by programmatic setup) |

## Error Messages

If the key is missing:

```
RuntimeError: auth error: No Horizon API key provided. Set HORIZON_API_KEY
or pass api_key to hz.run(). Get your key at api.mathematicalcompany.com
```

If the key is invalid or expired:

```
RuntimeError: auth error: Invalid or expired API key. Check your key at
api.mathematicalcompany.com
```

If there's a network error and no cached validation:

```
RuntimeError: auth error: Could not validate API key (network error) and no
cached validation found. Check your internet connection.
```

## Cache Location

The license cache is stored at:

```
~/.horizon/license.json
```

To force re-validation (e.g., after upgrading your plan), delete this file:

```bash
rm ~/.horizon/license.json
```

## Using with Docker

Pass the key as an environment variable:

```dockerfile
ENV HORIZON_API_KEY="hz_live_abc123..."
```

Or at runtime:

```bash
docker run -e HORIZON_API_KEY="hz_live_..." my-strategy
```
