# `CCXT.HTTP`
[🔗](https://github.com/ZenHive/ccxt_client/blob/main/lib/ccxt/http.ex#L1)

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:

1. **Signing requires raw params** — signing patterns need params before URL encoding
2. **Sorted encoding** — some exchanges require alphabetically sorted params
3. **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.Error` structs
- **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"}
    )

# `response`

```elixir
@type response() :: %{status: integer(), headers: response_headers(), body: term()}
```

# `response_headers`

```elixir
@type response_headers() :: %{optional(String.t()) =&gt; [String.t()]}
```

HTTP response with status, headers, and decoded body

# `request`

```elixir
@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 struct
- `method` - 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: from `CCXT.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

# `signed_request`

```elixir
@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 struct
- `signed` - Signed request from `CCXT.Signing.sign/4` with `:url`, `:method`, `:headers`, `:body`
- `base_url` - Base URL to prepend to the signed path
- `opts` - Options passed through to Req (`:timeout`, `:plug` for tests, etc.)

---

*Consult [api-reference.md](api-reference.md) for complete listing*
