HTTP client for exchange API requests.
Wraps Req with circuit breaker integration, error normalization, and telemetry. All exchange HTTP communication goes through this module.
Why Manual Query Encoding?
Uses URI.encode_query/1 instead of Req's :params step because:
- Signing requires raw params — signing patterns need params before URL encoding
- Sorted encoding — some exchanges require alphabetically sorted params
- Consistency — both public and private requests use the same encoding
Features
- Circuit breaker — per-exchange, trips on 500+ and transport errors
- Error normalization — HTTP/exchange errors to
CCXT.Errorstructs - Body-level error detection — many exchanges return HTTP 200 with error in body
- HTML response detection — geo-blocks and Cloudflare return HTML instead of JSON
- Safe retry — only GET/HEAD, never POST/PUT/DELETE
Telemetry — emits
[:ccxt, :request, :start | :stop | :exception]
Usage
{:ok, exchange} = CCXT.Exchange.new("bybit")
# Public endpoint
{:ok, response} = CCXT.HTTP.request(exchange, :get, "/v5/market/tickers",
params: %{"category" => "spot", "symbol" => "BTCUSDT"}
)
Summary
Types
@type response() :: %{status: integer(), headers: response_headers(), body: term()}
HTTP response with status, headers, and decoded body
Functions
@spec request(CCXT.Exchange.t(), atom(), String.t(), keyword()) :: {:ok, response()} | {:error, CCXT.Error.t()}
Makes an HTTP request to an exchange API.
Parameters
exchange- Exchange configuration structmethod- HTTP method (:get,:post,:put,:delete)path- API endpoint path (e.g., "/v5/market/tickers")
Options
:params- Query parameters or request body (default:%{}):headers- Additional request headers (default:[]):timeout- Request timeout in milliseconds (default: fromCCXT.Defaults):base_url- Override base URL (default: uses exchange.base_urls)
Any additional options are passed through to Req (useful for :plug in tests).
Returns
{:ok, response}- Successful response with:status,:headers,:body{:error, %CCXT.Error{}}- Normalized error
@spec signed_request( CCXT.Exchange.t(), CCXT.Signing.signed_request(), String.t(), keyword() ) :: {:ok, response()} | {:error, CCXT.Error.t()}
Executes a pre-signed request.
Used by CCXT.Dispatch for private endpoints after CCXT.Signing.sign/4
has built the signed URL, headers, and body.
Parameters
exchange- Exchange configuration structsigned- Signed request fromCCXT.Signing.sign/4with:url,:method,:headers,:bodybase_url- Base URL to prepend to the signed pathopts- Options passed through to Req (:timeout,:plugfor tests, etc.)