Skip to main content

polymarket_client_sdk/data/
client.rs

1//! Client for the Polymarket Data API.
2//!
3//! This module provides an HTTP client for interacting with the Polymarket Data API,
4//! which offers endpoints for querying user positions, trades, activity, and market data.
5//!
6//! # Example
7//!
8//! ```no_run
9//! use polymarket_client_sdk::types::address;
10//! use polymarket_client_sdk::data::{Client, types::request::PositionsRequest};
11//!
12//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
13//! let client = Client::default();
14//!
15//! let request = PositionsRequest::builder()
16//!     .user(address!("56687bf447db6ffa42ffe2204a05edaa20f55839"))
17//!     .build();
18//!
19//! let positions = client.positions(&request).await?;
20//! for position in positions {
21//!     println!("{}: {} tokens", position.title, position.size);
22//! }
23//! # Ok(())
24//! # }
25//! ```
26
27use reqwest::{
28    Client as ReqwestClient, Method,
29    header::{HeaderMap, HeaderValue},
30};
31use serde::Serialize;
32use serde::de::DeserializeOwned;
33use url::Url;
34
35use super::types::request::{
36    ActivityRequest, BuilderLeaderboardRequest, BuilderVolumeRequest, ClosedPositionsRequest,
37    HoldersRequest, LiveVolumeRequest, OpenInterestRequest, PositionsRequest, TradedRequest,
38    TraderLeaderboardRequest, TradesRequest, ValueRequest,
39};
40use super::types::response::{
41    Activity, BuilderLeaderboardEntry, BuilderVolumeEntry, ClosedPosition, Health, LiveVolume,
42    MetaHolder, OpenInterest, Position, Trade, Traded, TraderLeaderboardEntry, Value,
43};
44use crate::{Result, ToQueryParams as _};
45
46/// HTTP client for the Polymarket Data API.
47///
48/// Provides methods for querying user positions, trades, activity, market holders,
49/// open interest, volume data, and leaderboards.
50///
51/// # API Base URL
52///
53/// The default API endpoint is `https://data-api.polymarket.com`.
54///
55/// # Example
56///
57/// ```no_run
58/// use polymarket_client_sdk::data::Client;
59///
60/// // Create client with default endpoint
61/// let client = Client::default();
62///
63/// // Or with a custom endpoint
64/// let client = Client::new("https://custom-api.example.com").unwrap();
65/// ```
66#[derive(Clone, Debug)]
67pub struct Client {
68    host: Url,
69    client: ReqwestClient,
70}
71
72impl Default for Client {
73    fn default() -> Self {
74        Client::new("https://data-api.polymarket.com")
75            .expect("Client with default endpoint should succeed")
76    }
77}
78
79impl Client {
80    /// Creates a new Data API client with a custom host URL.
81    ///
82    /// # Arguments
83    ///
84    /// * `host` - The base URL for the Data API (e.g., `https://data-api.polymarket.com`).
85    ///
86    /// # Errors
87    ///
88    /// Returns an error if the URL is invalid or the HTTP client cannot be created.
89    pub fn new(host: &str) -> Result<Client> {
90        let mut headers = HeaderMap::new();
91
92        headers.insert("User-Agent", HeaderValue::from_static("rs_clob_client"));
93        headers.insert("Accept", HeaderValue::from_static("*/*"));
94        headers.insert("Connection", HeaderValue::from_static("keep-alive"));
95        headers.insert("Content-Type", HeaderValue::from_static("application/json"));
96        let client = ReqwestClient::builder().default_headers(headers).build()?;
97
98        Ok(Self {
99            host: Url::parse(host)?,
100            client,
101        })
102    }
103
104    /// Returns the base URL of the API.
105    #[must_use]
106    pub fn host(&self) -> &Url {
107        &self.host
108    }
109
110    async fn get<Req: Serialize, Res: DeserializeOwned>(
111        &self,
112        path: &str,
113        req: &Req,
114    ) -> Result<Res> {
115        let query = req.query_params(None);
116        let request = self
117            .client
118            .request(Method::GET, format!("{}{path}{query}", self.host))
119            .build()?;
120        crate::request(&self.client, request, None).await
121    }
122
123    /// Performs a health check on the API.
124    ///
125    /// Returns "OK" when the API is healthy and operational.
126    ///
127    /// # Errors
128    ///
129    /// Returns an error if the request fails or the API returns an error response.
130    pub async fn health(&self) -> Result<Health> {
131        self.get("", &()).await
132    }
133
134    /// Fetches current (open) positions for a user.
135    ///
136    /// Positions represent holdings of outcome tokens in prediction markets.
137    ///
138    /// # Errors
139    ///
140    /// Returns an error if the request fails or the API returns an error response.
141    pub async fn positions(&self, req: &PositionsRequest) -> Result<Vec<Position>> {
142        self.get("positions", req).await
143    }
144
145    /// Fetches trade history for a user or markets.
146    ///
147    /// Trades represent executed orders where outcome tokens were bought or sold.
148    ///
149    /// # Errors
150    ///
151    /// Returns an error if the request fails or the API returns an error response.
152    pub async fn trades(&self, req: &TradesRequest) -> Result<Vec<Trade>> {
153        self.get("trades", req).await
154    }
155
156    /// Fetches on-chain activity for a user.
157    ///
158    /// Returns various on-chain operations including trades, splits, merges,
159    /// redemptions, rewards, and conversions.
160    ///
161    /// # Errors
162    ///
163    /// Returns an error if the request fails or the API returns an error response.
164    pub async fn activity(&self, req: &ActivityRequest) -> Result<Vec<Activity>> {
165        self.get("activity", req).await
166    }
167
168    /// Fetches top token holders for specified markets.
169    ///
170    /// Returns holders grouped by token (outcome) for each market.
171    ///
172    /// # Errors
173    ///
174    /// Returns an error if the request fails or the API returns an error response.
175    pub async fn holders(&self, req: &HoldersRequest) -> Result<Vec<MetaHolder>> {
176        self.get("holders", req).await
177    }
178
179    /// Fetches the total value of a user's positions.
180    ///
181    /// Optionally filtered by specific markets.
182    ///
183    /// # Errors
184    ///
185    /// Returns an error if the request fails or the API returns an error response.
186    pub async fn value(&self, req: &ValueRequest) -> Result<Vec<Value>> {
187        self.get("value", req).await
188    }
189
190    /// Fetches closed (historical) positions for a user.
191    ///
192    /// These are positions that have been fully sold or redeemed.
193    ///
194    /// # Errors
195    ///
196    /// Returns an error if the request fails or the API returns an error response.
197    pub async fn closed_positions(
198        &self,
199        req: &ClosedPositionsRequest,
200    ) -> Result<Vec<ClosedPosition>> {
201        self.get("closed-positions", req).await
202    }
203
204    /// Fetches trader leaderboard rankings.
205    ///
206    /// Returns trader rankings filtered by category, time period, and ordering.
207    ///
208    /// # Errors
209    ///
210    /// Returns an error if the request fails or the API returns an error response.
211    pub async fn leaderboard(
212        &self,
213        req: &TraderLeaderboardRequest,
214    ) -> Result<Vec<TraderLeaderboardEntry>> {
215        self.get("v1/leaderboard", req).await
216    }
217
218    /// Fetches the total count of unique markets a user has traded.
219    ///
220    /// # Errors
221    ///
222    /// Returns an error if the request fails or the API returns an error response.
223    pub async fn traded(&self, req: &TradedRequest) -> Result<Traded> {
224        self.get("traded", req).await
225    }
226
227    /// Fetches open interest for markets.
228    ///
229    /// Open interest represents the total value of outstanding positions in a market.
230    ///
231    /// # Errors
232    ///
233    /// Returns an error if the request fails or the API returns an error response.
234    pub async fn open_interest(&self, req: &OpenInterestRequest) -> Result<Vec<OpenInterest>> {
235        self.get("oi", req).await
236    }
237
238    /// Fetches live trading volume for an event.
239    ///
240    /// Includes total volume and per-market breakdown.
241    ///
242    /// # Errors
243    ///
244    /// Returns an error if the request fails or the API returns an error response.
245    pub async fn live_volume(&self, req: &LiveVolumeRequest) -> Result<Vec<LiveVolume>> {
246        self.get("live-volume", req).await
247    }
248
249    /// Fetches aggregated builder leaderboard rankings.
250    ///
251    /// Builders are third-party applications that integrate with Polymarket.
252    /// Returns one entry per builder with aggregated totals for the specified time period.
253    ///
254    /// # Errors
255    ///
256    /// Returns an error if the request fails or the API returns an error response.
257    pub async fn builder_leaderboard(
258        &self,
259        req: &BuilderLeaderboardRequest,
260    ) -> Result<Vec<BuilderLeaderboardEntry>> {
261        self.get("v1/builders/leaderboard", req).await
262    }
263
264    /// Fetches daily time-series volume data for builders.
265    ///
266    /// Returns multiple entries per builder (one per day), each including a timestamp.
267    ///
268    /// # Errors
269    ///
270    /// Returns an error if the request fails or the API returns an error response.
271    pub async fn builder_volume(
272        &self,
273        req: &BuilderVolumeRequest,
274    ) -> Result<Vec<BuilderVolumeEntry>> {
275        self.get("v1/builders/volume", req).await
276    }
277}