polymarket_us/lib.rs
1//! Unofficial Rust SDK for the Polymarket US Retail API.
2//!
3//! The crate exposes a typed async REST client and a managed WebSocket stream.
4//! Requests to authenticated endpoints are signed with Ed25519 and carry the
5//! `X-PM-*` headers automatically.
6//!
7//! # Getting started
8//!
9//! Public endpoints need no credentials:
10//!
11//! ```no_run
12//! use polymarket_us::PolymarketUsClient;
13//!
14//! # async fn run() -> Result<(), polymarket_us::PolymarketUsError> {
15//! let client = PolymarketUsClient::builder().build()?;
16//! let markets = client.markets().list().await?;
17//! println!("{} markets", markets.markets.len());
18//! # Ok(())
19//! # }
20//! ```
21//!
22//! Authenticated endpoints read credentials from `POLYMARKET_US_KEY_ID` and
23//! `POLYMARKET_US_SECRET_KEY`:
24//!
25//! ```no_run
26//! use polymarket_us::{PolymarketUsClient, UsAuth};
27//!
28//! # async fn run() -> Result<(), polymarket_us::PolymarketUsError> {
29//! let client = PolymarketUsClient::builder()
30//! .auth(UsAuth::from_env()?)
31//! .build()?;
32//!
33//! let balances = client.account().balances().await?;
34//! # Ok(())
35//! # }
36//! ```
37//!
38//! # Resources
39//!
40//! Endpoints are grouped behind accessors on the client: [`PolymarketUsClient::markets`],
41//! [`PolymarketUsClient::events`], [`PolymarketUsClient::orders`],
42//! [`PolymarketUsClient::account`], [`PolymarketUsClient::portfolio`], and
43//! [`PolymarketUsClient::search`].
44//!
45//! # Retries
46//!
47//! Idempotent requests (`GET`, `DELETE`) are retried with exponential backoff and
48//! jitter, honouring a server-supplied `Retry-After`. `POST` is **never** retried
49//! automatically, so a submitted order cannot be duplicated by the transport
50//! layer. See [`RetryConfig`].
51//!
52//! # Streaming
53//!
54//! The venue splits its WebSocket surface across two sockets, and the SDK
55//! mirrors that split rather than multiplexing them:
56//!
57//! | Data | Endpoint | Client |
58//! |---|---|---|
59//! | Books, trades, best-bid/offer | `wss://api.polymarket.us/v1/ws/markets` | [`MarketStreamClient`] |
60//! | Orders, positions, balances | `wss://api.polymarket.us/v1/ws/private` | [`PrivateStreamClient`] |
61//!
62//! Each client maintains its connection with automatic reconnect, replaying its
63//! subscriptions every time. Connections that go silent are torn down after
64//! [`StreamConnectConfig::idle_timeout`] so a dead socket cannot stall the
65//! stream indefinitely, and a keepalive ping keeps a quiet market from tripping
66//! that check.
67//!
68//! ```no_run
69//! use polymarket_us::{MarketStreamClient, MarketSubscription};
70//!
71//! # async fn run() -> Result<(), polymarket_us::PolymarketUsError> {
72//! let client = MarketStreamClient::new(None);
73//!
74//! let mut stream = client
75//! .connect(vec![MarketSubscription::market_data(["btc-100k-2025"])])
76//! .await?;
77//!
78//! while let Some(message) = stream.next().await {
79//! println!("{:?}", message.kind);
80//! }
81//! # Ok(())
82//! # }
83//! ```
84
85pub mod auth;
86pub mod client;
87pub mod error;
88pub mod resources;
89pub mod retry;
90pub mod stream;
91pub mod types;
92
93pub use auth::UsAuth;
94pub use client::{PolymarketUsClient, PolymarketUsClientBuilder};
95pub use error::PolymarketUsError;
96pub use resources::{
97 AccountClient, EventsClient, MarketsClient, OrdersClient, PortfolioClient, SearchClient,
98};
99pub use retry::RetryConfig;
100pub use stream::{
101 MarketStream, MarketStreamClient, MarketSubscription, PrivateStream, PrivateStreamClient,
102 PrivateSubscription, ReconnectConfig, StreamConnectConfig, StreamControlEvent, StreamDataEvent,
103 StreamEndpoint, StreamMessage, StreamMessageKind, Subscription, SubscriptionType,
104};
105pub use types::{MarketStatus, OrderAction, OrderSide, OrderType, TimeInForce};