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}