Standalone Elixir client for the Polymarket CLOB API.
This package is being built as the first publishable version of polymarket_clob.
It targets Polymarket's current exchange model and starts from fixture-proven auth and
order-signing primitives before adding higher-level HTTP, order construction, and
WebSocket APIs.
Implemented:
- current CLOB contract address config for Polygon and Amoy
- client struct with address derivation and signature type defaults
- L1 ClobAuth signing with
nonce - L2 HMAC request signing
- HTTP wrapper with public/private helpers and L2 header injection
- read-only market-data and account endpoint wrappers
- write endpoint wrappers for posting already-built signed orders and cancellations
- float-based order rounding and local limit/market order builders
- order EIP-712 typed data, order hash, and signature generation
- deterministic parity fixtures generated from
Polymarket/py-clob-client-v2
Not implemented yet:
- allowance updates and write endpoints beyond already-built order posting/cancellation
- WebSocket clients
client =
PolymarketClob.Client.new(
private_key: System.fetch_env!("POLY_PRIVATE_KEY"),
api_key: System.get_env("POLY_API_KEY"),
api_secret: System.get_env("POLY_API_SECRET"),
api_passphrase: System.get_env("POLY_API_PASSPHRASE")
)If API credentials are omitted, the client defaults to EOA signature type 0. If all API
credential fields are present, it defaults to Polymarket Gnosis Safe signature type 2.
Partial API credentials raise.
{:ok, book} =
PolymarketClob.API.MarketData.get_order_book("1234567890")
{:ok, price} =
PolymarketClob.API.MarketData.get_price("1234567890", :buy)
{:ok, market_info} =
PolymarketClob.API.MarketData.get_clob_market_info("0xconditionid"){:ok, balance} =
PolymarketClob.API.Account.get_balance_allowance(client,
asset_type: "CONDITIONAL",
token_id: "1234567890"
)
{:ok, open_orders_page} =
PolymarketClob.API.Account.get_open_orders(client,
market: "0xconditionid",
next_cursor: "MA=="
)
{:ok, trades_page} =
PolymarketClob.API.Account.get_trades(client,
market: "0xconditionid",
next_cursor: "MA=="
)Private GET requests sign the base endpoint path, matching the official Python client, while query params are still appended to the URL sent to the CLOB.
signed_order =
PolymarketClob.Order.Builder.build_limit_order(
client,
"1234567890",
10.0,
0.55,
:buy,
tick_size: "0.01",
neg_risk: false
)This only builds and signs the order locally. To post an already-built signed order:
{:ok, response} =
PolymarketClob.API.Orders.post_order(client, signed_order)Audited against Polymarket/py-clob-client-v2 commit
a2ec069fb66096e2b80ff0b7fdf628fc41d6352c.
Coverage as of the audited commit: 44 of 50 candidate endpoints implemented
(rewards, RFQ, and builder surface are intentionally skipped — see
ENDPOINT_COVERAGE.md).
Implemented public read-only endpoints:
GET /okGET /versionGET /timeGET /marketsGET /simplified-marketsGET /sampling-marketsGET /sampling-simplified-marketsGET /markets/:condition_idGET /clob-markets/:condition_idGET /markets-by-token/:token_idGET /markets/live-activity/:condition_idGET /book?token_id=...POST /booksGET /tick-size?token_id=...GET /neg-risk?token_id=...GET /fee-rate?token_id=...GET /midpoint?token_id=...POST /midpointsGET /price?token_id=...&side=...POST /pricesGET /spread?token_id=...POST /spreadsGET /last-trade-price?token_id=...POST /last-trades-pricesGET /prices-history?...
Implemented private read-only endpoints:
GET /auth/api-keysGET /auth/ban-status/closed-onlyGET /data/order/:order_idGET /data/orders?...GET /data/pre-migration-orders?...GET /data/trades?...(also exposed asAccount.get_trades_paginated/3)GET /order-scoring?order_id=...GET /balance-allowance?...GET /notifications?signature_type=...
Implemented private write endpoints:
POST /orderwith an already-built signed order mapPOST /orderswith already-built signed order mapsPOST /orders-scoringwith a list of order IDsDELETE /orderwith an order IDDELETE /orderswith order IDsDELETE /cancel-allDELETE /cancel-market-orderswithmarketand/orasset_idGET /balance-allowance/update?...DELETE /notifications?ids=...
Not implemented yet:
- L1/L2 API key lifecycle (
create_api_key,derive_api_key,delete_api_key, readonly key management) — seeENDPOINT_COVERAGE.mdfor the gating product/domain decisions - reward, RFQ, builder, and WebSocket endpoints (intentionally out of scope for
this package; see
ENDPOINT_COVERAGE.md) - bot integration
Intentional differences from the Python client:
get_open_orders/3,get_trades/3, andget_pre_migration_orders/3are direct page wrappers. They do not auto-paginate yet.get_prices_history/2accepts caller-supplied CLOB query names such as:market,:startTs,:endTs,:fidelity, and:interval; it does not validate interval combinations yet.get_fee_rate/2is exposed for legacy compatibility; order-building work should preferget_clob_market_info/2fee data when fee-aware helpers are added.
After publication, add polymarket_clob to your dependencies:
def deps do
[
{:polymarket_clob, "~> 0.1.0"}
]
endThe checked-in fixtures under test/fixtures/parity were generated from the official Python
v2 client at commit a2ec069fb66096e2b80ff0b7fdf628fc41d6352c:
PY_CLOB_CLIENT_V2_PATH=/path/to/py-clob-client-v2 \
python3 scripts/generate_parity_fixtures.pyThe script defaults to /tmp/py-clob-client-v2, which is useful for local scratch
checkouts during development.