EVM Subsquid Network API
The EVM API of Subsquid Network is currently in beta. Breaking changes may be introduced in the future releases.
Subsquid Network API distributes the requests over a (potentially decentralized) network of workers. The main gateway URL now points at a router that provides URLs of workers that do the heavy lifting. Each worker has its own range of blocks that it serves. The recommended data retrieval procedure is as follows:
- Retrieve the dataset height from the router with
GET /height
. - Query the router for an URL of a worker that has the data for the first block of the relevant range with
GET /${firstBlock}/worker
. - Retrieve the data from the worker with
POST /
, making sure to set the"fromBlock"
query field to${firstBlock}
. - Exclude the received blocks from the relevant range by setting
firstBlock
to the value ofheader.number
of the last received block plus one. - Repeat steps 2-4 until all the required data is retrieved.
Main URLs of EVM gateways are available on the Supported networks page.
Implementation examples:
Manually with cURL
Suppose we want data on Ethereum txs to vitalik.eth
/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
from block 16_000_000. We begin by finding the main URL for the Ethereum Mainnet gateway on the Supported networks page. Then we have to:
-
Verify that the dataset has reached the required height:
$ curl https://v2.archive.subsquid.io/network/ethereum-mainnet/height
Output
18593441
-
Get a worker URL
$ curl https://v2.archive.subsquid.io/network/ethereum-mainnet/16000000/worker
Output
https://lm02.sqd-archive.net/worker/query/czM6Ly9ldGhlcmV1bS1tYWlubmV0
-
Retrieve the data from the worker
$ curl https://lm02.sqd-archive.net/worker/query/czM6Ly9ldGhlcmV1bS1tYWlubmV0 \
-X 'POST' -H 'content-type: application/json' -H 'accept: application/json' \
-d '{
"fromBlock":16000000,
"toBlock":18593441,
"fields":{"transaction":{"hash":true}},
"transactions":[{"to":["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"]}]
}' | jqNote how the address in the
transactions
data request is lowercased.Output:
[
{
"header": {
"number": 16000000,
"hash": "0x3dc4ef568ae2635db1419c5fec55c4a9322c05302ae527cd40bff380c1d465dd",
"parentHash": "0x6f377dc6bd1f3e38b9ceb8c946a88c13211fa3f084622df3ee5cfcd98cc6bb16"
},
"transactions": []
},
// ...
{
"header": {
"number": 16027977,
"hash": "0x4b332878deb33e963b68c8bbbea60cbca72a88c297b6800eafa82baab497c166",
"parentHash": "0x2b979d67d9b03394da336938ee0bcf5aedfdf87e1b5bd574d985aee749eb8b76"
},
"transactions": [
{
"transactionIndex": 96,
"hash": "0xbaede248ec6fce28e9d874f69ea70359bea0107ce9144d6838898674d9d10c8c"
}
]
},
// ...
{
"header": {
"number": 16031419,
"hash": "0x9cc48c9b4ad8dddb1de86a15e30a62ffd48cf9b72930930cfa5167c4e1685d0a",
"parentHash": "0x4ec7b4562739032f51e70d26fe5129e571e2bf0348a744c1509f8205f4381696"
},
"transactions": []
}
] -
Observe that we received the transactions up to and including block 16031419. To get the rest of the data, we find a worker who has blocks from 16031420 on:
$ curl https://v2.archive.subsquid.io/network/ethereum-mainnet/16031420/worker
Output:
https://rb02.sqd-archive.net/worker/query/czM6Ly9ldGhlcmV1bS1tYWlubmV0
We can see that this part of the dataset is located on another host.
-
Retrieve the data from the new worker
$ curl https://rb02.sqd-archive.net/worker/query/czM6Ly9ldGhlcmV1bS1tYWlubmV0 \
-X 'POST' -H 'content-type: application/json' -H 'accept: application/json' \
-d '{
"fromBlock":16031420,
"toBlock":18593441,
"fields":{"transaction":{"hash":true}},
"transactions":[{"to":["0xd8da6bf26964af9d7eed9e03e53415d37aa96045"]}]
}' | jqOutput is similar to that of step 3.
-
Repeat steps 4 and 5 until the dataset height of 18593441 reached.
In Python
def get_text(url: str) -> str:
res = requests.get(url)
res.raise_for_status()
return res.text
def dump(
gateway_url: str,
query: Query,
first_block: int,
last_block: int
) -> None:
assert 0 <= first_block <= last_block
query = dict(query) # copy query to mess with it later
dataset_height = int(get_text(f'{gateway_url}/height'))
next_block = first_block
last_block = min(last_block, dataset_height)
while next_block <= last_block:
worker_url = get_text(f'{gateway_url}/{next_block}/worker')
query['fromBlock'] = next_block
query['toBlock'] = last_block
res = requests.post(worker_url, json=query)
res.raise_for_status()
blocks = res.json()
last_processed_block = blocks[-1]['header']['number']
next_block = last_processed_block + 1
for block in blocks:
print(json.dumps(block))
Full code here.
Router API
GET
/height
(get height of the dataset)
Example response: 16576911
.
GET
${firstBlock}/worker
(get a suitable worker URL)
The returned worker will be capable of processing POST /
requests in which the "fromBlock"
field is equal to ${firstBlock}
.
Example response: https://v2.archive.subsquid.io/worker/1/query/czM6Ly9ldGhlcmV1bS1tYWlubmV0
.
Worker API
POST
/
(query logs and transactions)
Query Fields
- fromBlock: Block number to start from (inclusive).
- toBlock: (optional) Block number to end on (inclusive). If this is not given, the query will go on for a fixed amount of time or until it reaches the height of the dataset.
- includeAllBlocks: (optional) If true, the Network will include blocks that contain no data selected by data requests into its response.
- fields: (optional) A selector of data fields to retrieve. Common for all data items.
- logs: (optional) A list of log requests. An empty list requests no data.
- transactions: (optional) A list of transaction requests. An empty list requests no data.
- traces: (optional) A list of traces requests. An empty list requests no data.
- stateDiffs: (optional) A list of state diffs requests. An empty list requests no data.
Example Request
{
"logs": [
{
"address": ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"],
"topic0": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
],
"transaction": true
}
],
"fields": {
"block": {
"gasUsed": true
},
"log": {
"topics": true,
"data": true
}
},
"fromBlock": 16000000,
"toBlock": 16000000
}
Example Response
[
{
"header": {
"number": 16000000,
"hash": "0x3dc4ef568ae2635db1419c5fec55c4a9322c05302ae527cd40bff380c1d465dd",
"parentHash": "0x6f377dc6bd1f3e38b9ceb8c946a88c13211fa3f084622df3ee5cfcd98cc6bb16",
"gasUsed": "0x121cdff"
},
"transactions": [
{
"transactionIndex": 0
},
{
"transactionIndex": 124
},
{
"transactionIndex": 131
},
{
"transactionIndex": 140
},
{
"transactionIndex": 188
},
{
"transactionIndex": 205
}
],
"logs": [
{
"logIndex": 0,
"transactionIndex": 0,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000ffec0067f5a79cff07527f63d83dd5462ccf8ba4",
"0x000000000000000000000000e47872c80e3af63bd237b82c065e441fa75c4dea"
],
"data": "0x0000000000000000000000000000000000000000000000000000000007270e00"
},
{
"logIndex": 30,
"transactionIndex": 124,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000f42ed7184f3bdd07b0456952f67695683afd9044",
"0x0000000000000000000000009bbcfc016adcc21d8f86b30cda5e9f100ff9f108"
],
"data": "0x0000000000000000000000000000000000000000000000000000000032430d8b"
},
{
"logIndex": 34,
"transactionIndex": 131,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000001d76271fb3d5a61184ba00052caa636e666d11ec",
"0x00000000000000000000000074de5d4fcbf63e00296fd95d33236b9794016631"
],
"data": "0x000000000000000000000000000000000000000000000000000000000fa56ea0"
},
{
"logIndex": 35,
"transactionIndex": 131,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000074de5d4fcbf63e00296fd95d33236b9794016631",
"0x000000000000000000000000af0b0000f0210d0f421f0009c72406703b50506b"
],
"data": "0x000000000000000000000000000000000000000000000000000000000fa56ea0"
},
{
"logIndex": 58,
"transactionIndex": 140,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff",
"0x000000000000000000000000f41d156a9bbc1fa6172a50002060cbc757035385"
],
"data": "0x0000000000000000000000000000000000000000000000000000000026273075"
},
{
"logIndex": 230,
"transactionIndex": 188,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000ba12222222228d8ba445958a75a0704d566bf2c8",
"0x00000000000000000000000053222470cdcfb8081c0e3a50fd106f0d69e63f20"
],
"data": "0x00000000000000000000000000000000000000000000000000000002536916b7"
},
{
"logIndex": 232,
"transactionIndex": 188,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000053222470cdcfb8081c0e3a50fd106f0d69e63f20",
"0x00000000000000000000000088e6a0c2ddd26feeb64f039a2c41296fcb3f5640"
],
"data": "0x00000000000000000000000000000000000000000000000000000002536916b7"
},
{
"logIndex": 372,
"transactionIndex": 205,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000001116898dda4015ed8ddefb84b6e8bc24528af2d8",
"0x0000000000000000000000002796317b0ff8538f253012862c06787adfb8ceb6"
],
"data": "0x0000000000000000000000000000000000000000000000000000000018307e19"
},
{
"logIndex": 374,
"transactionIndex": 205,
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000002796317b0ff8538f253012862c06787adfb8ceb6",
"0x000000000000000000000000735b75559ebb9cd7fed7cec2372b16c3871d2031"
],
"data": "0x0000000000000000000000000000000000000000000000000000000018307e19"
}
]
}
]
Data requests
Addresses in all data requests must be in lowercase. All addresses in the responses will be in lowercase, too.
Logs
{
address: string[],
topic0: string[],
topic1: string[],
topic2: string[],
topic3: string[],
transaction: boolean
}
A log will be included in the response if it matches all the requests. An empty array matches no logs; omit all requests to match all logs. See EVM logs for a detailed description of data request fields.
Transactions
{
from: string[],
to: string[],
sighash: string[],
logs: boolean,
traces: boolean,
stateDiffs: boolean
}
A transaction will be included in the response if it matches all the requests. An empty array matches no transactions; omit all requests to match all transactions. See EVM transactions for a detailed description of data request fields.
Traces
{
type: string[],
createFrom: string[],
callFrom: string[],
callTo: string[],
callSighash: string[],
suicideRefundAddress: string[],
rewardAuthor: string[]
transaction: boolean,
subtraces: boolean
}
A trace will be included in the response if it matches all the requests. An empty array matches no traces; omit all requests to match all traces. See Traces for a detailed description of data request fields.
State diffs
{
address: string[],
key: string[],
kind: string[],
transaction: bool
}
A state diff will be included in the response if it matches all the requests. An empty array matches no state diffs; omit all requests to match all state diffs. See Storage state diffs for a detailed description of data request fields.
Data fields selector
A JSON selector of fields for the returned data items. Documented in the Field selectors section.