Packages

Higher-level Elixir SDK for Polymarket: Gamma market discovery, Data API, CTF/pUSD helpers, and convenience facades. Depends on polymarket_clob for the low-level CLOB surface.

Current section

Files

Jump to
polymarket_sdk lib polymarket data.ex
Raw

lib/polymarket/data.ex

defmodule Polymarket.Data do
@moduledoc """
Read-only wrappers for Polymarket's public Data API.
All endpoints are public GETs against `Polymarket.Config.data_api_host/0`
(`https://data-api.polymarket.com`). No authentication is required —
the wallet address is passed as a query parameter, so callers can read
any address they have, not just their own.
This is the source of truth for fee-inclusive trade costs (`/activity`)
and current holdings including unredeemed winners (`/positions`). The
CLOB fills endpoint reports `size * price` and excludes taker fees;
prefer Data API `usdcSize` when computing P&L on completed BUY → REDEEM
cycles.
## Response shape and options
All wrappers delegate to `Polymarket.HTTP.get/3`. See its `@moduledoc`
for the response contract and the per-call options list (`:host`,
`:plug`, `:timeout`, `:retry`, `:max_retries`, `:headers`).
GET requests default to Req's `:transient` retry policy; pass
`retry: false` to disable.
"""
@type result :: Polymarket.HTTP.result()
# Defaults aligned with production usage — these values have shipped
# against the live API without rejection.
@default_size_threshold 0.1
@default_activity_limit 1000
@default_trades_limit 20
@doc """
Lists current positions for a wallet address.
Returns positions with camelCase fields as sent by the Data API —
including `size`, `avgPrice`, `currentValue`, and `redeemable`. This
wrapper does **not** normalize keys to snake_case; pipe the result
through `Polymarket.Activity` or your own mapper if you need that.
## Options
* `:size_threshold` — minimum position size to return (default `0.1`).
The Data API filters dust positions on the server side.
* Plus any option supported by `Polymarket.HTTP.get/3`.
> #### Pagination {: .info}
>
> This wrapper does not send `limit`/`offset`. The Data API defaults to
> `limit=100`, so wallets with more than 100 positions are truncated by
> the server. Paginated fetching is not yet exposed here.
"""
@spec get_positions(String.t(), keyword()) :: result()
def get_positions(address, opts \\ []) when is_binary(address) do
{size_threshold, opts} = Keyword.pop(opts, :size_threshold, @default_size_threshold)
params = [user: address, sizeThreshold: size_threshold]
request("/positions", Keyword.put(opts, :params, params))
end
@doc """
Lists activity (trades + redemptions) for a wallet address.
Activity events include:
* `TRADE` — buys and sells with fee-inclusive `usdcSize` (the actual
wallet debit/credit, including taker fees).
* `REDEEM` — payouts on resolved markets.
Records are keyed by `transactionHash`, not by order hash.
## Options
* `:limit` — max events to return (default `1000`).
* `:offset` — pagination offset.
* Plus any option supported by `Polymarket.HTTP.get/3`.
"""
@spec get_activity(String.t(), keyword()) :: result()
def get_activity(address, opts \\ []) when is_binary(address) do
{limit, opts} = Keyword.pop(opts, :limit, @default_activity_limit)
{offset, opts} = Keyword.pop(opts, :offset, nil)
params =
[user: address, limit: limit]
|> maybe_put_param(:offset, offset)
request("/activity", Keyword.put(opts, :params, params))
end
@doc """
Lists public trade history for a wallet address.
## Options
* `:limit` — max trades to return (default `20`).
* `:after` — sent as-is to the API. **Note:** the live Data API
`GET /trades` endpoint does not currently document an `after`
filter, so this may be ignored server-side — do not rely on it for
time-windowing until verified against the live API.
* `:asset_id` — sent as-is. **Note:** the live `GET /trades` endpoint
does not currently document an asset filter, so this may likewise
be ignored server-side.
* Plus any option supported by `Polymarket.HTTP.get/3`.
"""
@spec get_trades(String.t(), keyword()) :: result()
def get_trades(address, opts \\ []) when is_binary(address) do
{limit, opts} = Keyword.pop(opts, :limit, @default_trades_limit)
{after_ts, opts} = Keyword.pop(opts, :after, nil)
{asset_id, opts} = Keyword.pop(opts, :asset_id, nil)
params =
[user: address, limit: limit]
|> maybe_put_param(:after, after_ts)
|> maybe_put_param(:asset_id, asset_id)
request("/trades", Keyword.put(opts, :params, params))
end
# ── Private ──
defp request(path, opts) do
host = Keyword.get(opts, :host, Polymarket.Config.data_api_host())
Polymarket.HTTP.get(host, path, Keyword.delete(opts, :host))
end
defp maybe_put_param(params, _key, nil), do: params
defp maybe_put_param(params, key, value), do: Keyword.put(params, key, value)
end