Elixir SDK for the Hyperliquid decentralized exchange with DSL-based API endpoints, WebSocket subscriptions, and optional Postgres/Phoenix integration.
Hyperliquid provides a comprehensive, type-safe interface to the Hyperliquid DEX. The v0.2.0 release introduces a modern DSL-based architecture that eliminates boilerplate while providing response validation, automatic caching, and optional database persistence.
- DSL-based endpoint definitions - Clean, declarative API with automatic function generation
- 125+ typed endpoints - 62 Info endpoints, 38 Exchange endpoints, 26 WebSocket subscriptions
- Ecto schema validation - Built-in response validation and type safety
- WebSocket connection pooling - Efficient connection management with automatic reconnection
- Cachex-based caching - Fast in-memory asset metadata and mid price lookups
- Optional Postgres persistence - Config-driven database storage for API data
- Testnet/mainnet support - Easy chain switching with automatic database separation
- Phoenix PubSub integration - Real-time event broadcasting
Add hyperliquid to your list of dependencies in mix.exs:
defdepsdo[{:hyperliquid,"~> 0.2.0"}]endThe minimal configuration requires only your private key:
# config/config.exsconfig:hyperliquid,private_key: "YOUR_PRIVATE_KEY_HERE"Enable database features by setting enable_db: true and adding the required dependencies:
# mix.exsdefpdepsdo[{:hyperliquid,"~> 0.2.0"},# Required when enable_db: true{:phoenix_ecto,"~> 4.5"},{:ecto_sql,"~> 3.10"},{:postgrex,">= 0.0.0"}]end# config/config.exsconfig:hyperliquid,private_key: "YOUR_PRIVATE_KEY_HERE",enable_db: true# Configure the Repoconfig:hyperliquid,Hyperliquid.Repo,database: "hyperliquid_dev",username: "postgres",password: "postgres",hostname: "localhost",pool_size: 10Switch to testnet and optionally disable automatic cache initialization:
config:hyperliquid,chain: :testnet,private_key: "YOUR_TESTNET_KEY",autostart_cache: true# Set to false to manually initialize cacheThe database name automatically gets a _testnet suffix when using testnet.
config:hyperliquid,# Chain selectionchain: :mainnet,# or :testnet# API endpoints (optional - defaults based on chain)http_url: "https://api.hyperliquid.xyz",ws_url: "wss://api.hyperliquid.xyz/ws",# Optional featuresenable_db: false,enable_web: false,autostart_cache: true,# Debug loggingdebug: false,# Private keyprivate_key: "YOUR_PRIVATE_KEY_HERE"Use Info API endpoints to retrieve market data:
# Get mid prices for all assetsaliasHyperliquid.Api.Info.AllMids{:ok,mids}=AllMids.request()# Returns raw map: %{"BTC" => "43250.5", "ETH" => "2280.75", ...}# Get account summaryaliasHyperliquid.Api.Info.ClearinghouseState{:ok,state}=ClearinghouseState.request("0x1234...")state.margin_summary.account_value# => "10000.0"# Get open ordersaliasHyperliquid.Api.Info.FrontendOpenOrders{:ok,orders}=FrontendOpenOrders.request("0x1234...")# => [%{coin: "BTC", limit_px: "43000.0", ...}]# Get user fillsaliasHyperliquid.Api.Info.UserFills{:ok,fills}=UserFills.request("0x1234...")# => %{fills: [%{coin: "BTC", px: "43100.5", ...}]}Use Exchange API endpoints to trade. The private key defaults to the one in your config,
or you can pass it explicitly via the :private_key option:
aliasHyperliquid.Api.Exchange.{Order,Cancel}# Place a limit order (uses private_key from config){:ok,result}=Order.place_limit("BTC",true,"43000.0","0.1")# => %{status: "ok", response: %{data: %{statuses: [%{resting: %{oid: 12345}}]}}}# Place a market order{:ok,result}=Order.place_market("ETH",false,"1.5")# Or build and place separatelyorder=Order.limit_order("BTC",true,"43000.0","0.1"){:ok,result}=Order.place(order)# Override private key per-request{:ok,result}=Order.place_limit("BTC",true,"43000.0","0.1",private_key: other_key)# Cancel an order by asset and order ID{:ok,cancel_result}=Cancel.cancel(0,12345)# => %{status: "ok", response: %{data: %{statuses: ["success"]}}}Hyperliquid exchange actions use two different signing schemes:
Agent-key compatible — Orders, cancels, leverage updates, and other trading actions use EIP-712 exchange domain signing. These can be signed with an agent key (approved via
ApproveAgent) instead of your main private key. This is the recommended setup for trading bots.L1-signed actions — Transfers (
UsdClassTransfer,SubAccountTransfer), withdrawals, vault operations, sub-account creation, and other account-level actions require your actual private key. These cannot be delegated to an agent key.
# Agent-key compatible (trading actions)# Configure your agent key in config and trade without exposing your main keyconfig:hyperliquid,private_key: "YOUR_AGENT_KEY"Order.place_limit("BTC",true,"43000.0","0.1")Cancel.cancel(0,12345)# L1-signed actions (require main private key)aliasHyperliquid.Api.Exchange.UsdClassTransferUsdClassTransfer.request(%{...},private_key: "YOUR_MAIN_PRIVATE_KEY")Subscribe to real-time data feeds:
aliasHyperliquid.WebSocket.ManageraliasHyperliquid.Api.Subscription.{AllMids,Trades,UserFills}# Subscribe to all mid prices (shared connection){:ok,sub_id}=Manager.subscribe(AllMids,%{})# Subscribe to trades for BTC (shared connection){:ok,sub_id}=Manager.subscribe(Trades,%{coin: "BTC"})# Subscribe to user fills (user-grouped connection){:ok,sub_id}=Manager.subscribe(UserFills,%{user: "0x1234..."})# UnsubscribeManager.unsubscribe(sub_id)# List active subscriptionsManager.list_subscriptions()The cache provides fast access to asset metadata and mid prices:
aliasHyperliquid.Cache# The cache auto-initializes on startup (unless autostart_cache: false)# Manual initialization:Cache.init()# Get mid price for a coinCache.get_mid("BTC")# => 43250.5# Get asset index for a coinCache.asset_from_coin("BTC")# => 0Cache.asset_from_coin("HYPE/USDC")# Spot pairs work too# => 10107# Get size decimalsCache.decimals_from_coin("BTC")# => 5# Get token infoCache.get_token_by_name("HFUN")# => %{"name" => "HFUN", "index" => 2, "sz_decimals" => 2, ...}# Subscribe to live mid price updates{:ok,sub_id}=Cache.subscribe_to_mids()The Info API provides read-only market and account information. All endpoints are located in Hyperliquid.Api.Info.*:
Market Data:
AllMids- Mid prices for all assetsAllPerpMetas- Perpetual market metadataActiveAssetData- Asset context dataCandleSnapshot- Historical candlesFundingHistory- Funding rate historyL2Book- Order book snapshot
Account Data:
ClearinghouseState- Perpetuals account summarySpotClearinghouseState- Spot account summaryUserFills- Trade fill historyHistoricalOrders- Historical ordersFrontendOpenOrders- Current open ordersUserFunding- User funding payments
Vault & Delegation:
VaultDetails- Vault informationDelegations- User delegationsDelegatorRewards- Delegation rewards
See the HexDocs for the complete list of 62 Info endpoints.
The Exchange API handles all trading operations. All endpoints are located in Hyperliquid.Api.Exchange.*:
Order Management:
Modify- Place or modify ordersBatchModify- Batch order modificationsCancel- Cancel ordersCancelByCloid- Cancel by client order ID
Account Operations:
UsdTransfer- Transfer USD between accountsWithdraw3- Withdraw to L1CreateSubAccount- Create sub-accountsUpdateLeverage- Adjust position leverageUpdateIsolatedMargin- Modify isolated margin
Vault Operations:
CreateVault- Create a new vaultVaultTransfer- Vault deposits/withdrawals
See the HexDocs for the complete list of 38 Exchange endpoints.
The Subscription API provides WebSocket channels for real-time data. All endpoints are located in Hyperliquid.Api.Subscription.*:
Market Subscriptions:
AllMids- All mid prices (shared connection)Trades- Recent trades (shared connection)L2Book- Order book updates (dedicated connection)Candle- Real-time candles (shared connection)
User Subscriptions:
UserFills- User trade fills (user-grouped)UserFundings- Funding payments (user-grouped)OrderUpdates- Order status changes (user-grouped)Notification- User notifications (user-grouped)
Explorer Subscriptions:
ExplorerBlock- New blocks (shared connection)ExplorerTxs- Transactions (shared connection)
See the HexDocs for the complete list of 26 subscription channels.
All API endpoints are defined using declarative macros that eliminate boilerplate:
defmoduleHyperliquid.Api.Info.AllMidsdouseHyperliquid.Api.Endpoint,type: :info,request_type: "allMids",optional_params: [:dex],rate_limit_cost: 2,raw_response: trueembedded_schemadofield(:mids,:map)field(:dex,:string)enddefchangeset(struct\\%__MODULE__{},attrs)do# Validation logicendendThis automatically generates:
request/0,request/1- Make API request, return{:ok, result}or{:error, reason}request!/0,request!/1- Bang variant that raises on errorbuild_request/1- Build request parametersparse_response/1- Parse and validate responserate_limit_cost/0- Get rate limit cost
defmoduleHyperliquid.Api.Subscription.TradesdouseHyperliquid.Api.SubscriptionEndpoint,request_type: "trades",params: [:coin],connection_type: :shared,storage: [postgres: [enabled: true,table: "trades"],cache: [enabled: true,ttl: :timer.minutes(5)]]embedded_schemadoembeds_many:trades,Tradedofield(:coin,:string)field(:px,:string)# ...endenddefchangeset(event\\%__MODULE__{},attrs)do# Validation logicendendThis automatically generates:
build_request/1- Build subscription request__subscription_info__/0- Metadata about the subscriptiongenerate_subscription_key/1- Unique key for connection routing
The Hyperliquid.WebSocket.Manager handles all WebSocket connections and subscriptions:
:shared- Multiple subscriptions share one connection (e.g.,AllMids,Trades):dedicated- Each subscription gets its own connection (e.g.,L2Bookwith params):user_grouped- All subscriptions for the same user share one connection (e.g.,UserFills)
aliasHyperliquid.WebSocket.ManageraliasHyperliquid.Api.Subscription.Trades# Subscribe with callback functioncallback=fnevent->IO.inspect(event,label: "Trade event")end{:ok,sub_id}=Manager.subscribe(Trades,%{coin: "BTC"},callback)All WebSocket events are broadcast via Phoenix.PubSub:
# Subscribe to events in your LiveView or GenServerPhoenix.PubSub.subscribe(Hyperliquid.PubSub,"ws_event")# Or use the utility functionHyperliquid.Utils.subscribe("ws_event")# Handle eventsdefhandle_info({:ws_event,event},state)do# Process event{:noreply,state}endThe cache module provides efficient access to frequently-used data:
When autostart_cache: true (default), the cache automatically:
- Fetches exchange metadata on startup
- Populates asset mappings and decimal precision
- Updates mid prices from WebSocket subscriptions
aliasHyperliquid.Cache# Asset lookupsCache.asset_from_coin("BTC")# => 0Cache.decimals_from_coin("BTC")# => 5Cache.get_mid("BTC")# => 43250.5# MetadataCache.perps()# => [%{"name" => "BTC", ...}, ...]Cache.spot_pairs()# => [%{"name" => "@0", ...}, ...]Cache.tokens()# => [%{"name" => "USDC", ...}, ...]# Token lookupsCache.get_token_by_name("HFUN")# => %{"index" => 2, ...}Cache.get_token_key("HFUN")# => "HFUN:0xbaf265..."# Low-level cache accessCache.get(:all_mids)# => %{"BTC" => "43250.5", ...}Cache.put(:my_key,value)Cache.exists?(:my_key)# => trueWhen enable_db: true, the package provides Postgres persistence:
# Install database dependencies
mix deps.get
# Create and migrate database
mix ecto.create
mix ecto.migrate# config/config.exsconfig:hyperliquid,ecto_repos: [Hyperliquid.Repo]config:hyperliquid,Hyperliquid.Repo,database: "hyperliquid_dev",username: "postgres",password: "postgres",hostname: "localhost",pool_size: 10Endpoints with storage configuration automatically persist data:
# This subscription will automatically store trades in Postgres and CachexaliasHyperliquid.Api.Subscription.Trades{:ok,sub_id}=Manager.subscribe(Trades,%{coin: "BTC"})# Query stored dataimportEcto.QueryaliasHyperliquid.Repoquery=fromtin"trades",where: t.coin=="BTC",order_by: [desc: t.time],limit: 10Repo.all(query)Database migrations are located in priv/repo/migrations/. The package includes migrations for:
trades,fills,orders,historical_ordersclearinghouse_states,user_snapshotsexplorer_blocks,transactionscandles
Use Hyperliquid in Livebook for interactive trading and analysis:
Mix.install([{:hyperliquid,"~> 0.2.0"}],config: [hyperliquid: [private_key: "YOUR_PRIVATE_KEY_HERE"]])# Start working with the APIaliasHyperliquid.Api.Info.AllMids{:ok,mids}=AllMids.request()Mix.install([{:hyperliquid,"~> 0.2.0"}],config: [hyperliquid: [chain: :testnet,private_key: "YOUR_TESTNET_KEY"]])Query the Hyperliquid explorer for block and transaction details:
aliasHyperliquid.Api.Explorer.{BlockDetails,TxDetails,UserDetails}{:ok,block}=BlockDetails.request(block_height){:ok,tx}=TxDetails.request(tx_hash){:ok,user}=UserDetails.request("0x1234...")Make JSON-RPC calls to the Hyperliquid EVM:
aliasHyperliquid.Transport.Rpc{:ok,block_number}=Rpc.call("eth_blockNumber",[]){:ok,[block,chain]}=Rpc.batch([{"eth_blockNumber",[]},{"eth_chainId",[]}])Hyperliquid emits :telemetry events for API requests, WebSocket connections, cache operations, RPC calls, and storage flushes. See Hyperliquid.Telemetry for the full event reference.
Hyperliquid.Telemetry.attach_default_logger()defmoduleMyApp.TelemetrydoimportTelemetry.Metricsdefmetricsdo[summary("hyperliquid.api.request.stop.duration",unit: {:native,:millisecond}),summary("hyperliquid.api.exchange.stop.duration",unit: {:native,:millisecond}),counter("hyperliquid.ws.message.received.count"),summary("hyperliquid.rpc.request.stop.duration",unit: {:native,:millisecond}),last_value("hyperliquid.storage.flush.stop.record_count")]endend# Get dependencies
mix deps.get
# Run tests
mix test# Run tests with database
mix test# Format code
mix format
# Generate docs
mix docsFull documentation is available on HexDocs.
This project is licensed under the MIT License. See LICENSE.md for details.