---
title: "Horizon + Polymarket"
description: "Trade on Polymarket with Horizon, the largest prediction market on Polygon."
icon: "chart-line"
---

[Polymarket](https://polymarket.com) is the largest prediction market, running on Polygon. Horizon supports the CLOB (Central Limit Order Book) API with EIP-712 signed orders.

## Quick Setup

```python
hz.run(
    name="poly_mm",
    exchange=hz.Polymarket(
        private_key="0x...",
        api_key="...",
        api_secret="...",
        api_passphrase="...",
    ),
    mode="live",
    ...
)
```

## Credentials

There are three ways to provide credentials:

<Tabs>
  <Tab title="Private key only">
    When only `private_key` is set, Horizon automatically derives API credentials from the CLOB `/auth/derive-api-key` endpoint on startup (requires `eth-account` package):

    ```python
    hz.Polymarket(private_key="0xYOUR_PRIVATE_KEY")
    ```

    <Note>
      This requires `pip install eth-account`. The derivation only happens in live mode.
    </Note>
  </Tab>
  <Tab title="Full credentials">
    ```python
    hz.Polymarket(
        private_key="0x...",
        api_key="...",
        api_secret="...",
        api_passphrase="...",
    )
    ```
  </Tab>
  <Tab title="Environment variables">
    ```bash
    export POLYMARKET_PRIVATE_KEY="0x..."
    export POLYMARKET_API_KEY="..."
    export POLYMARKET_API_SECRET="..."
    export POLYMARKET_API_PASSPHRASE="..."
    ```

    ```python
    hz.Polymarket()  # reads from env
    ```
  </Tab>
</Tabs>

## Polymarket Configuration

```python
@dataclass
class Polymarket:
    private_key: str | None = None
    clob_url: str = "https://clob.polymarket.com"
    gamma_url: str = "https://gamma-api.polymarket.com"
    api_key: str | None = None
    api_secret: str | None = None
    api_passphrase: str | None = None
```

| Field | Default | Description |
|-------|---------|-------------|
| `private_key` | `None` | Ethereum private key for EIP-712 signing |
| `clob_url` | `https://clob.polymarket.com` | CLOB API base URL |
| `gamma_url` | `https://gamma-api.polymarket.com` | Gamma API for market metadata |
| `api_key` | `None` | CLOB API key |
| `api_secret` | `None` | CLOB API secret (HMAC-SHA256) |
| `api_passphrase` | `None` | CLOB API passphrase |

## EIP-712 Signing

Polymarket orders require EIP-712 typed data signatures for the CTF Exchange contract on Polygon (chainId 137). Horizon handles this entirely in Rust using `k256` (secp256k1). No Python crypto dependencies needed.

The signing flow:
1. Build `CtfOrder` struct (token_id, maker address, price, size, nonce, etc.)
2. Compute EIP-712 struct hash and domain separator
3. Sign the digest with the private key (ECDSA on secp256k1)
4. Submit the signed order to the CLOB API

## Token ID Routing

Polymarket uses numeric **token IDs** (not market slugs) to identify outcomes. Each market has a Yes token and a No token:

```python
market = hz.Market(
    id="will-btc-hit-100k",
    yes_token_id="123456789",
    no_token_id="987654321",
    condition_id="0x...",
    neg_risk=True,
)

market.token_id(Side.Yes)  # "123456789"
market.token_id(Side.No)   # "987654321"
```

<Tip>
  In live mode, `hz.run()` automatically resolves token IDs from the Gamma API. You don't need to set them manually unless you want to override the resolution.
</Tip>

### Market Resolution

When `mode="live"`, Horizon queries the Gamma API to resolve market metadata:

```
GET https://gamma-api.polymarket.com/markets?slug=will-btc-hit-100k
```

This populates:
- `yes_token_id` / `no_token_id`
- `condition_id`
- `neg_risk`
- `exchange = "polymarket"`

## Fill Polling

Polymarket fills arrive via polling the `/trades` endpoint. Each cycle, `drain_fills()` calls `poll_fills_async()` which:

1. Queries recent trades for tracked order IDs
2. Deduplicates against previously seen fill IDs
3. Returns new fills with exchange-assigned timestamps

## HMAC-SHA256 Authentication

API requests are authenticated with HMAC-SHA256:
- The `api_secret` is used as the HMAC key
- Each request includes a timestamp, API key, and signature in headers
