Polymarket Bot Tutorial · Chapter 6 of 32

Polymarket bot authentication and wallet setup: proxy wallets vs EOA, API key generation via SDK, sigType 2 for Gnosis Safe, key storage best practices, and the Magic-to-Privy migration.

What this chapter covers

Polymarket's wallet model has three moving parts: an externally owned account (EOA) that signs orders, a smart-contract proxy that holds funds, and the Polymarket CLOB API key that authenticates HTTP requests. Getting all three wired correctly is the single most common Day 1 failure for new builders, and it became more confusing after the August 2025 Magic Labs to Privy migration. This chapter walks each piece in setup order, with the specific environment variables and signature-type flag that production code needs.

  • Proxy wallet vs EOA: which to bot with
  • Generating an API key (SDK steps)
  • sigType 2 and POLY_FUNDER_ADDRESS (Gnosis Safe)
  • Key storage: .env, vault, KMS
  • The Magic Labs to Privy migration
  • Approving USDC/pUSD spending
  • Wallet recovery and backup

Proxy wallet vs EOA: which to bot with

Polymarket uses a smart-contract proxy wallet pattern. Your EOA - the address tied to your private key - signs transactions and orders. A Gnosis Safe deployed at a deterministic address holds the actual pUSD and outcome shares. The proxy address is what shows in the Polymarket UI's "wallet" panel; the EOA is what signs.

For bots, you always sign with the EOA (PRIVATE_KEY in env) and reference the proxy address as POLY_FUNDER_ADDRESS in the CLOB client config. Sending orders with the EOA as funder produces "insufficient balance" errors even when the proxy is funded.

You cannot bot with the EOA alone - Polymarket's web flow always creates a proxy on signup. Confirm both addresses with polymarket wallet show from the CLI, or read the proxy address from the Polymarket UI's settings.

Generating an API key (SDK steps)

The CLOB API requires three credentials: key, secret, passphrase. These are not your wallet private key - they are an HMAC-style credential set bound to your wallet, used for HTTP request authentication only.

Generate them once with the SDK:

# Python
from py_clob_client_v2 import ClobClient
c = ClobClient(host="https://clob.polymarket.com", chain_id=137,
               key="<PRIVATE_KEY>", signature_type=2,
               funder="<PROXY_ADDRESS>")
creds = c.create_or_derive_api_key()     # V2 method (was create_or_derive_api_creds in V1)
print(creds.key, creds.secret, creds.passphrase)   # V2 returns key / secret / passphrase

Store the output in a JSON file and load it on every bot start; do not regenerate per session - the API server caches the credential set, and rotating frequently can trip rate-limit logic. The credentials never expire automatically. Rotate them only if you suspect a leak.

sigType 2 and POLY_FUNDER_ADDRESS (Gnosis Safe)

The signature_type argument controls how the CLOB validates your order signatures. There are four:

  • 0 / EOA: the EOA is both signer and funder. Used when a private key was imported directly.
  • 1 / POLY_PROXY: Magic/email proxy contract. Many pre-2025 accounts.
  • 2 / POLY_GNOSIS_SAFE: a Gnosis Safe holds funds, the EOA signs. Common current standard.
  • 3 / POLY_1271: deposit wallets (EIP-1271 smart-contract signatures) - the type Polymarket recommends for new accounts. Today the Python/TS V2 clients have an open deposit-wallet signing bug, so use the Rust client for type 3 - see Authentication.

Use signature_type=2 for any account created after August 2025 (the Privy migration) or any account where you can see a Gnosis Safe address in the Polymarket UI. The POLY_FUNDER_ADDRESS env var must be the Safe address, not the EOA. Mismatched signature_type against the funder type silently produces order rejections that look like "insufficient allowance" or "balance: 0" - the error message is misleading.

Key storage: .env, vault, KMS

Three reasonable storage tiers for the EOA private key.

  1. .env file (single-machine development). The file lives on the VPS, the bot reads it on start, the key never leaves the host. Adequate for <$1k wallets. chmod 600 .env and make sure your repo's .gitignore excludes it.
  2. Self-hosted vault (HashiCorp Vault, age-encrypted file, or systemd-creds). Adds an unlock step on bot start. Worth it for wallets in the $1k-$10k range.
  3. Cloud KMS (AWS KMS, GCP KMS). The bot calls KMS to decrypt the key in memory; the key never touches disk. Worth the operational complexity only above $10k or for multi-bot fleets.

What to never do: commit a private key to git, paste it into a chat, store it in a password manager that syncs to cloud services without local-only mode. The on-chain blast radius of a Polymarket EOA leak is your entire pUSD balance and outcome share inventory.

The Magic Labs to Privy migration

In August 2025 Polymarket migrated their primary embedded-wallet provider from Magic Labs to Privy. The bot-facing effect is small but specific.

Pre-migration accounts (created via Magic) typically use signature_type=1 (POLY_PROXY). Post-migration accounts use signature_type=2 (POLY_GNOSIS_SAFE). Some users migrated their old account; some kept the original. There is no way to tell from the public API which type your account uses - you check by trying to sign an order and observing the rejection.

The migration also changed how the UI exposes the funder address. Older Polymarket UI flows showed the proxy address in the dashboard; the current flow buries it in account settings. The CLI command polymarket wallet show is the cleanest way to confirm both values, regardless of when the account was created.

Approving USDC/pUSD spending

For the CLOB to move your pUSD on order match, the proxy must have approved the Polymarket exchange contracts as spenders. The Polymarket UI sets these approvals during the first deposit. For bots that fund the proxy directly, you must set them manually.

Three approvals to set, once per wallet:

  1. pUSD (ERC-20) → exchange contract
  2. Conditional Tokens (ERC-1155) → exchange contract (for selling shares)
  3. Conditional Tokens (ERC-1155) → NegRisk exchange contract (for selling NegRisk shares)

Run polymarket approve from the CLI on first setup. The transaction costs a few cents in MATIC gas. Verify with polymarket approve check - all three should return "approved." The most common silent bug for new builders is missing the NegRisk approval, which fails only when selling shares from multi-outcome markets and looks like a balance error.

Wallet recovery and backup

The bot's wallet has two recoverable elements: the EOA private key, and the Polymarket account password (which gates access through the web UI but not through the SDK).

The EOA private key is the only thing that matters for the bot. Loss = loss of everything in the proxy. Cold backup: write it on paper, seal in an envelope, store offsite. Hot backup: encrypted USB stick. Never email it to yourself; never store unencrypted in cloud storage.

The Polymarket account password is recoverable via Magic Labs / Privy email recovery as long as you still control the original signup email. It does not gate bot access - the bot uses the EOA private key directly.

If you suspect a key leak: immediately withdraw pUSD and outcome tokens to a new wallet, generate a new EOA, redeploy the bot with the new key. The leaked key cannot be revoked; it can only be drained.

Frequently asked questions

Do I need a separate wallet for my bot?
Strongly recommended yes. Use a fresh EOA or a fresh email-account-derived proxy wallet that holds only the capital you have allocated to the bot. If the bot key leaks, only the bot funds are at risk - your main holdings stay safe.
What is sigType 2 in Polymarkets API?
sigType 2 indicates a Gnosis Safe (proxy wallet) signature, used when you log in with email/Google and Polymarket creates the proxy for you. For sigType 2, the POLY_FUNDER_ADDRESS environment variable must be the PROXY address (the one shown in the Polymarket UI), not the underlying EOA. This is a common configuration bug.
How do I generate a Polymarket API key?
Use the SDK. In Python: ApiCreds returned by client.create_api_key() once you have authenticated with your wallet. In Node.js: similar via @polymarket/clob-client-v2 client.createApiKey(). Save the returned key/secret/passphrase to your .env (never commit to git).
Are Polymarket API keys revocable?
Yes. You can derive new keys at any time via the SDK; old keys remain valid until explicitly revoked via client.deleteApiKey(creds). Best practice is to rotate keys periodically and revoke any key that touched a compromised machine.
What changed when Polymarket migrated from Magic Labs to Privy?
Login OTP codes went from 3 digits (vulnerable to brute force, exploited in the December 2025 hack) to longer codes plus device binding via Privy. For bots, the practical change is the auth ceremony - the SDK abstracts most of it. If your bot was hard-coded to Magic Labs API endpoints (rare), update to the Privy flow.
Should I store keys in a .env file?
For a single-VPS bot - yes, with proper file permissions (chmod 600 .env, owned by the bot user). For multi-machine setups or production-grade ops - move to a secrets manager (AWS Secrets Manager, Vault, doppler.com). Never commit .env to git, ever.