API · Reference

Reading Polymarket market data needs no key. Placing or cancelling orders needs two layers of auth: an L1 wallet signature (EIP-712) to create your API credentials, and an L2 HMAC signature on every authenticated request. This page covers both, plus the four signature types and the funder address — the two things that cause most auth errors.

At a glance
  • Read (Gamma, Data, public CLOB) — no auth.
  • L1 — your wallet signs an EIP-712 message (ClobAuth domain version "1") to create or derive API credentials.
  • L2 — each order request is signed with an HMAC of api_key / api_secret / api_passphrase.

Create or derive your API credentials (L1)

Your wallet signs once to mint (or re-derive) a deterministic set of API credentials. Use the V2 SDK — the V1 py-clob-client (0.34.x) is archived and non-functional.

from py_clob_client_v2 import ClobClient   # pip install py-clob-client-v2

# L1: your wallet signs to derive API credentials
client = ClobClient(host="https://clob.polymarket.com", chain_id=137, key=PRIVATE_KEY)
creds = client.create_or_derive_api_key()    # api_key, api_secret, api_passphrase

# L1 + L2: rebuild the client WITH creds for authenticated calls (no set_api_creds in v2)
client = ClobClient(host="https://clob.polymarket.com", chain_id=137, key=PRIVATE_KEY, creds=creds)
// polymarket_client_sdk_v2 — official Rust client (repo rs-clob-client-v2)
use polymarket_client_sdk_v2::clob::{Client, Config};
use polymarket_client_sdk_v2::clob::types::SignatureType;
let client = Client::new("https://clob.polymarket.com", Config::default())?
    .authentication_builder(&signer)
    .funder(deposit_wallet)                   // deposit wallets work in Rust today
    .signature_type(SignatureType::Poly1271)
    .authenticate().await?;

Store the three values as secrets (env vars). Reading market data never needs them.

Signature types (0–3)

The signature type tells the exchange how your order signature maps to the wallet that holds the funds (the funder). Pick the one that matches how your account was created.

TypeNameWhen
0EOAA plain wallet (e.g. MetaMask) that both signs and holds funds.
1POLY_PROXYEmail / Magic.link signup — a proxy wallet holds funds; your key signs on its behalf.
2POLY_GNOSIS_SAFEA Gnosis Safe holds funds; a signer key signs for it.
3POLY_1271A deposit wallet (EIP-1271 smart-contract signature).

When the signing key differs from the fund-holding address (types 1–3), you must pass both the private key and the funder address.

Deposit wallets (type 3) — use Rust today. As of June 2026 the Python (py-clob-client-v2) and TypeScript (@polymarket/clob-client-v2) clients have an open bug: L1 auth binds the API key to your EOA instead of the deposit wallet, so orders are rejected ("maker address not allowed, please use the deposit wallet flow"). The official Rust client handles EIP-1271 deposit-wallet signing correctly. Non-Rust workaround: use an email/Magic account and sign with signature_type = POLY_PROXY (type 2). Re-check Polymarket issues #70 (Python) / #65 (TS) for a fix.

Signing requests (L2)

Each authenticated request carries an HMAC signature built from your API secret plus the request method, path, body and a timestamp. The V2 SDK builds these headers for you. Two things to keep correct:

  • Clock — drift greater than ~60 seconds gets your signature rejected. Sync via NTP.
  • Domains — the L2/ClobAuth EIP-712 domain version stays "1"; only the V2 Exchange (order) domain moved to "2". A V1 client signs the wrong order domain and fails — see order_version_mismatch.

Key safety

  • Never hardcode private keys or API secrets — use environment variables; never commit them.
  • Keep your signing key and funder address separate from your day-to-day wallet where you can.
  • Reading-only bots need no credentials at all — start there.

Next: CLOB API: place an order · Fix an auth error · Migrate v1 → v2.