Versão resumida

A Polymarket oferece três APIs públicas (uma API é uma forma de programas lerem dados ou enviarem operações automaticamente): CLOB (negociação), Gamma (descoberta de mercados) e Data (análises). O SDK oficial em Python é o py-clob-client 0.34.6. A autenticação usa uma chave de API + assinatura ECDSA, e as ordens são assinadas via EIP-712 por meio de uma carteira proxy na Polygon. Os limites de taxa restringem você a cerca de 60 ordens por minuto por chave. O principal ponto de atenção para novos desenvolvedores é o problema de mapeamento condition_id → token_id entre Gamma e CLOB. Resolva isso primeiro, e o restante fica muito mais simples. Cerca de US$ 40 milhões por mês em recompensas de liquidez e spread capturado por bots é ganho na Polymarket, quase tudo por usuários de API.

Parte 1: As três APIs

A Polymarket divide o trabalho de forma clara entre três serviços separados. Usar a API certa para cada tarefa mantém seu bot rápido, simples e dentro dos limites de taxa.

APIBase URLPurposeAuth Required
CLOB APIclob.polymarket.comEnviar, cancelar e acompanhar ordens. Ler livros de ofertas. Consultar posições.Sim (para negociar)
Gamma APIgamma-api.polymarket.comNavegar por mercados, buscar metadados, imagens, preços de resultados, volume, vencimento e tags.Não (pública)
Data APIdata-api.polymarket.comOperações históricas, snapshots de posições, análises de usuários e dados de ranking.Não (pública)

Um loop típico de bot usa Gamma para encontrar mercados, CLOB para buscar livros de ofertas e enviar operações, e Data para fazer backtest do desempenho da estratégia offline. Pense no Gamma como o "catálogo", no CLOB como a "bolsa" e no Data como o "armazém".

Parte 2: Autenticação & o modelo de carteira proxy

A Polymarket não assina operações com a chave privada da sua carteira principal. Em vez disso, ela usa uma carteira proxy no estilo Gnosis Safe. Sua carteira principal autoriza um proxy, e o proxy executa todas as operações na Polygon. Seu bot de API se comunica com esse proxy.

Do que você precisa

  • Chave de API - gere em Polymarket Settings → Developer
  • Chave privada - a chave da sua carteira de negociação (NÃO a seed phrase da sua MetaMask principal)
  • Endereço funder - o endereço da sua carteira proxy (exibido em Settings → Wallet)
  • Chain ID - 137 (Polygon mainnet)
  • Signature type - 1 (POLY_PROXY, padrão para usuários de varejo)

Parte 3: Instalação do py-clob-client

O SDK oficial em Python é a forma mais rápida de sair do zero e enviar sua primeira ordem. Vamos usar a versão 0.34.6 — a versão atual no PyPI (fev. de 2026) e a que praticamente todo bot em produção usa.

# Create a virtual environment first
python3 -m venv venv
source venv/bin/activate   # macOS/Linux
venv\Scripts\activate      # Windows

# Install the SDK
pip install py-clob-client==0.34.6 requests websocket-client python-dotenv

Configuração básica do cliente

import os
from dotenv import load_dotenv
from py_clob_client.client import ClobClient
from py_clob_client.constants import POLYGON

load_dotenv()

client = ClobClient(
    host="https://clob.polymarket.com",
    key=os.environ["POLY_PRIVATE_KEY"],
    chain_id=POLYGON,          # 137
    signature_type=1,          # POLY_PROXY
    funder=os.environ["POLY_FUNDER"],
)

# One-time: derive and cache API credentials
client.set_api_creds(client.create_or_derive_api_creds())

A chamada create_or_derive_api_creds() assina uma mensagem com sua chave privada. Ela troca essa assinatura por uma chave de API, um segredo e uma passphrase. Armazene esses dados no seu .env após a primeira execução, para não chamar o endpoint de derivação a cada inicialização.

Parte 4: Descobrindo mercados via Gamma

Antes de operar, você precisa encontrar mercados que valham a pena. Gamma retorna JSON com tudo o que a interface da Polymarket mostra: a pergunta, os resultados, os preços, o volume em 24 h, a expiração, as tags e as imagens.

import requests

resp = requests.get(
    "https://gamma-api.polymarket.com/markets",
    params={
        "active": "true",
        "closed": "false",
        "tag_slug": "politics",
        "limit": 20,
        "order": "volume24hr",
        "ascending": "false",
    },
    timeout=10,
)
resp.raise_for_status()
markets = resp.json()

for m in markets:
    print(f"{m['slug']:50} Yes ${float(m['outcomePrices'][0]):.3f}  Vol24h ${m.get('volume24hr', 0):,.0f}")

Parâmetros úteis de consulta do Gamma

ParameterWhat it does
tag_slugFiltra por categoria (política, esportes, cripto, cultura etc.)
active=trueSomente mercados que estão aceitando negociações no momento
closed=falseOculta mercados já resolvidos
order=volume24hrOrdena por volume recente (sinal de liquidez)
end_date_minData ISO — ignora mercados que serão resolvidos cedo demais
limitAté 500 por página (use offset para paginação)

Parte 5: O mapeamento condition_id → token_id

Este é o principal ponto de atrito ao criar bots para a Polymarket. Gamma retorna um condition_id (um por mercado). As negociações no CLOB usam um token_id (um por resultado). Você sempre precisa dos dois.

# Each Gamma market object contains 'clobTokenIds' - a JSON string array
import json

market = markets[0]
token_ids = json.loads(market['clobTokenIds'])   # ['7410...', '1120...']
yes_token = token_ids[0]   # First outcome
no_token  = token_ids[1]   # Second outcome

# Alternative: ask CLOB directly using condition_id
info = client.get_market(condition_id=market['conditionId'])
yes_token = info['tokens'][0]['token_id']

Atenção à ordem dos resultados

O array outcomes e o array clobTokenIds do Gamma são correspondidos por índice. Sempre leia o rótulo do resultado. Não presuma que o índice 0 é "Yes". Em mercados com múltiplos resultados (NegRisk, Oscar, eleições), o índice 0 pode ser "Kamala Harris" ou "Taylor Swift". A ordem é fixa, mas específica de cada mercado.

Parte 6: Lendo livros de ofertas

book = client.get_order_book(token_id=yes_token)

best_bid = float(book.bids[0].price) if book.bids else None
best_ask = float(book.asks[0].price) if book.asks else None
mid      = (best_bid + best_ask) / 2 if best_bid and best_ask else None
spread   = best_ask - best_bid if best_bid and best_ask else None

print(f"Bid {best_bid}  Ask {best_ask}  Mid {mid:.4f}  Spread {spread:.4f}")

Os livros de ofertas retornam como arrays ordenados (bids em ordem decrescente, asks em ordem crescente). Cada nível tem um price e um size. Para estimar o slippage em uma ordem maior, percorra o livro e some o notional até preencher o tamanho desejado.

Parte 6b: Os endpoints REST CLOB v2 (brutos, sem SDK)

O SDK encapsula esses endpoints, mas conhecer os endpoints brutos ajuda a depurar, usar outra linguagem ou criar um cliente leve. URL base: https://clob.polymarket.com. Todas as leituras abaixo são públicas — não exigem autenticação. Elas foram verificadas em produção em junho de 2026.

EndpointMethodWhat it returns
/marketsGETTodos os mercados (paginados via next_cursor). Inclui condition_id, tokens[], minimum_tick_size, neg_risk.
/sampling-marketsGETApenas mercados com livro de ofertas ativo — a forma mais rápida de encontrar token_ids negociáveis.
/book?token_id=GETLivro de ofertas completo: bids[] e asks[] com preço + tamanho.
/price?token_id=&side=buyGETMelhor preço para um lado. side não diferencia maiúsculas de minúsculas (buy/BUY). Retorna {"price":"0.14"}.
/midpoint?token_id=GET{"mid":"0.21"} — ponto médio entre a melhor oferta de compra e a melhor oferta de venda.
/spread?token_id=GET{"spread":"0.14"} — melhor oferta de venda menos a melhor oferta de compra.
/tick-size?token_id=GET{"minimum_tick_size":0.01} — o menor incremento de preço permitido para esse token.
/prices-history?market=&interval=GETPontos de preço históricos. interval = 1m,1h,6h,1d,1w,max.
/tradesGETNegociações recentes (com autenticação para as suas; público para o mercado).
/orderPOSTEnvia uma ordem assinada (autenticação obrigatória).
/orderDELETECancela uma ordem por id (autenticação).
/ordersGETSuas ordens abertas pendentes (autenticação).
/balance-allowance?asset_type=GETSeu saldo em USDC e a autorização on-chain (autenticação). Verifique antes de cada ordem.

Respostas verificadas, direto da API ativa:

$ curl "https://clob.polymarket.com/price?token_id=7347...&side=buy"
{"price":"0.14"}

$ curl "https://clob.polymarket.com/midpoint?token_id=7347..."
{"mid":"0.21"}

$ curl "https://clob.polymarket.com/spread?token_id=7347..."
{"spread":"0.14"}

$ curl "https://clob.polymarket.com/tick-size?token_id=7347..."
{"minimum_tick_size":0.01}

Cabeçalhos de autenticação L2 (para REST puro sem o SDK)

Endpoints de leitura são públicos. Para enviar ou cancelar ordens via REST puro, você assina cada requisição com suas credenciais de API. O SDK faz isso para você; veja o que ele monta internamente:

HeaderWhat it carries
POLY_ADDRESSO endereço da sua carteira de assinatura
POLY_API_KEYA chave de API de create_or_derive_api_creds()
POLY_PASSPHRASEA passphrase da mesma chamada de derivação
POLY_TIMESTAMPSegundos UNIX atuais (deve corresponder ao relógio do servidor — veja a dica de sincronização de relógio)
POLY_NONCENonce por requisição
POLY_SIGNATUREHMAC-SHA256 de timestamp + method + path + body, usando seu secret de API como chave, codificado em base64-url

Parte 7: Envio de ordens — compra e venda

Ordem limitada (GTC — o padrão)

from py_clob_client.clob_types import OrderArgs, OrderType

args = OrderArgs(
    token_id=yes_token,
    price=0.45,
    size=100,           # Shares, not dollars. 100 shares @ $0.45 = $45 max cost.
    side="BUY",
)
signed_order = client.create_order(args)
response = client.post_order(signed_order, OrderType.GTC)
print(response)

A chamada create_order assina uma mensagem estruturada EIP-712 com sua chave privada. Em seguida, post_order a envia para o CLOB. Você nunca envia chaves privadas brutas pela rede — apenas ordens assinadas.

Ajuste o preço ao incremento mínimo primeiro

Todo preço de ordem deve ser um múltiplo exato do minimum_tick_size do mercado (0,01 na maioria dos mercados, 0,001 nos mais precisos). Um preço fora do incremento permitido é rejeitado. Busque o incremento uma vez e arredonde para ele:

from py_clob_client.clob_types import OrderArgs, OrderType

tick = float(client.get_tick_size(token_id=yes_token))   # e.g. 0.01
def to_tick(p, tick): return round(round(p / tick) * tick, 4)

price = to_tick(0.453, tick)   # -> 0.45 on a 0.01 market

Compra

side="BUY", size é em cotas (não em dólares). 100 cotas a US$ 0,45 custam no máximo US$ 45 e pagam US$ 100 se o resultado vencer. O valor mínimo da ordem é de cerca de US$ 1.

buy = OrderArgs(token_id=yes_token, price=to_tick(0.45, tick), size=100, side="BUY")
resp = client.post_order(client.create_order(buy), OrderType.GTC)
print(resp)   # {'success': True, 'orderID': '0x...', 'status': 'live', ...}

Venda

Vender usa a mesma chamada com side="SELL". Você só pode vender cotas que já possui; tentar vender mais do que sua posição é rejeitado com um erro de "insufficient balance". Para encerrar uma posição, venda o mesmo token_id que você comprou.

sell = OrderArgs(token_id=yes_token, price=to_tick(0.62, tick), size=100, side="SELL")
resp = client.post_order(client.create_order(sell), OrderType.GTC)

Resumo dos parâmetros de ordem

FieldMeaningNotes
token_idO resultado que você está negociandoNão é condition_id; veja a Parte 5
sideBUY ou SELLBUY exige USDC; SELL exige cotas
price0,001-0,999Deve ser múltiplo do tamanho do tick
sizeNúmero de cotasValor mínimo da ordem de ~US$ 1; custo = preço x quantidade
tipo de ordemGTC / GTD / FOK / FAKPassado para post_order(...)

Juntando tudo: sua primeira negociação via API (um script executável)

Este é o fluxo completo, do início ao fim: conectar, encontrar um mercado líquido, ler o livro de ofertas, alinhar ao tick, enviar uma pequena ordem real e depois cancelá-la. Preencha seus dois segredos e execute. Na primeira execução, comece com um tamanho bem pequeno (alguns dólares).

import os, json, requests
from dotenv import load_dotenv
from py_clob_client.client import ClobClient
from py_clob_client.constants import POLYGON
from py_clob_client.clob_types import OrderArgs, OrderType

load_dotenv()

# 1) Connect (signing key in your EOA, funds in your proxy/funder)
client = ClobClient(
    "https://clob.polymarket.com",
    key=os.environ["POLY_PRIVATE_KEY"],
    chain_id=POLYGON,            # 137
    signature_type=1,            # 1 = email/Magic proxy, 2 = browser-wallet proxy
    funder=os.environ["POLY_FUNDER"],
)
client.set_api_creds(client.create_or_derive_api_creds())   # cache these after first run

# 2) Find the most-traded open market (Gamma, no auth)
m = requests.get(
    "https://gamma-api.polymarket.com/markets",
    params={"active": "true", "closed": "false", "order": "volume24hr",
            "ascending": "false", "limit": 1}, timeout=10,
).json()[0]
token_id = json.loads(m["clobTokenIds"])[0]   # index 0 = first outcome (read the label!)
print("Trading:", m["question"])

# 3) Read the book + the tick size
tick = float(client.get_tick_size(token_id))
book = client.get_order_book(token_id)
best_ask = float(book.asks[0].price)
print("best ask", best_ask, "| tick", tick)

# 4) Place a small BUY at the ask (tiny size to start)
price = round(round(best_ask / tick) * tick, 4)      # snap to tick
order = OrderArgs(token_id=token_id, price=price, size=5, side="BUY")  # 5 shares
resp = client.post_order(client.create_order(order), OrderType.GTC)
print(resp)                                          # {'success': True, 'orderID': '0x...', ...}

# 5) Cancel it (clean up)
# client.cancel(order_id=resp["orderID"])

Tipos de ordem

TypeCodeBehaviourWhen to use
Válida até cancelamentoGTCFica no livro de ofertas até ser executada ou até você cancelarPadrão. A maioria das estratégias de market making e de limite.
Válida até uma dataGTDÉ cancelada automaticamente em um timestamp especificadoOrientado a eventos: "cancelar 5 min antes do comunicado do Fed"
Executar ou cancelarFOKDeve executar o tamanho inteiro imediatamente ou ser totalmente canceladaPernas de arbitragem em que execuções parciais inviabilizam a operação
Executar e cancelarFAKExecuta o que conseguir no preço limite e cancela o restanteTomada agressiva de liquidez: funciona como uma ordem a mercado com limite de preço

Cancelamento

# Single order
client.cancel(order_id="0xabc...")

# Cancel all orders on a specific market
client.cancel_market_orders(market=market['conditionId'])

# Nuclear option: cancel everything
client.cancel_all()

Parte 8: Streaming via WebSocket

Consultar Gamma a cada segundo é desperdício, e você atingirá os limites de taxa rapidamente. O feed WebSocket transmite atualizações em tempo real do livro de ofertas e das negociações, com latência inferior a um segundo.

import json, websocket

WS_URL = "wss://ws-subscriptions-clob.polymarket.com/ws/market"

def on_open(ws):
    ws.send(json.dumps({
        "type": "market",
        "assets_ids": [yes_token, no_token],
    }))

def on_message(ws, message):
    event = json.loads(message)
    if event.get("event_type") == "price_change":
        print(f"{event['market']} {event['side']} {event['price']} size={event['size']}")

ws = websocket.WebSocketApp(
    WS_URL,
    on_open=on_open,
    on_message=on_message,
)
ws.run_forever(ping_interval=20)

Existem dois feeds. O feed /market carrega o livro de ofertas público e as negociações. O feed /user carrega seus próprios eventos de ordens e execuções (autenticado). Bots em produção se conectam aos dois, reconectam automaticamente em caso de desconexão e tratam o WebSocket como a fonte da verdade para o livro de ofertas atual.

Parte 9: Limites de taxa e backoff

Endpoint classLimitBurst
Envio de ordens (CLOB)~60/minuto por API key~10/segundo
Cancelamento de ordens~120/minuto~20/segundo
Leituras de dados de mercado (livro do CLOB)~300/minutomaior, varia
Gamma APIGeneroso; respeite 429s-
Mensagens WebSocketSem limite prático de entrada-

Quando você recebe um HTTP 429, o servidor retorna um cabeçalho Retry-After. Use backoff exponencial com jitter:

import random, time

def post_with_backoff(fn, *args, max_retries=6):
    for attempt in range(max_retries):
        try:
            return fn(*args)
        except Exception as e:
            if "429" in str(e):
                sleep = (2 ** attempt) + random.random()
                time.sleep(min(sleep, 30))
                continue
            raise
    raise RuntimeError("Too many retries")

Parte 10: Uma arquitetura de bot de referência

Todo bot confiável da Polymarket tem os mesmos seis componentes. Crie cada um como seu próprio módulo e mantenha-os com baixo acoplamento.

ComponentResponsibilityAPIs used
VarredorJob agendado: buscar mercados que atendam aos seus critérios (tags, volume, dias até o vencimento)Gamma
Motor de preçosManter livros de ordens locais em tempo real via WebSocketCLOB WS
Gerador de sinaisFunção pura: estado do livro + metadados → posição-alvo- (em memória)
Gerenciador de ordensComparar ordens atuais com o alvo e criar/cancelar o mínimo necessárioCLOB REST
Gerenciador de riscoAplicar limites por mercado, limites de perda diária e circuit breakers- (em memória + DB)
Logger e livro-razãoPersistir todas as decisões, fills e cancelamentos. Alimenta relatórios fiscais e depuração.SQLite / Postgres

Parte 11: Modos comuns de falha

  • Rejeição por preço fora do tick - o preço deve ser um múltiplo exato do minimum_tick_size do mercado. Busque esse valor via /tick-size?token_id= e arredonde antes de assinar, ou a ordem será rejeitada.
  • 404 "No orderbook exists" - você consultou /book, /price ou /midpoint em um token fechado/resolvido. Use /sampling-markets para encontrar tokens com livro ativo.
  • Dados obsoletos no WebSocket - acompanhe o horário da última mensagem por ativo; se não houver atualizações por >30s em um mercado ativo, force uma atualização via REST.
  • Colisões de nonce - py-clob-client gerencia os nonces das ordens para você, mas, se você estiver implementando seu próprio assinador, incremente o nonce a cada ordem.
  • Saldo insuficiente - sempre verifique o saldo de USDC antes de enviar; o livro pode exibir sua ordem, mas o matching vai rejeitá-la.
  • Mercado pausado ou em resolução - verifique market.active && !market.closed antes de negociar. As atualizações do Gamma ficam alguns segundos atrás do CLOB perto da resolução.
  • Incompatibilidade do adaptador NegRisk - mercados com múltiplos resultados passam por um adaptador NegRisk separado. O SDK lida com isso, mas confirme se sua ordem foi para o venue correto.

Parte 12: Recompensas de Liquidez via API

A Polymarket distribui ~US$ 5 milhões/mês em recompensas gerais de liquidez, além de mais de US$ 5 milhões/mês em recompensas específicas de esportes (consulte Recompensas de Liquidez). A maior parte vai para market makers orientados por API. Eles mantêm cotações ajustadas em ambos os lados em milhares de mercados.

A fórmula de recompensa favorece ordens próximas ao ponto médio, com tamanho e tempo no livro. Veja um loop mínimo de market making:

  1. Ler o livro de ordens do mercado-alvo
  2. Calcular um ponto médio justo (por exemplo, VWAP dos 3 primeiros níveis de cada lado)
  3. Publicar uma bid em mid − spread_target/2 e uma ask em mid + spread_target/2
  4. A cada atualização do WebSocket, reajustar o preço se sua cotação se afastar mais de um tick do alvo
  5. Cancelar e sair se o livro perder profundidade ou se surgir uma notícia relevante

Parte 13: Indo para produção

  • Hospedagem: uma VPS de US$ 6/mês (Hetzner, DigitalOcean) na Europa ou em US-East é suficiente para a maioria dos bots. Hospede próximo ao RPC da Polygon se você precisar de latência abaixo de 10 ms.
  • RPC: use Alchemy, Infura ou QuickNode para ter um RPC da Polygon confiável. Os planos gratuitos são suficientes até você começar a enviar centenas de ordens por minuto.
  • Monitoramento: Prometheus + Grafana para métricas; um bot do Telegram para alertas. Registre em log cada ID de ordem que você enviar e cada fill que receber.
  • Backups: persista o estado a cada minuto. Se a VPS cair no meio de um fill, você vai querer retomar em segundos, não reconciliar tudo manualmente.
  • Impostos: seu logger também é sua trilha de auditoria - consulte o Guia de Impostos.

Parte 14 - Dicas avançadas validadas para a API da Polymarket

Guia rápido Situação → Ação

SituationActionWhy
401 "invalid api key" na primeira chamadaVerifique se signature_type corresponde à origem da carteira e se funder é o endereço do proxyA incompatibilidade entre os tipos 1 e 2 causa 80% dos erros 401; o restante é EOA como funder
Ordens rejeitadas com "insufficient balance"Consulte /balance-allowance antes de cada ordem e reserve localmenteO CLOB reserva a garantia no instante em que você envia a ordem; duas ordens simultâneas podem comprometer o mesmo saldo
429 por throttling no endpoint /orderUse backoff com jitter: 2^attempt + random() limitado a 30sO Cloudflare aplica throttling em vez de rejeitar; retentativas ingênuas aumentam o backlog
WebSocket desconectado no meio da negociaçãoObtenha um snapshot do book via REST, reconcilie o estado local e depois assine novamenteOs deltas durante a interrupção são perdidos; o snapshot ressincroniza os níveis de preço
Ordem enviada, mas sem confirmação de fillConsulte /data/order/{id} em até 5s; se estiver pendente, aguarde; se não for encontrada, substituaÉ raro, mas recuperável; por padrão, “verifique o estado e só então aja”
Mercado resolvido enquanto há cotação ativaCancele todas as ordens abertas desse conditionId no evento de resoluçãoOrdens pós-resolução podem permanecer como fills zumbis se peculiaridades do adapter forem acionadas
Executando um bot de market-makingCote a até 2 centavos do midpoint com tamanho de 100+ sharesA fórmula de recompensa pondera proximidade + tamanho + tempo no book; cotação próxima + tamanho + persistência vencem
Executando um bot de arbitragem em multi-outcomeUse FOK para cada perna, não GTCFills parciais na perna A com uma perna B completa = exposição sem hedge e perda imediata
Primeira vez criando um botConstrua primeiro o scanner, depois o mecanismo de preço e então o sinal — nunca comece pelo sinalSinais sem um estado de book limpo são armadilhas de correlação; faça os fluxos funcionarem primeiro
Bot de produção caiu às 3hTenha reinício automático com systemd + alerta no Telegram + estado persistenteQualquer bot sem supervisão vai cair; a única questão é se ele reinicia corretamente

Próximos passos

Verificação rápida