要点

Polymarket には 3つの公開 API(API とは、プログラムが自動で情報を読み取ったり取引を発注したりするための仕組みです)があります。CLOB(取引)、Gamma(マーケットの検出)、Data(分析)です。公式の Python SDK は py-clob-client 0.34.6 です。認証には API キー + ECDSA 署名を使い、注文は Polygon のプロキシウォレットを介して EIP-712 で署名されます。レート制限は、キーごとにおおむね1分あたり60件の注文です。新しく開発を始める人が最もつまずきやすいのは、Gamma と CLOB の間にある condition_id → token_id のマッピング問題です。まずこれを解決すれば、その後の実装はスムーズに進みます。Polymarket では、流動性報酬とボットが獲得するスプレッドを合わせて、月間約4,000万ドルが得られており、そのほぼすべてを API ユーザーが獲得しています。

パート 1: 3つの API

Polymarket では、役割ごとに3つのサービスが明確に分かれています。用途に合った API を使うことで、ボットを高速かつシンプルに保ち、レート制限の範囲内で運用できます。

APIBase URLPurposeAuth Required
CLOB APIclob.polymarket.com注文の発注、キャンセル、追跡。オーダーブックの読み取り。ポジションの照会。必要(取引時)
Gamma APIgamma-api.polymarket.comマーケットの閲覧、メタデータ、画像、結果価格、出来高、期限、タグの取得。不要(公開)
Data APIdata-api.polymarket.com過去の取引、ポジションのスナップショット、ユーザー分析、リーダーボードデータ。不要(公開)

典型的なボットのループでは、Gamma でマーケットを探し、CLOB でオーダーブックを取得して取引を発注し、Data で戦略のパフォーマンスをオフラインでバックテストします。Gamma は「カタログ」、CLOB は「取引所」、Data は「データウェアハウス」と考えるとよいでしょう。

パート 2: 認証とプロキシウォレットモデル

Polymarket は、メインウォレットの秘密鍵で取引に署名するわけではありません。代わりに、Gnosis Safe 形式のプロキシウォレットを使用します。メインウォレットがプロキシを承認し、そのプロキシが Polygon 上ですべての取引を実行します。API ボットはそのプロキシとやり取りします。

必要なもの

  • API キー - Polymarket Settings → Developer で生成します
  • 秘密鍵 - 取引用ウォレットの鍵(メインの MetaMask シードフレーズではありません)
  • Funder アドレス - プロキシウォレットのアドレス(Settings → Wallet に表示されます)
  • Chain ID - 137(Polygon メインネット)
  • Signature type - 1(POLY_PROXY、一般ユーザー向けの標準)

パート 3: py-clob-client のインストール

公式の Python SDK を使うのが、最初の注文まで最短で到達する方法です。ここでは、PyPI の現行リリース(2026 年 2 月時点)であり、稼働中の bot のほぼすべてで使われているバージョン 0.34.6 を使用します。

# 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

基本的なクライアント設定

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())

create_or_derive_api_creds() 呼び出しは、あなたの秘密鍵でメッセージに署名します。その署名を使って、API キー、シークレット、パスフレーズを発行します。初回実行後はこれらを .env にキャッシュしておくと、起動のたびに derive endpoint を呼ばずに済みます。

パート 4: Gamma でマーケットを見つける

取引を始める前に、取引する価値のあるマーケットを見つける必要があります。Gamma は、Polymarket UI に表示される内容をすべて含む JSON を返します。質問、結果候補、価格、24 時間出来高、期限、タグ、画像などです。

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}")

便利な Gamma クエリパラメータ

ParameterWhat it does
tag_slugカテゴリ(政治、スポーツ、暗号資産、カルチャーなど)で絞り込みます
active=true現在取引を受け付けているマーケットだけを対象にします
closed=false解決済みのマーケットを非表示にします
order=volume24hr直近の出来高で並べ替えます(流動性の目安)
end_date_minISO 日付 - 解決が近すぎるマーケットを除外します
limit1 ページあたり最大 500 件(ページングには offset を使用)

パート 5: condition_id → token_id のマッピング

これは Polymarket bot 構築で最大のつまずきどころです。Gamma は condition_id(マーケットごとに 1 つ)を返します。一方、CLOB の取引では token_id(結果候補ごとに 1 つ)を使います。常に両方が必要です。

# 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']

結果候補の並び順に関する注意点

Gamma の outcomes 配列と clobTokenIds 配列は、同じインデックス同士が対応しています。結果候補のラベルは必ず確認してください。インデックス 0 が "Yes" だと決めつけないでください。複数の結果候補があるマーケット(NegRisk、Oscars、選挙など)では、インデックス 0 が "Kamala Harris" や "Taylor Swift" の場合もあります。順序は固定されていますが、マーケットごとに異なります。

パート 6: 注文板を読む

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}")

注文板はソート済みの配列として返されます(買い注文は降順、売り注文は昇順)。各レベルには pricesize があります。大きめの注文でスリッページを見積もるには、板を順にたどり、目標サイズを満たすまで金額を積み上げます。

パート 6b: CLOB v2 REST Endpoints(raw、SDK なし)

SDK はこれらをラップしていますが、raw endpoints を知っておくと、デバッグしたり、別の言語を使ったり、薄いクライアントを構築したりできます。Base URL: https://clob.polymarket.com。以下の読み取り系はすべて公開 API で、認証は不要です。これらは 2026 年 6 月に live 環境で確認済みです。

EndpointMethodWhat it returns
/marketsGETすべてのマーケット(next_cursor によるページネーション)。condition_idtokens[]minimum_tick_sizeneg_risk を含みます。
/sampling-marketsGET稼働中の注文板があるマーケットのみ。取引可能な token_id を見つける最速の方法です。
/book?token_id=GET完全な注文板。価格とサイズを含む bids[]asks[]
/price?token_id=&side=buyGET指定したサイドの最良価格。side は大文字小文字を区別しません(buy/BUY)。{"price":"0.14"} を返します。
/midpoint?token_id=GET{"mid":"0.21"} - 最良買い気配と最良売り気配の中間値。
/spread?token_id=GET{"spread":"0.14"} - 最良売り気配から最良買い気配を引いた値。
/tick-size?token_id=GET{"minimum_tick_size":0.01} - そのトークンで許可される最小の価格刻み。
/prices-history?market=&interval=GET過去の価格ポイント。interval = 1m,1h,6h,1d,1w,max
/tradesGET直近の取引(自分の取引には認証が必要、マーケットの取引は公開)。
/orderPOST署名済み注文を発注します(認証が必要)。
/orderDELETEid で 1 件の注文をキャンセルします(認証)。
/ordersGETあなたの未約定のオープン注文(認証)。
/balance-allowance?asset_type=GETあなたの USDC 残高とオンチェーンの allowance(認証)。各注文の前に確認してください。

稼働中の API から直接確認したレスポンス:

$ 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}

L2 認証ヘッダー(SDK を使わず raw REST を使う場合)

読み取りエンドポイントは公開されています。raw REST 経由で注文を発注またはキャンセルするには、API 認証情報で各リクエストに署名します。SDK はこれを自動で行います。内部で生成される内容は次のとおりです。

HeaderWhat it carries
POLY_ADDRESS署名に使うウォレットアドレス
POLY_API_KEYcreate_or_derive_api_creds() から取得した API キー
POLY_PASSPHRASE同じ derive 呼び出しから取得したパスフレーズ
POLY_TIMESTAMP現在の UNIX 秒(サーバー時刻と一致している必要があります。時刻同期のヒントを参照)
POLY_NONCEリクエストごとの nonce
POLY_SIGNATUREtimestamp + method + path + body の HMAC-SHA256。API の secret をキーとして使用し、base64-url エンコードしたもの

パート 7: 注文の発注 - Buy と Sell

指値注文(GTC - デフォルト)

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)

create_order 呼び出しは、秘密鍵で EIP-712 構造化メッセージに署名します。その後、post_order がそれを CLOB に送信します。秘密鍵そのものをネットワーク経由で送信することはなく、送信されるのは署名済み注文だけです。

先に価格を tick に合わせる

すべての注文価格は、マーケットの minimum_tick_size の正確な倍数である必要があります(多くのマーケットでは 0.01、細かい刻みのマーケットでは 0.001)。tick に合っていない価格は拒否されます。tick を一度取得し、それに合わせて丸めてください。

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

Buy

side="BUY"size株数(ドルではありません)です。0.45ドルで 100 株を購入する場合、最大コストは 45ドルで、結果が勝ちなら 100ドルが支払われます。最小注文額は約 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', ...}

Sell

売りも同じ呼び出しで、side="SELL"を指定します。売却できるのは、すでに保有しているシェアだけです。ポジションを超えて売ろうとすると、"insufficient balance" エラーで拒否されます。ポジションをクローズするには、購入したものと同じtoken_idを売却します。

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)

注文パラメータの早見表

FieldMeaningNotes
token_id取引するアウトカムcondition_idではありません。Part 5を参照してください
sideBUYまたはSELLBUYにはUSDCが必要です。SELLにはシェアが必要です
price0.001〜0.999tick sizeの倍数である必要があります
sizeシェア数最小注文額は約1ドル。コスト = price × size
order typeGTC / GTD / FOK / FAKpost_order(...)に渡します

まとめ: 最初のAPI取引(そのまま実行できるスクリプト)

ここでは、接続、流動性のあるマーケットの検索、板情報の読み取り、tickへの合わせ込み、小さな実注文の発注、そしてキャンセルまで、エンドツーエンドの流れを示します。2つのシークレットを入力して実行してください。初回はごく小さなサイズ(数ドル程度)から始めましょう。

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"])

Order types

TypeCodeBehaviourWhen to use
キャンセルまで有効GTC約定するか、自分でキャンセルするまで板に残りますデフォルト。多くのマーケットメイキング戦略や指値戦略で使います。
指定日時まで有効GTD指定したtimestampで自動的にキャンセルされますイベント駆動型: 「Fed発表の5分前にキャンセル」
全量即時約定または取消FOK全数量がただちに約定しなければ、注文全体がキャンセルされます部分約定で取引が崩れる裁定取引のレッグ
即時約定可能分のみ約定FAK指値価格で約定できる分だけ約定し、残りはキャンセルされます積極的に取りに行く場合。価格上限付きの成行注文のように機能します

キャンセル

# 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()

Part 8: WebSocket Streaming

Gammaを毎秒ポーリングするのは無駄が多く、すぐにレート制限に達します。WebSocketフィードでは、リアルタイムの板情報と取引更新が1秒未満のレイテンシでストリーミングされます。

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)

フィードは2種類あります。/marketフィードは公開の板情報と取引を配信します。/userフィードは、自分の注文と約定イベントを配信します(認証が必要)。本番環境のbotは両方に接続し、切断時には自動で再接続し、現在の板の信頼できる情報源としてWebSocketを扱います。

Part 9: Rate Limits & Backoff

Endpoint classLimitBurst
注文発注(CLOB)APIキーあたり約60件/分約10件/秒
注文キャンセル約120件/分約20件/秒
マーケットデータ読み取り(CLOB book)約300件/分より高め、変動あり
Gamma API余裕はありますが、429には従ってください-
WebSocketメッセージ受信側に実質的な制限なし-

HTTP 429に達すると、サーバーはRetry-Afterヘッダーを返します。ジッター付き指数バックオフを使用してください。

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")

Part 10: A Reference Bot Architecture

信頼性の高いPolymarket botは、いずれも同じ6つのコンポーネントで構成されます。それぞれを独立したモジュールとして作成し、疎結合に保ちます。

ComponentResponsibilityAPIs used
スキャナースケジュール実行ジョブ: 条件(タグ、出来高、満期までの日数)に合うマーケットを取得するGamma
価格エンジンWebSocket でローカルの注文板をリアルタイムに維持するCLOB WS
シグナル生成純粋関数: 板の状態 + メタデータ → 目標ポジション-(メモリ内)
注文管理現在の注文と目標を差分比較し、必要最小限の発注・キャンセルを行うCLOB REST
リスク管理マーケットごとの上限、日次損失上限、サーキットブレーカーを適用する-(メモリ内 + DB)
ロガー & 台帳すべての判断、約定、キャンセルを永続化する。税務レポートとデバッグに利用する。SQLite / Postgres

パート11: よくある障害パターン

  • ティックに合わない価格による拒否 - 価格はマーケットの minimum_tick_size の正確な倍数である必要があります。署名する前に /tick-size?token_id= で取得して丸めてください。そうしないと注文は拒否されます。
  • 404 "No orderbook exists" - クローズ済みまたは解決済みのトークンに対して /book/price、または /midpoint を照会しています。ライブの板があるトークンを見つけるには /sampling-markets を使用してください。
  • 古い WebSocket データ - アセットごとに最後のメッセージ時刻を追跡します。アクティブなマーケットで 30 秒超更新がなければ、REST で強制的に再取得してください。
  • Nonce の衝突 - py-clob-client は注文の nonce を自動で処理しますが、独自の署名処理を実装している場合は、注文ごとに nonce をインクリメントしてください。
  • 残高不足 - 発注前に必ず USDC 残高を確認してください。板に注文が表示されても、マッチング時に拒否されることがあります。
  • マーケットの一時停止または解決処理中 - 取引前に market.active && !market.closed を確認してください。解決前後では、Gamma の更新が CLOB より数秒遅れることがあります。
  • NegRisk アダプターの不一致 - 複数結果のマーケットは別の NegRisk アダプター経由でルーティングされます。SDK が処理しますが、注文が正しい取引先に送られたことを確認してください。

パート12: API による流動性報酬

Polymarket は、一般向け流動性報酬として月間約500万ドル、さらにスポーツ専用報酬として月間500万ドル超を提供しています(Liquidity Rewards を参照)。その大半は API 駆動のマーケットメーカーに流れます。彼らは数千のマーケットで、タイトな両サイドの気配を維持しています。

報酬計算式では、ミッドポイントに近く、サイズがあり、板に長く残る注文が優遇されます。最小限のマーケットメイクループは次のとおりです。

  1. 対象マーケットの注文板を読み取る
  2. 公正なミッドポイントを計算する(例: 両サイド上位 3 レベルの VWAP)
  3. mid − spread_target/2 に買い気配を、mid + spread_target/2 に売り気配を出す
  4. WebSocket 更新のたびに、気配が目標から 1 ティック超ずれていれば価格を更新する
  5. 板が薄くなった場合やニュースが出た場合は、キャンセルして退出する

パート13: 本番運用へ移行する

  • ホスティング: ほとんどのボットでは、ヨーロッパまたは US-East の月額6ドルの VPS(Hetzner、DigitalOcean)で十分です。10ms 未満のレイテンシが必要な場合は、Polygon RPC と同じ場所に配置してください。
  • RPC: 信頼性の高い Polygon RPC には Alchemy、Infura、または QuickNode を使用します。1 分あたり数百件の注文を出すまでは、無料プランで十分です。
  • 監視: メトリクスには Prometheus + Grafana、アラートには Telegram ボットを使います。送信したすべての注文 ID と、受信したすべての約定をログに記録してください。
  • バックアップ: 状態を毎分永続化します。VPS が約定途中で停止した場合でも、手作業で照合するのではなく、数秒で再開できるようにします。
  • 税務: ロガーは監査証跡にもなります。Tax Guide を参照してください。

第14部 - Polymarket API の実運用で検証済みのプロ向けTips

状況 → 対応チートシート

SituationActionWhy
初回呼び出しで 401 "invalid api key" が返るsignature_type がウォレットの由来と一致していること、funder がプロキシアドレスであることを確認する401 エラーの 80% は Type 1 と 2 の不一致が原因で、残りは EOA を funder にしていることが原因です
注文が "insufficient balance" で拒否される各注文の前に /balance-allowance を照会し、ローカルで残高を予約するCLOB は注文を投稿した瞬間に担保を予約します。同時に 2 件の注文を出すと、同じ残高を二重に使おうとする可能性があります
/order エンドポイントで 429 スロットリングが発生するジッター付きでバックオフする: 2^attempt + random()、上限は 30秒Cloudflare は拒否ではなくスロットリングします。単純なリトライはバックログを増幅させます
取引中に WebSocket が切断されたREST で板のスナップショットを取得し、ローカル状態を照合してから再購読する切断中の差分は失われます。スナップショットにより価格ラダーを再同期できます
注文は出したが約定確認がない5秒以内に /data/order/{id} を照会する。pending なら待機し、not found なら出し直すまれですが復旧可能です。基本は「状態を確認してから行動する」です
アクティブな気配提示中にマーケットが解決した解決イベントを受けたら、その conditionId の未約定注文をすべてキャンセルするアダプターの挙動によっては、解決後の注文がゾンビ約定として残ることがあります
マーケットメイキングボットを運用しているmidpoint から 2セント以内、かつ 100 株以上のサイズで気配を提示する報酬式では、タイトさ + サイズ + 板上の滞在時間が重視されます。タイトで、サイズがあり、継続的な気配が有利です
複数アウトカムでアービトラージボットを運用している各レッグには GTC ではなく FOK を使うレッグ A が部分約定し、レッグ B が全約定すると、ヘッジされていないエクスポージャーとなり即時損失につながります
初めてボットを作るまずスキャナー、次に価格エンジン、その後にシグナルを作る。シグナルを最初に作ってはいけないクリーンな板状態がないシグナルは相関の罠になります。まずパイプラインを動かしましょう
本番ボットが午前3時にクラッシュしたsystemd の自動再起動 + Telegram アラート + 永続状態を用意しておく無人運用のボットはいずれ必ずクラッシュします。問題は、きれいに再起動できるかどうかだけです

次に読むもの

  • ツールとリソース - API を補完するサードパーティのダッシュボード、分析、データフィード
  • 高度な戦略 - ボットに適したマルチレッグアービトラージやオプション風の構成
  • 流動性報酬 - マーケットメイキングのリベートを獲得するための正確な計算式
  • 板ガイド - コードを書く前に、板の読み方をより深く理解する
  • 用語集 - このガイドに登場するすべての用語を平易な英語で説明

クイックチェック