polyoxide 0.17.0

Unified Rust client for Polymarket APIs (CLOB, Gamma and Data)
Documentation

polyoxide

Unified Rust client for Polymarket APIs, re-exporting the CLOB (trading), Gamma (market data), and Data (user positions/trades) crates behind feature flags.

More information about this crate can be found in the crate documentation.

Installation

By default, all three REST API modules are enabled:

cargo add polyoxide

Select only what you need:

# Market data only
cargo add polyoxide --no-default-features --features gamma

# Trading only
cargo add polyoxide --no-default-features --features clob

# User data only
cargo add polyoxide --no-default-features --features data

# Everything including WebSocket
cargo add polyoxide --no-default-features --features full

Feature flags

Feature Default Description
clob yes CLOB order-book trading via polyoxide-clob
gamma yes Read-only market data via polyoxide-gamma
data yes Read-only user positions/trades via polyoxide-data
ws no WebSocket streaming (implies clob)
full no Enables clob + gamma + data + ws
keychain no OS keychain credential storage; off by default

Usage

Unified client (all three features enabled)

When clob, gamma, and data are all enabled, the Polymarket struct provides a single entry point. It requires an Account for authenticated CLOB operations.

use polyoxide::prelude::*;
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
let account = Account::from_env()?;

let pm = Polymarket::builder(account)
    .chain(Chain::PolygonMainnet)
    .build()?;

// Gamma: list open markets
let markets = pm.gamma.markets()
    .list()
    .open(true)
    .limit(10)
    .send()
    .await?;

// Data: list a user's positions
let positions = pm.data
    .user("0xabc...")
    .list_positions()
    .limit(5)
    .send()
    .await?;

for pos in &positions {
    println!("{}: {} shares @ {}", pos.title, pos.size, pos.cur_price);
}

// CLOB: place a limit order
let params = CreateOrderParams {
    token_id: "token_id".into(),
    price: 0.52,
    size: 100.0,
    side: OrderSide::Buy,
    order_type: OrderKind::Gtc,
    post_only: false,
    expiration: None,
    funder: None,
    signature_type: Some(SignatureType::PolyProxy),
};

let response = pm.clob.place_order(&params, None).await?;
println!("Order placed: {:?}", response);
# Ok(())
# }

Using individual crates directly

Each sub-client can be used standalone without the unified Polymarket wrapper.

// Read-only Gamma client (no auth required)
use polyoxide::polyoxide_gamma::Gamma;
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
let gamma = Gamma::builder().build()?;
let events = gamma.events().list().limit(5).send().await?;
# Ok(())
# }
// Read-only Data API client (no auth required)
use polyoxide::polyoxide_data::DataApi;
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
let data = DataApi::builder().build()?;
let leaders = data.leaderboard().get().send().await?;
# Ok(())
# }
// Public (unauthenticated) CLOB client for read-only market data
use polyoxide::polyoxide_clob::Clob;
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
let clob = Clob::public();
let book = clob.markets().order_book("token_id").send().await?;
# Ok(())
# }

WebSocket (requires ws feature)

use polyoxide::prelude::*;
use futures_util::StreamExt;
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
let mut stream = ws::WebSocket::connect_market(vec![
    "token_id".to_string(),
]).await?;

while let Some(msg) = stream.next().await {
    match msg? {
        ws::Channel::Market(ws::MarketMessage::Book(book)) => {
            println!("Book: {} bids, {} asks", book.bids.len(), book.asks.len());
        }
        ws::Channel::Market(ws::MarketMessage::PriceChange(pc)) => {
            println!("Price changes: {:?}", pc.price_changes);
        }
        _ => {}
    }
}
# Ok(())
# }

For long-running connections, WebSocketBuilder provides automatic keep-alive pings:

use polyoxide::prelude::*;
use std::time::Duration;
# async fn doctest() -> Result<(), Box<dyn std::error::Error>> {
let stream = ws::WebSocketBuilder::new()
    .ping_interval(Duration::from_secs(10))
    .connect_market(vec!["token_id".to_string()])
    .await?;

stream.run(|msg| async move {
    println!("{:?}", msg);
    Ok(())
}).await?;
# Ok(())
# }

Environment variables

Authenticated operations (CLOB trading) require:

Variable Description
POLYMARKET_PRIVATE_KEY Hex-encoded private key
POLYMARKET_API_KEY L2 API key
POLYMARKET_API_SECRET L2 API secret (base64)
POLYMARKET_API_PASSPHRASE L2 API passphrase

Account::from_env() reads all four. Alternatively, use Account::new(private_key, credentials) or Account::from_file(path) for file-based config. With the keychain feature enabled, Account::from_keychain() loads the same credentials from the OS keychain instead of environment variables.

License

Licensed under either of MIT or Apache-2.0 at your option.