The SQL Explorer REST API enables you to execute SQL queries programmatically against indexed blockchain data. You can query billions of rows of on-chain data using standard SQL syntax and receive results in JSON format.
You can use SQL Explorer in two ways:
All requests to the SQL Explorer REST API require authentication via an API key. The API key must be included in the x-api-key header with every request.
To get your API key:
Note: This is the same API key system used across all Quicknode products (RPC, Streams, IPFS, etc.).
Here's a complete workflow for using SQL Explorer programmatically:
Step 1: Fetch the Schema
Get available tables and columns for your target chain. Replace {clusterId} with your desired cluster ID (e.g., hyperliquid-core-mainnet for Hyperliquid) and YOUR_API_KEY with the API key you obtained from the Authentication section above. See Schema Endpoint for all supported cluster IDs.
curl https://api.quicknode.com/sql/rest/v1/schema/{clusterId} \
-H "x-api-key: YOUR_API_KEY"
Alternatively, download static schema files from the Schema Reference page.
Step 2: Build Your SQL Query
Using the schema, construct your query:
SELECT toDateTime(block_time) AS time, action_type, user
FROM hyperliquid_system_actions
WHERE block_time >= now() - INTERVAL 1 DAY
ORDER BY block_time DESC
LIMIT 100
Step 3: Execute the Query
Send to the query endpoint with your target cluster ID (see Query Endpoint for details):
curl -X POST 'https://api.quicknode.com/sql/rest/v1/query' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"query": "SELECT toDateTime(block_time) AS time, action_type, user FROM hyperliquid_system_actions WHERE block_time >= now() - INTERVAL 1 DAY ORDER BY block_time DESC LIMIT 100",
"clusterId": "{clusterId}"
}'
SQL Explorer provides two REST API endpoints:
Base URL: https://api.quicknode.com
Fetch complete database schema including table names, columns, data types, sort keys, and partition strategies.
Endpoint:
GET https://api.quicknode.com/sql/rest/v1/schema/{clusterId}
Supported Cluster IDs:
Example Request:
curl https://api.quicknode.com/sql/rest/v1/schema/hyperliquid-core-mainnet \
-H "x-api-key: YOUR_API_KEY"
Example Response:
[
{
"chain": "Hyperliquid (HyperCore)",
"clusterId": "hyperliquid-core-mainnet",
"tables": [
{
"name": "hyperliquid_agents",
"engine": "SharedReplacingMergeTree",
"total_rows": 81120375,
"partition_key": "toYYYYMM(snapshot_time)",
"sorting_key": ["block_number", "agent"],
"columns": [
{
"name": "agent",
"type": "FixedString(42)"
},
{
"name": "block_number",
"type": "UInt64"
},
{
"name": "snapshot_time",
"type": "DateTime64(6, 'UTC')"
}
// ... more columns
]
}
// ... more tables
]
}
]
Alternative: Download static schema files (no API key required) from the Schema Reference page in JSON or plain text format.
Execute SQL queries and retrieve results.
Endpoint:
POST https://api.quicknode.com/sql/rest/v1/query
Request Parameters:
Example Request:
curl -X POST 'https://api.quicknode.com/sql/rest/v1/query' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"query": "SELECT toDateTime(block_time) AS time, action_type, user FROM hyperliquid_system_actions WHERE block_time >= now() - INTERVAL 1 DAY ORDER BY block_time DESC LIMIT 100",
"clusterId": "{clusterId}"
}'
var myHeaders = new Headers();
myHeaders.append('accept', 'application/json');
myHeaders.append('Content-Type', 'application/json');
myHeaders.append('x-api-key', 'YOUR_API_KEY');
var requestOptions = {
method: 'POST',
headers: myHeaders,
redirect: 'follow',
body: JSON.stringify({
query: 'SELECT toDateTime(block_time) AS time, action_type, user FROM hyperliquid_system_actions WHERE block_time >= now() - INTERVAL 1 DAY ORDER BY block_time DESC LIMIT 100',
clusterId: '{clusterId}'
})
};
fetch('https://api.quicknode.com/sql/rest/v1/query', requestOptions)
.then(response => response.json())
.then(result => console.log(result))
.catch(error => console.log('error', error));
import requests
import json
url = "https://api.quicknode.com/sql/rest/v1/query"
payload = json.dumps({
"query": "SELECT toDateTime(block_time) AS time, action_type, user FROM hyperliquid_system_actions WHERE block_time >= now() - INTERVAL 1 DAY ORDER BY block_time DESC LIMIT 100",
"clusterId": "{clusterId}"
})
headers = {
'accept': 'application/json',
'Content-Type': 'application/json',
'x-api-key': 'YOUR_API_KEY'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
require "uri"
require "json"
require "net/http"
url = URI("https://api.quicknode.com/sql/rest/v1/query")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request["accept"] = "application/json"
request["Content-Type"] = "application/json"
request["x-api-key"] = "YOUR_API_KEY"
request.body = JSON.dump({
"query": "SELECT toDateTime(block_time) AS time, action_type, user FROM hyperliquid_system_actions WHERE block_time >= now() - INTERVAL 1 DAY ORDER BY block_time DESC LIMIT 100",
"clusterId": "{clusterId}"
})
response = https.request(request)
puts response.read_body
Response Format:
The API returns a JSON response with query results, metadata, and execution statistics.
Response Fields:
meta: Array of column metadata with name and type for each column in the result setdata: Array of objects containing the actual query results, with each object representing one rowrows: Number of rows returned in this responserows_before_limit_at_least: Total number of rows that matched the query before applying LIMIT (useful for pagination)statistics: Performance metrics for query optimization:
elapsed: Total query execution time in secondsrows_read: Total number of rows scanned during execution (may be more than returned rows due to filtering)bytes_read: Total data scanned in bytes (helpful for understanding query cost and optimization opportunities)Example Response:
{
"meta": [
{
"name": "time",
"type": "DateTime('UTC')"
},
{
"name": "action_type",
"type": "LowCardinality(String)"
}
],
"data": [
{
"time": "2026-03-30 18:05:15",
"action_type": "SystemSpotSendAction",
"user": "0x2222222222222222222222222222222222222222"
},
{
"time": "2026-03-30 18:05:14",
"action_type": "SystemSpotSendAction",
"user": "0x2000000000000000000000000000000000000153"
}
],
"rows": 100,
"rows_before_limit_at_least": 14781,
"statistics": {
"elapsed": 0.004817085,
"rows_read": 33599,
"bytes_read": 1009864
}
}
SQL Explorer limits query results to 1000 rows per request. To retrieve larger result sets, use pagination with the LIMIT and OFFSET clauses in your SQL queries.
Pagination works by:
LIMIT to specify the number of rows to return per page (max 1000)OFFSET to skip rows from previous pagesrows_before_limit_at_least field in the response to determine if more results existKey Response Fields for Pagination:
rows: Number of rows returned in the current responserows_before_limit_at_least: Total number of rows that matched your query before applying LIMIT. Use this to calculate total pages and determine when to stop paginating.To paginate through results in batches of 100 rows:
Page 1 (rows 1-100):
curl -X POST 'https://api.quicknode.com/sql/rest/v1/query' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"query": "SELECT timestamp, coin, side, price, size, price * size AS notional_usd, buyer_address, seller_address, buyer_fee, seller_fee, fee_token FROM hyperliquid_trades WHERE block_time > now() - INTERVAL 1 HOUR ORDER BY block_number DESC, trade_id DESC LIMIT 100 OFFSET 0",
"clusterId": "hyperliquid-core-mainnet"
}'
const pageSize = 100;
const pageNumber = 1;
const offset = (pageNumber - 1) * pageSize;
var myHeaders = new Headers();
myHeaders.append('accept', 'application/json');
myHeaders.append('Content-Type', 'application/json');
myHeaders.append('x-api-key', 'YOUR_API_KEY');
var requestOptions = {
method: 'POST',
headers: myHeaders,
redirect: 'follow',
body: JSON.stringify({
query: `SELECT timestamp, coin, side, price, size, price * size AS notional_usd, buyer_address, seller_address, buyer_fee, seller_fee, fee_token FROM hyperliquid_trades WHERE block_time > now() - INTERVAL 1 HOUR ORDER BY block_number DESC, trade_id DESC LIMIT ${pageSize} OFFSET ${offset}`,
clusterId: 'hyperliquid-core-mainnet'
})
};
fetch('https://api.quicknode.com/sql/rest/v1/query', requestOptions)
.then(response => response.json())
.then(result => {
console.log(`Page ${pageNumber}: ${result.rows} rows`);
console.log(`Total matching rows: ${result.rows_before_limit_at_least}`);
})
.catch(error => console.log('error', error));
import requests
import json
page_size = 100
page_number = 1
offset = (page_number - 1) * page_size
url = "https://api.quicknode.com/sql/rest/v1/query"
payload = json.dumps({
"query": f"SELECT timestamp, coin, side, price, size, price * size AS notional_usd, buyer_address, seller_address, buyer_fee, seller_fee, fee_token FROM hyperliquid_trades WHERE block_time > now() - INTERVAL 1 HOUR ORDER BY block_number DESC, trade_id DESC LIMIT {page_size} OFFSET {offset}",
"clusterId": "hyperliquid-core-mainnet"
})
headers = {
'accept': 'application/json',
'Content-Type': 'application/json',
'x-api-key': 'YOUR_API_KEY'
}
response = requests.request("POST", url, headers=headers, data=payload)
result = response.json()
print(f"Page {page_number}: {result['rows']} rows")
print(f"Total matching rows: {result['rows_before_limit_at_least']}")
Page 2 (rows 101-200):
curl -X POST 'https://api.quicknode.com/sql/rest/v1/query' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"query": "SELECT timestamp, coin, side, price, size, price * size AS notional_usd, buyer_address, seller_address, buyer_fee, seller_fee, fee_token FROM hyperliquid_trades WHERE block_time > now() - INTERVAL 1 HOUR ORDER BY block_number DESC, trade_id DESC LIMIT 100 OFFSET 100",
"clusterId": "hyperliquid-core-mainnet"
}'
When using pagination, pay attention to these response fields:
{
"meta": [...],
"data": [...],
"rows": 100,
"rows_before_limit_at_least": 4567,
"statistics": {
"elapsed": 0.042,
"rows_read": 15847,
"bytes_read": 2456789
}
}
rows: 100 - This page returned 100 rowsrows_before_limit_at_least: 4567 - At least 4,567 total rows match your queryrows < LIMIT, you've reached the last pagerows_before_limit_at_least > (OFFSET + rows), more pages are availableSQL Explorer includes pre-built queries for Hyperliquid covering trading, market analysis, position tracking, and infrastructure monitoring across multiple categories: Trading, Fills, Orders, Funding, Infrastructure, Ledger, Markets, Builders, and Staking.
See all pre-built queries with request and response examples in the Hyperliquid Queries page.
You can write custom SQL queries beyond the pre-built ones. SQL Explorer supports standard SQL syntax including:
toDateTime(), base58Encode(), countIf(), round(), etc.GROUP BY, aggregations, JOIN operationsTwo Approaches:
Building Custom Data APIs:
SQL Explorer lets you execute any SQL query programmatically. Wrap queries in your own backend services to create custom endpoints, build reusable data APIs, or use the same queries across dashboards, mobile apps, and trading bots.
If you have any feedback or questions about this documentation, let us know. We'd love to hear from you!