View Source Hyperliquid.Api.Endpoint (hyperliquid v0.2.2)

DSL for defining API endpoints with automatic request/response handling.

This macro reduces boilerplate while preserving explicit Ecto schemas for response validation and type safety.

Usage

Simple endpoint (no parameters)

defmodule Hyperliquid.Api.Info.AllMids do
  use Hyperliquid.Api.Endpoint,
    type: :info,
    request: %{type: "allMids"},
    rate_limit_cost: 2

  @primary_key false
  embedded_schema do
    field :mids, :map
  end

  def changeset(struct \\ %__MODULE__{}, attrs) do
    # Your validation logic
  end

  # Optional domain helpers
  def get_mid(%__MODULE__{mids: mids}, coin), do: Map.fetch(mids, coin)
end

Parametrized endpoint

defmodule Hyperliquid.Api.Info.L2Book do
  use Hyperliquid.Api.Endpoint,
    type: :info,
    request_type: "l2Book",
    params: [coin: :required],
    optional_params: [:nSigFigs, :mantissa],
    rate_limit_cost: 20

  # Schema and changeset...

  # Optional: preprocess data before validation
  def preprocess(data), do: transform_levels(data)
end

Generated Functions

The macro generates:

  • build_request/0 or build_request/1,2 - Build the request payload
  • request/0 or request/1,2 - Make HTTP request and parse response
  • request!/0 or request!/1,2 - Bang variant that raises on error
  • parse_response/1 - Parse and validate response (uses your changeset)
  • rate_limit_cost/0 - Get the rate limit cost for this endpoint

Options

  • :type - Required. :info, :explorer, or :stats
  • :request - Static request map (for no-param endpoints)
  • :request_type - The "type" field value (for parametrized endpoints)
  • :params - List of required parameters as atoms
  • :optional_params - List of optional parameters as atoms
  • :rate_limit_cost - Integer cost for rate limiting (default: 0)
  • :doc - Short description of the endpoint
  • :returns - Description of what the endpoint returns
  • :raw_response - Generate request_raw/N functions for raw API responses (no key transformation)
  • :storage - Storage configuration (see below)

Storage Options

The :storage option enables automatic persistence when using fetch/N:

Postgres Storage

storage: [
  postgres: [
    enabled: true,
    table: "blocks"
  ]
]

Postgres with Upsert (for mutable data)

storage: [
  postgres: [
    enabled: true,
    table: "perp_dexs",
    extract: :dexs,                    # Extract nested records from this field
    conflict_target: :name,            # Unique key for upsert
    on_conflict: {:replace, [          # Fields to update on conflict
      :full_name, :deployer, :asset_to_streaming_oi_cap, :updated_at
    ]}
  ],
  context_params: []                   # Request params to merge (default: [:user])
]

Cache Storage

storage: [
  cache: [
    enabled: true,
    ttl: :timer.minutes(5),
    key_pattern: "block:{{block_number}}"
  ]
]

Both backends can be enabled simultaneously. Use fetch/N instead of request/N to automatically persist results.

Rate Limit Weights (per IP, 1200/min aggregate)

  • Weight 2: l2Book, allMids, clearinghouseState, orderStatus, spotClearinghouseState, exchangeStatus
  • Weight 20: Most info endpoints (default)
  • Weight 60: userRole
  • Exchange actions: 1 + floor(batch_length / 40)

Introspection

Each endpoint module provides:

  • __endpoint_info__/0 - Returns metadata about the endpoint
  • rate_limit_cost/0 - Returns the rate limit weight

Telemetry Events

The DSL emits telemetry events for observability:

  • [:hyperliquid, :api, :request, :start] - When request begins
  • [:hyperliquid, :api, :request, :stop] - When request completes successfully
  • [:hyperliquid, :api, :request, :exception] - When request fails

Metadata includes: endpoint, type, params

Summary

Functions

Interpolate a key pattern with data values.

Functions

Link to this function

interpolate_key_pattern(pattern, data)

View Source
@spec interpolate_key_pattern(String.t(), map()) :: String.t()

Interpolate a key pattern with data values.

Replaces {{field}} placeholders with actual values.

Examples

iex> interpolate_key_pattern("block:{{block_number}}", %{block_number: 123})
"block:123"