Gemini's REST surface — internal. The facade's market-data callbacks are served here.
Every endpoint below was measured against the live venue on 2026-08-28. Where the
measurement disagreed with Gemini's documentation, the measurement won and the
divergence is recorded in docs/reference/gemini/.
The candle window is fixed and every parameter is ignored
/v2/candles/{symbol}/{time_frame} takes no bounds. limit, start and end are
accepted and discarded — three requests differing only in those returned byte-identical
responses. Each width serves a fixed window:
| Width sent | Bars | ≈ span |
|---|---|---|
1m | 1440 | 1 day |
5m | 2015 | 7 days |
15m | 1343 | 14 days |
30m | 1439 | 30 days |
1hr | 1463 | 61 days |
6hr | 367 | 92 days |
1day | 364 | 1 year |
So a range is honoured by filtering here, and a range the window cannot cover is an error rather than a short answer. Handing back 364 daily bars to a caller who asked for five years is the family's named failure mode in its quietest form: every value real, only the meaning wrong.
Neither ticker carries a quote timestamp, so the venue's own clock is used
/v1/pubticker returns a timestamp, but it is inside the volume object — it
stamps the 24-hour volume window, updates about once a minute, and is not when the bid
and ask were true. /v2/ticker carries no timestamp at all. Using either as the quote
time would be a substitution of exactly the kind this family exists to stop, and the
host adapter does something worse: parse_timestamp(nil) returns DateTime.utc_now(),
so a quote with no venue time gets the client's clock and looks perfectly fresh.
This package uses the HTTP Date response header — the venue's own statement of
when it served the answer, which bounds the quote's age and is not our clock. When that
header is absent the request fails with {:error, :missing_venue_timestamp} rather than
returning a quote whose freshness cannot be stated.
Summary
Functions
Base URL, overridable per process for tests through Core.Config.
Candles for a symbol and canonical timeframe, filtered to range.
Every pair with its last price and 24-hour change, in one call.
A price-level snapshot for one symbol.
Best bid, best ask and last trade for one symbol.
Every spot symbol the venue lists, canonical.
The increments and minimum the venue will actually accept for a symbol.
Canonical timeframes this venue serves, shortest first.
Functions
Base URL, overridable per process for tests through Core.Config.
@spec get_historical_prices(String.t(), String.t(), keyword(), keyword()) :: {:ok, [map()]} | {:error, term()} | {:refused, term()}
Candles for a symbol and canonical timeframe, filtered to range.
range accepts :start and :end as DateTimes. Both are optional; with neither, the
venue's whole fixed window is returned.
Refuses rather than truncating:
- an unknown width →
{:error, {:unsupported_timeframe, width}} - a
:startolder than the window can reach →{:error, {:range_unavailable, …}}
Every pair with its last price and 24-hour change, in one call.
/v1/pricefeed is the only endpoint here that describes the whole catalogue at once,
which is what makes an overview affordable — the alternative is one request per symbol
across 346 symbols, which is not an overview, it is a rate-limit incident.
@spec get_order_book( String.t(), keyword() ) :: {:ok, DpExchange.Core.Types.OrderBook.t()} | {:error, term()} | {:refused, term()}
A price-level snapshot for one symbol.
Each level carries the venue's own timestamp, so unlike a quote there is nothing to
derive: the book's time is the newest level's time.
@spec get_price( String.t(), keyword() ) :: {:ok, DpExchange.Core.Types.Quote.t()} | {:error, term()} | {:refused, term()}
Best bid, best ask and last trade for one symbol.
Timestamped from the venue's Date response header — see the module doc for why not
from the payload.
Every spot symbol the venue lists, canonical.
Perpetuals are excluded. They are real instruments and the venue lists them alongside
spot pairs, but this package declares supported_instrument_types: [:spot], and a
perpetual has no canonical BASE-QUOTE form — emitting one would invent a spot pair
that does not exist.
The increments and minimum the venue will actually accept for a symbol.
From /v1/symbols/details/{symbol}, which is also the source behind the venue's own
published minimums table — that page states it fetches this endpoint live.
tick_size is the base-asset increment and quote_increment the price
increment. They are not interchangeable and the names do not say so: for btcusd,
tick_size is 1.0e-8 BTC while quote_increment is 0.01 USD.
@spec timeframes() :: [String.t()]
Canonical timeframes this venue serves, shortest first.