Coinbase, behind the DpExchange facade.
⚠️ EXPERIMENTAL
This package has not run in production. While it is 0.x the API may change without
a major version — pin all three segments. Maturity is declared per endpoint
through capabilities/0; do not read this banner as your check.
This module is the entire public API of this package. Transport, signing, session handling and supervision are internal, and there is nothing here that returns them.
What is specific to Coinbase, and what a caller can therefore rely on
Credentials choose the endpoint; they do not gate it. Coinbase serves the same
market data publicly and authenticated. Pass credentials and this package uses the
authenticated path, which has the higher ceiling; pass none and it uses the public one.
Either way you get the same Core.Types.* back, and capabilities/0 tells you what the
difference bought.
Nine candle widths, and 12h is not one of them. The shared vocabulary models it;
Coinbase does not serve it. Asking for a width Coinbase does not serve is an error,
never the nearest one — a caller handed one-hour bars labelled four-hour has every value
real and every label wrong, which is how it went unnoticed for weeks.
350 candles per request is a hard boundary. Measured: 350 minutes of one-minute
candles returns 349; 351 returns zero and an error, not the first 350. This package
refuses an over-wide range up front with {:error, {:range_too_wide, …}} rather than
sending it and handing back an empty result, because empty reads as "no data for this
period".
Coinbase publishes no rate-limit headers. Measured 2026-08-28: no x-ratelimit-*,
no retry-after. The ceilings in capabilities/0 are declared, and
measured_against says exactly where they came from.
Supervision
Add it to your own tree. Nothing starts on load — a consumer that has not asked for Coinbase must not find a socket open.
children = [{DpExchange.Coinbase, credentials: my_credentials()}]