Reference
Node API Reference
Every HTTP route a node serves, with real responses · Estimated read time: 20 minutes
Accurate as of node 0.8.1.
TL;DR
Every Asentum node serves one HTTP API on its RPC port. It has two parts: plain REST routes (this page) and Ethereum-style JSON-RPC on POST / (see the JSON-RPC Reference). The REST routes are what the wallets, the SDK, @asentum/connect, the explorer and the Operator app use.
- Send a signed transaction with
POST /tx. PollGET /receipts/<txHash>until it has ablockNumber. - Read contracts with
POST /view, balances and nonces withGET /balance/<address>. - Get the finalized height and the newest block in one call with
GET /v3/heads. - Most errors come back as HTTP 200 with an
errororreasonfield. Always check the body, not only the status.
New to tip reads and finality? Read Fast Confirmations first. It explains the three states a transaction goes through and how to show each one.
Hosts
| Base URL | What it is |
|---|---|
| https://testnet.asentum.com | Public gateway. Every route on this page plus JSON-RPC on POST /. Load balanced over the public RPC nodes; one client IP always lands on the same node. |
| https://explorer.asentum.com | The explorer website. JSON-RPC works on POST /. REST routes work under /rpc, for example https://explorer.asentum.com/rpc/v3/heads. GET / is the explorer itself. |
| http://<server-ip>:8545 | Your own VPS node. Same routes, no gateway in front. See Run Your Own RPC. |
| http://127.0.0.1:9545 | The node inside the Operator desktop app. Only reachable from the same computer. |
The examples on this page use https://testnet.asentum.com. Do not point scrapers or backfills at the core validator nodes directly: they are watched and an IP that hammers them is blocked. Run your own node for heavy work (see Run Your Own RPC).
Conventions
- JSON in, JSON out. POST bodies are JSON (send
Content-Type: application/json). Every response is JSON except the explorer pages. - Numbers are decimal strings. Heights, balances, nonces and gas are strings like
"571252"so big values survive JSON. Amounts are in wei (1 ASE = 1018 wei). JSON-RPC uses hex quantities instead. - Timestamps are milliseconds. A block header's
timestampis Unix time in ms, for example"1792490329086". - Hashes and raw bytes are 0x hex. Transaction hashes are 32 bytes (64 hex characters).
- Errors are usually HTTP 200. A missing receipt, a missing block, a bad address and a rejected transaction all answer 200 with
errororaccepted: false, reason. Real non-200 codes: 404 for an unknown route, 429 for rate limits, 204 on some sync routes, 500 for a body that is not valid JSON, 503 from/readyor while a node shuts down. - Finalized or tip. The finalized height is final and never changes. The tip is the newest block on the best chain, usually about 3 blocks ahead. Some routes read the tip by default, some only finalized data. Each route below says which.
- CORS is open.
Access-Control-Allow-Origin: *on every route, so a web page can call the API directly. The gateway allows theContent-TypeandAuthorizationrequest headers. A node on its own allowsContent-Type,AcceptandX-Requested-With. - gzip. Large sync responses are gzipped when the client sends
Accept-Encoding: gzip(fetch in Node and browsers does this for you).
Addresses
Every route that takes an address accepts an ase1 address or the same 20 bytes as 0x hex. Responses are not consistent yet, so convert before you show anything to a user (toAse1 in @asentum/connect, or bech32 with prefix ase over the same 20 bytes):
| Returned as ase1 | Returned as 0x hex |
|---|---|
| /balance, /public-key, /asentum/account, /validators (address), /chain and /block headers (proposer), block transactions (to), /slashing/tombstones (slashingContract), /api (faucetAddress) | Receipts (sender, recipient, and addresses inside event data), /participation, /v3/committee, /consensus/liveness, /consensus/state, /genesis |
Contract arguments are different again: POST /view passes args to the contract exactly as you send them, and contracts store accounts as lowercase 0x hex. See POST /view.
Rate limits and caching
There are two layers of limits: the public gateway, and the node itself. Your own node only has the node layer.
| Where | Limit | When you hit it |
|---|---|---|
| Gateway, per IP | About 25 requests a second, with a burst of 100. Applies to every route that is not cached. | HTTP 429 with an HTML body and no Retry-After header. Back off and retry. |
| Node: block reads, per client IP | Burst of 240, then 4 a second. Counts GET /block/*, /block-raw/*, /receipts/* and the JSON-RPC block methods (eth_getBlockByNumber, eth_getBlockByHash, eth_getBlockTransactionCountByNumber/ByHash). | HTTP 429, JSON {"error":"too many block requests from this address, slow down"} and a Retry-After header in seconds. |
| Node: /v3/proposal | Burst of 1,500, then 4 a second. Only charged when a block is served. | Same 429 as block reads. |
| Node: transactions | Off by default. An operator can turn on a per-IP budget for POST /tx, /tx/batch and JSON-RPC sends (ASENTUM_TX_IP_PER_SEC, burst ASENTUM_TX_IP_BURST, default 30). | 429 {"error":"too many transactions from this address, slow down"}. A /tx/batch larger than the burst never passes. |
| Node: /probe/reachable | 10 a minute per IP. | 429 {"reachable":false,"error":"too many probes, try again in a minute"}. |
| Node: POST /v3/precommit | Burst of 20, then 10 a second per socket IP. Body capped at 64 KB. | 429 with accepted: false, or 413 for a large body. |
Behind the gateway the node sees your real IP (the gateway is a trusted proxy), so the node limits are per client, not shared. Polling one receipt a second is well inside every limit.
| Cached response | Cache | Where |
|---|---|---|
| GET /chain | 1 s | Gateway (X-Cache-Status header) |
| GET /validators | 2 s (5 s on explorer.asentum.com/rpc), plus 500 ms in the node | Gateway and node |
| GET /participation | 2 s (5 s on explorer.asentum.com/rpc), per query string | Gateway |
| GET /metadata | 30 s | Gateway |
| GET /v3/heads | 250 ms in the node, Cache-Control: max-age=1 | Node |
| GET /v3/proposal, GET /v3/commit?quorum=min | Finalized heights: Cache-Control: public, max-age=300 | Node |
The cached gateway routes can be a second or two behind the chain. If you need the exact current height, use /v3/heads. The gateway gives each request 30 seconds before it fails with a 504.
Chain and health
The finalized height and the tip (newest block on the best chain) in one tiny response. The cheapest way to drive a block counter or decide which heights to fetch.
Request
curl -s https://testnet.asentum.com/v3/heads
Response
{"finalized":"571252","tip":"571255"}finalizedis the same height aseth_blockNumberand/chain.tipis usually 2 to 4 blocks ahead.- A transaction in a block at or below finalized can never change. Between finalized and tip it can still move to another block (rare). See Fast Confirmations.
Chain id, the finalized height, this node's mempool size and the finalized block header.
Request
curl -s https://testnet.asentum.com/chain
Response
{
"chainId": "1423",
"height": "571241",
"mempoolSize": 33,
"latestHeader": {
"parentHash": "0x17063346ef1453...", "stateRoot": "0x4e4fe3fb8538bf...",
"transactionsRoot": "0x7122dea29dd4f4...", "receiptsRoot": "0xd2755b10f4287d...",
"number": "571241", "timestamp": "1792490301086",
"gasLimit": "30000000", "gasUsed": "937818", "baseFeePerGas": "1",
"proposer": "ase13mqx732wza4lvtv7e7a985mlxft6tdwgduvcky",
"lastCommitBitmap": "0xfeea56f79aed2b7e7dfd1a0aa0000000"
}
}heightis the finalized height, not the tip. Cached for 1 second on the gateway.mempoolSizeis this node's own mempool. Each node has its own.- There is no block hash in the header JSON. Get it from
eth_getBlockByNumberor from a receipt'sblockHash.
Liveness probe. Answers as long as the HTTP server is up.
Request
curl -s https://testnet.asentum.com/health
Response
{"status":"ok"}- It does not check sync, peers or block production. Use /v3/heads and compare with the public gateway for that.
Readiness probe meant to report whether the chain is advancing: 200 with status ready, or 503 with status stale.
Request
curl -s https://testnet.asentum.com/ready
Response
{"status":"ready","height":"571241","lastBlockAgeSec":0}- Do not rely on it in 0.8.1. It compares the clock in seconds with a block timestamp in milliseconds, so
lastBlockAgeSecis always 0 and it always answers ready, even on a stalled node. To detect a stall, check that/v3/headskeeps moving.
Node identity: chain id, finalized height and the node's dev faucet address.
Request
curl -s https://testnet.asentum.com/api
Response
{"name":"asentum-node","version":"0.0.0-alpha.0","chainId":"1423","height":"571241","faucetAddress":"ase1hm4ja2dgczv2lm599z2uwr8szu0zsz8twu69nm"}versionis a fixed placeholder, not the node release. There is no API route that reports the release; on a VPS runasentum-validator status.
Chain details in the EIP-3085 shape (wallet_addEthereumChain), plus a few extras.
Request
curl -s https://testnet.asentum.com/metadata
Response
{
"chainId": "0x58f",
"chainName": "Asentum Testnet",
"nativeCurrency": {"name":"Asentum","symbol":"ASE","decimals":18},
"rpcUrls": ["https://testnet.asentum.com"],
"blockExplorerUrls": ["https://testnet.asentum.com"],
"chainIdDecimal": "1423",
"currentBlock": "571241",
"faucetUrl": "https://testnet.asentum.com/dev/faucet"
}- Cached for 30 seconds on the gateway, so currentBlock lags.
faucetUrlis not a public faucet: the node route is off by default. Get test ASE from the airdrop dashboard (see Get Test ASE).- The real block explorer is
https://explorer.asentum.com.
The genesis spec a new node uses to join: chain id, gas limit, base fee, initial accounts and validators, genesis hash and state root.
Request
curl -s https://testnet.asentum.com/genesis
Response
{"chainId":"1423","gasLimit":"30000000","baseFeePerGas":"1",
"initialAccounts":[{"address":"0xbeeb2ea9a8c098afee852895c70cf0171e2808eb","balance":"2000000000000000000000000"}, ...],
"initialValidators":[...], "initialAseHolders":[...], "genesisHash":"0x...", "genesisStateRoot":"0x..."}- About 17 KB. Addresses are 0x hex. A node without a stored spec answers {"error":"no genesis spec available", ...}.
Accounts
Balance and nonces of an account or contract, read at the tip.
| Parameter | Meaning |
|---|---|
| address | ase1 address (or 0x hex of the same 20 bytes). |
Request
curl -s https://testnet.asentum.com/balance/ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl
Response
{"address":"ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl","balance":"460922160143819003814070","nonce":"0","pendingNonce":"0"}balanceis in wei.nonceis the account's nonce at the tip.pendingNoncealso counts this sender's transactions queued in this node's mempool. Sign your next transaction withpendingNonceso several sends in a row get n, n+1, n+2.- A bad address answers 200 with
{"error":"invalid address","message":"..."}. An address that has never been used answers balance and nonce 0. - There is no /nonce route. This is the nonce route.
The full account record: balance, nonce, code hash and storage root.
| Parameter | Meaning |
|---|---|
| address | ase1 address (or 0x hex). |
Request
curl -s https://testnet.asentum.com/asentum/account/ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl
Response
{"address":"ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl","exists":true,
"balance":"461078849858191139118984","nonce":"0",
"codeHash":"0x331901b8e05f019598da59892039dd65f5ef4deb407ad68410ad8dc41e8c7770",
"storageRoot":"0x410cd2facf28f99becdedc72d0c14d4354ae8920680cec6c5fce7409647f4685",
"height":"571334"}- An unknown address answers
{"address":"ase1...","exists":false,"height":"..."}. codeHashis the BLAKE3 hash of the contract source, so you can check a contract against its published source (see Verifying Contracts). For accounts that are not contracts it is all zeros.
Whether the chain already has this account's public key on record. Once it does, a wallet may send smaller transactions without the key (key-once, live since block 528,000).
| Parameter | Meaning |
|---|---|
| address | ase1 address (or 0x hex). |
Request
curl -s https://testnet.asentum.com/public-key/ase1r3m77x6eyxt0emxg9jepm8ah7gy8kxmw90w5ty
Response
{"address":"ase1r3m77x6eyxt0emxg9jepm8ah7gy8kxmw90w5ty","recorded":true,
"keyHash":"0x1c77ef1b592196fcecc82cb21d9fb7f2087b1b6e3ab844b1892e3b1c844f3529",
"keylessActive":true,"keylessFromBlock":"528000","nextBlock":"571405","canOmitKey":true}- Send a key-less transaction only when
canOmitKeyis true. Otherwise it is rejected withsender public key not on record.
Blocks
A block with its decoded transactions and receipts.
| Parameter | Meaning |
|---|---|
| number | Block height, decimal. |
| tip | Optional. 1 (or true) also answers heights above the finalized head, from the pending block on the node's best chain. Default 0: finalized blocks only. |
Request
curl -s https://testnet.asentum.com/block/571252 curl -s "https://testnet.asentum.com/block/571255?tip=1"
Response
{
"header": {"parentHash":"0xd4b6923e...","stateRoot":"0x9f806bb4...","number":"571252",
"timestamp":"1792490329086","gasUsed":"446457","baseFeePerGas":"1",
"proposer":"ase1vcfv3x57vu2gdxm6ergmctzu9xgp62lvt242ah", ...},
"transactions": [
{"kind":"call",
"body":{"chainId":"1423","nonce":"8","maxPriorityFeePerGas":"0","maxFeePerGas":"1",
"gasLimit":"20000000","to":"ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl","value":"0","dataSize":118},
"call":{"method":"swapExactIn","args":["1","0xc24044dccea3ba8b362e1c85fa16ddc658022269","45933719102783","9301718868854625"]},
"publicKeyLength":1952,"signatureLength":3309}
],
"receipts": [ {"txHash":"0x3261a75d...","txIndex":0,"success":true, ...} ],
"finalized": true
}
# with ?tip=1 for a height above finality:
{"header":{...},"transactions":[...],"receipts":[...],
"finalized":false,"confirmations":"2","auraHash":"0xc8e549d4cdbb5557..."}- Without
?tip=1, a height above the finalized head answers 200{"error":"not found","number":"571255"}. So does a height this node does not have (see History). kindistransfer,deployorcall. For calls,callis the decoded method and args. Public keys and signatures are left out, only their sizes are shown. A key-less transaction haskeyless: trueand asender.- Transactions in this JSON have no hash. Match them to receipts by position (
txIndex), or use the receipts'txHash. - Counts against the node block-read budget. A browser navigating to /block/<n> gets the explorer page instead of JSON (see below).
Only the receipts of a block. Smaller than the full block; what indexers want.
| Parameter | Meaning |
|---|---|
| number | Block height, decimal. |
| tip | Optional, as for /block. Default 0. |
Request
curl -s https://testnet.asentum.com/block/571252/receipts
Response
{"blockNumber":"571252","receipts":[{"txHash":"0x3261a75d...","blockNumber":"571252","txIndex":0,
"sender":"0x1c77ef1b592196fcecc82cb21d9fb7f2087b1b6e","recipient":"0x63f640a9b98b6f28e06cb5cc0fce5d72b6c0eabd",
"kind":"call","success":true,"gasUsed":"88573","events":[...],"returnValue":"9443894822196084"}, ...],
"finalized":true}- A height that is not available answers
{"blockNumber":"...","receipts":[]}with nofinalizedfield. An empty list does not always mean an empty block: check thatfinalizedis present.
The block as SSZ bytes in hex, for nodes and tools that decode the wire format themselves.
Request
curl -s https://testnet.asentum.com/block-raw/571252 curl -s https://testnet.asentum.com/raw-block/571252
Response
{"number":"571252","bytes":"0xd4b6923e48f3d8b5..."}
{"rawBlock":"0xd4b6923e48f3d8b5..."}- Two routes, same bytes, different field names. /block-raw counts against the block-read budget; /raw-block does not. Missing block: {"error":"not found"} (200) on /block-raw, HTTP 404 on /raw-block.
- Unlike /block, both also answer a height above the finalized head with the pending block, and nothing in the response says it is not final. Compare the height with /v3/heads.
Transactions and receipts
Submit one signed transaction. This is the only way to submit: eth_sendRawTransaction is not supported.
| Parameter | Meaning |
|---|---|
| rawTx | The signed transaction, SSZ encoded, as a 0x hex string. The SDK, @asentum/connect and the wallets build and sign it (ML-DSA-65). There is no unsigned or JSON transaction format. |
Request
curl -s -X POST https://testnet.asentum.com/tx -H 'content-type: application/json' \
-d '{"rawTx":"0x..."}'Response
{"accepted":true,"txHash":"0x6c91b3cec4c0cf9da739221d2e8c1fac73df528c1225c918ce6135b02dd4d5cd"}
{"accepted":false,"reason":"stale nonce: account is at 153, tx has 152"}accepted: truemeans this node checked the transaction and put it in its mempool. It has not run yet and it is not in a block. It can still fail, or be dropped from the mempool. Poll/receiptsto know what happened. Do not tell a user it is done because ofaccepted.- A rejected transaction has no
txHashand never reaches the chain. Show thereason. - Reasons you will see:
stale nonce: account is at N, tx has M(nonce already used),nonce too far ahead: ...,duplicate(this exact transaction is already in the mempool),duplicate nonce(another transaction with that nonce is queued),insufficient balance for value plus gas,gas limit too low for a contract call: min 50000 ...,wrong chainId: ...,invalid signature,sender public key not on record: ...,mempool full,sender queue full,blocked: this wallet is blocked on the testnet for spam, and for bad inputmissing rawTx (expected hex string),invalid rawTx hex: ...,invalid rawTx SSZ: .... - The node forwards an accepted transaction to the block producers. There is no route that lists the mempool or a pending transaction (
txpool_*andeth_pendingTransactionsdo not exist). If a transaction never gets a receipt, re-sign it with the same nonce and submit again. - Submit and poll through the same host. On the gateway one IP always reaches the same node, which keeps pendingNonce consistent.
Submit several signed transactions in one request. They are admitted one by one in the order given, so nonce order is kept.
| Parameter | Meaning |
|---|---|
| transactions | Array of {"rawTx":"0x..."} objects. |
Request
curl -s -X POST https://testnet.asentum.com/tx/batch -H 'content-type: application/json' \
-d '{"transactions":[{"rawTx":"0x..."},{"rawTx":"0x..."}]}'Response
{"accepted":1,"rejected":1,"total":2,"results":[
{"accepted":true,"txHash":"0x..."},
{"accepted":false,"reason":"duplicate nonce"}]}- Each result has the same shape as POST /tx. One rejection does not stop the rest, so a gap in nonces can leave later transactions waiting.
- A body without a transactions array answers HTTP 400 {"error":"expected { transactions: [{ rawTx }] }"}.
The receipt of a transaction: whether it ran, in which block, gas, events and return value.
| Parameter | Meaning |
|---|---|
| txHash | 32-byte transaction hash, 0x hex. |
| tip | Optional. Default 1: also returns receipts from blocks that are on the best chain but not final yet (finalized: false). 0: finalized receipts only. |
Request
curl -s https://testnet.asentum.com/receipts/0x3261a75d9804a1f92a08d88ca0878ad5edac0bf1e63c408d8f2181a6ffcd7ea1
Response
{
"txHash": "0x3261a75d9804a1f92a08d88ca0878ad5edac0bf1e63c408d8f2181a6ffcd7ea1",
"blockNumber": "571252",
"txIndex": 0,
"sender": "0x1c77ef1b592196fcecc82cb21d9fb7f2087b1b6e",
"recipient": "0x63f640a9b98b6f28e06cb5cc0fce5d72b6c0eabd",
"kind": "call",
"success": true,
"gasUsed": "88573",
"events": [{"name":"Transfer","data":{"from":"0x1c77ef1b...","to":"0x63f640a9...","value":"45933719102783"}}],
"returnValue": "9443894822196084",
"finalized": true,
"confirmations": "8",
"blockHash": "0x5c21659e3aa85b139eece5ac1e7dca7f184695cd2ee377427ddd8e69f481e90d"
}
# not in a block (yet), HTTP 200:
{"error":"receipt not found","txHash":"0xabab...abab"}- The success field is
success(true or false). There is nostatusfield on this route.status: "0x1"is the JSON-RPC receipt (eth_getTransactionReceipt). Code that checksr.status === 1against/receiptsreports every transaction as failed. - "Not found" is HTTP 200. A receipt exists only when the body has a
blockNumber. A failed transaction also has anerrorfield (its revert reason), but it has ablockNumbertoo, so test forblockNumber, not forerror. finalized: falsemeans the block is on the best chain but not final. In rare cases that block loses to a sibling: the receipt disappears, the transaction goes back to the mempool and normally lands again in a later block, with a newblockNumberandblockHash(and possibly a different result). Onlyfinalized: trueis permanent. While not final the receipt also hasauraHash.confirmationsis tip height minusblockNumber, plus 1.sender,recipientand addresses in event data are 0x hex. For a deploy,recipientis the new contract.- Wait for this receipt, not for a block height. A transaction can land several blocks after you sent it. The copy-paste polling helper is in Fast Confirmations.
- Counts against the node block-read budget (a burst of 240, then 4 a second). Poll about once a second.
Contract reads
Run a contract method read-only. No transaction, no gas paid, nothing is written.
| Parameter | Meaning |
|---|---|
| contract | Contract address, ase1 (or 0x hex). |
| method | Method name, a string. |
| args | Optional array of arguments, passed to the method exactly as sent. |
| blockTag | Optional. "tip" (default), "latest" or "pending": the newest block. "finalized" or "safe": the finalized state. Anything else is rejected. Block numbers are not accepted: there are no historical state reads. |
Request
curl -s -X POST https://testnet.asentum.com/view -H 'content-type: application/json' \
-d '{"contract":"ase1v0myp2de3dhj3crvkhxqlnjaw2mvp64a9efkpl","method":"getPool","args":["3"]}'Response
{"ok":true,
"returnValue":"{\"id\":\"3\",\"token0\":\"0x50cb8d861050ad0052959e0f942c5347842346f1\",\"reserve0\":\"1046355455215355449482246\", ...}",
"events":[],"gasUsed":"21800","blockNumber":"571273","finalized":false}
# with "blockTag":"finalized"
{"ok":true,"returnValue":"...","events":[],"gasUsed":"21800","blockNumber":"571271","finalized":true}
# errors (HTTP 200)
{"ok":false,"reason":"contract method not found: nope"}
{"ok":false,"reason":"invalid \"blockTag\" (expected \"latest\", \"tip\" or \"finalized\")"}- Address arguments must be 0x hex. Contracts store accounts as lowercase 0x hex (that is what
msg.senderis), and/viewdoes not convertargs.getValidatoron the staking contract returnsnullfor an ase1 argument and the record for its 0x form.viewCallin the SDK andclient.viewin@asentum/connectconvert ase1 arguments for you; with raw/viewusease1ArgsToHex. returnValueis whatever the method returns. Many contracts return a JSON string (asgetPooldoes above), so you may needJSON.parse. BigInts come back as decimal strings.- Reads the tip by default, so right after your transaction is in a block,
/viewalready shows its effect. Behind a load balancer, checkblockNumberis at least your receipt's block before trusting the read. eventslists what the method would have emitted. Nothing is stored.- A body that is not valid JSON answers HTTP 500 {"error":"internal","message":"invalid JSON body: ..."}.
Validators and consensus
Every bonded validator with stake and committee state, plus the current epoch and entry window.
Request
curl -s https://testnet.asentum.com/validators
Response
{"count":812,"stakingContract":"ase1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpf05455",
"epoch":{"head":"571247","current":"1904","blocksPerEpoch":"300","entryWindowBlocks":"100",
"currentStart":"571200","currentEnds":"571500","currentWindowOpensAt":"571400","nextWindowOpensAt":"571400"},
"validators":[{"address":"ase1n2kslv6ra3nkgxec2u9vntvzcrkrlpcwp6yl3a","stake":"20000000000000000000000000",
"bondedAt":"0","status":"active","activeAtBlock":"10","pubKeySize":3906,
"broadcastUrl":"http://204.168.132.194:8545","committeeState":"signing",
"consecutiveMisses":0,"lastVotedHeight":"571249","nextCommitteeEntryAt":null}, ...]}committeeStateis one ofsigning,waiting_epoch_entry,dormant,pending_bondortombstoned.signingmeans the validator has voted and is not dormant. It does not mean it holds a committee seat in this epoch: the committee is 100 seats drawn each epoch (see /v3/committee).- About 260 KB with 800+ validators. Cached 2 s on the gateway and 500 ms in the node, so poll it every few seconds at most.
Who actually signed recent blocks: per validator signed blocks, ratio and last signed block over the last N blocks.
| Parameter | Meaning |
|---|---|
| lookback | Optional. Blocks to scan, 1 to 10,000. Default 1,000. |
Request
curl -s "https://testnet.asentum.com/participation?lookback=50"
Response
{"head":"571247","scannedFrom":"571198","lookback":50,"totalValidators":812,
"signing":109,"idle":0,"dormant":703,"recentWindow":50,
"validators":[{"address":"0x9aad0fb343ec67641b38570ac9ad82c0ec3f870e","stake":"20000000000000000000000000",
"signedBlocks":50,"participationRatio":1,"lastSignedBlock":"571247","status":"signing"}, ...]}- Addresses are 0x hex.
statushere issigning(signed in the last 100 blocks, or the whole window if shorter),idleordormant, judged from the blocks themselves. - Expensive on the node for large lookbacks. Cached per query string for 2 s on the gateway.
The voting committee of an epoch in canonical order (the order commit bitmaps use), the leader base, and whether the committee lottery chose it.
| Parameter | Meaning |
|---|---|
| epoch | Optional epoch number. Default: the current epoch (finalized height / 300). |
Request
curl -s https://testnet.asentum.com/v3/committee
Response
{"epoch":"1904","lottery":true,
"committee":["0x1cb5e65c853b8dfe4c3202a728d84e902c426a79","0x72b2064cdd4c02e21577f22baee166e276aff5f2", ...100 entries],
"leaderBase":["0x1cb5e65c853b8dfe4c3202a728d84e902c426a79", ...]}- Addresses are 0x hex. An epoch the node cannot compute answers HTTP 404 {"error":"..."}.
Whether a height is finalized on this node, and the Aura hash of the block finalized there.
| Parameter | Meaning |
|---|---|
| height | Required block height. |
Request
curl -s "https://testnet.asentum.com/v3/finalized?height=571000"
Response
{"height":"571000","finalized":true,"auraHash":"0x03da607b14ab33a66148463ae6168160d11bcbe7cfaac740d2d8311da9db00d5"}
{"height":"571443","finalized":false}The node's liveness tracker: last vote height and consecutive misses for every tracked validator, the dormancy threshold and the core validators.
Request
curl -s https://testnet.asentum.com/consensus/liveness
Response
{"head":"571247","livenessWindow":"100","maxConsecutiveMisses":50,
"baked":["0x1cb5e65c...","0x72b2064c...","0x86f62ed9...","0x9aad0fb3..."],
"tracked":[{"address":"0x004b8ddacfd6d729644080ee30021f1472ccf697","lastVotedHeight":"569999","consecutiveMisses":0,"baked":false}, ...],
"certPersist":{...},"voteBroadcast":{...}}- About 40 KB. Addresses are 0x hex. This is the data behind committeeState in /validators.
Validators that have been slashed and tombstoned.
Request
curl -s https://testnet.asentum.com/slashing/tombstones
Response
{"count":0,"slashingContract":"ase1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz8upr6t","tombstones":[]}Deprecated. A snapshot from the old v2 BFT engine, kept so old tools do not break.
Request
curl -s https://testnet.asentum.com/consensus/state
Response
{"height":"550198","round":"0","phase":"propose","proposer":"0x9aad0fb343ec67641b38570ac9ad82c0ec3f870e",
"validatorCount":83,"quorumThresholdStake":"53786493388964146162534705",
"prevoteCounts":[],"precommitCounts":[],"committedBlockHash":null}- Do not use it. It does not follow the live chain: in the sample above it is about 21,000 blocks behind, and it can refresh rarely or go backwards. Use
/v3/headsfor heights,/v3/committeefor the committee and the block header'sproposerfor who produced a block.
Governance
Read-only wrappers around the governance contract. Voting and proposing are contract calls sent with POST /tx. Background in Protocol Governance.
| Route | Returns |
|---|---|
| GET /governance/proposals | {"proposals":[...]}: the newest proposals (up to 50). |
| GET /governance/proposal/<id> | {"proposal":{...},"amendments":[...]}. An unknown id gives {"proposal":null,"amendments":[]}. |
| GET /governance/locked/<address> | {"address":"ase1...","locked":"0"}: ASE locked in votes, in wei. |
| GET /governance/withdrawable/<address> | {"address":"ase1...","locked":"0","withdrawable":"0","positions":[]}. |
| GET /governance/libraries | {"libraries":[]}. Library approval is disabled, so always empty. |
| GET /governance/library/<name> | {"library":null}. |
curl -s https://testnet.asentum.com/governance/locked/ase1hm4ja2dgczv2lm599z2uwr8szu0zsz8twu69nm
{"address":"ase1hm4ja2dgczv2lm599z2uwr8szu0zsz8twu69nm","locked":"0"}On failure each route answers 200 with an empty value and an error field.
Node operator and sync routes
These routes are how nodes sync from each other and how pull-mode validators (Operator app, nodes behind NAT) get blocks and send votes. They are public because nodes need them, but they are internal: the formats are binary wire frames and can change between releases without notice. Apps should not use them.
| Route | What it does |
|---|---|
| GET /v3/proposal?height=H | {"available":true,"height":"H","wire":"0x...","auraHash":"0x..."}: the block frame applied at H. HTTP 204 when this node does not have it yet. The main sync route for pull-mode nodes. |
| GET /v3/commit?height=H[&quorum=min] | The GRANDPA commit certificate for H as JSON (round, target hash, signer bitmap, signatures). 204 when H is not finalized here or the certificate has aged out (the node keeps about 1,100 rounds). |
| POST /v3/precommit | Body {"wire":"0x<vote frame>"}. A committee member sends its signed precommit. Answers {accepted, folded?, reason, finalized}. Signature and committee membership are always checked. |
| GET /v3/route-stats | Request and byte counters for the sync and vote routes on this node, plus relay and sync-pool state. |
| GET /v3/vote-metrics | Per-voter precommit timing on a producing node. 404 when v3 is not engaged. |
| GET /probe/reachable?url=<http(s) URL> | Asks this node to fetch <url>/metadata and says whether it answered as a chain node: {"reachable":true,"latencyMs":255,"chainId":"0x58f"}. Used by the Operator app to check that a validator's address is reachable. Public addresses only, 10 a minute per IP. |
| GET /libp2p-addrs | {"enabled":true,"peerId":"","addresses":[]} on current nodes (blocks and votes travel over ANT on port 9000). |
| GET /cron-status | {"enabled":true,"envVar":null}: whether this node runs scheduled contract calls. |
| GET /consensus/proposal, GET /consensus/votes | Old v2 pull routes. Answer {"available":false}. |
| POST /consensus/proposal, POST /consensus/vote | Old v2 push routes. Answer {"accepted":false,"reason":"v2 consensus is off while v3 is engaged"}. |
| GET /debug/state | Full state dump for debugging. 404 unless the node runs with ASENTUM_DEBUG_ENDPOINTS=1. |
Faucet and dev routes
A node's built-in dev faucet. Off by default: a node answers HTTP 404 {"accepted":false,"reason":"faucet not enabled on this node"} unless its operator sets ASENTUM_DEV_FAUCET=1, which only makes sense on a local devnet.
| Parameter | Meaning |
|---|---|
| to | Recipient address (ase1 or 0x hex). |
| amount | Amount in wei, as a string. At most 100,000 ASE per drip. |
- For test ASE, use the airdrop dashboard (see Get Test ASE). The SDK's
requestFaucet()andfaucet()call this route and only work against a devnet node with it turned on. - On
testnet.asentum.comandexplorer.asentum.com, POST /dev/faucet is a separate service: the one-time validator bond drip the installer and the Operator app use. It is not a general faucet.explorer.asentum.com/api/dev/faucetis retired and answers HTTP 410.
| Route | Status |
|---|---|
| POST /dev/produce-block | Old v2 route. Does nothing on the live chain and answers {"skipped":true,"reason":"not the scheduled proposer"}. |
| GET /playground | Retired. The gateway redirects it to https://ide.asentum.com. |
Explorer pages on a node
A node also serves a small built-in explorer. A browser navigation (or Accept: text/html without Sec-Fetch-Dest) to these paths gets HTML; a fetch() gets the JSON described above:
GET /: the explorer page.POST /is JSON-RPC.GET /block/<n>: HTML for a browser, block JSON for fetch.GET /address/<ase1 or 0x>andGET /tx/<hash>: HTML only. For data use/balance/<address>and/receipts/<hash>. A fetch to these paths answers 404.GET /governance,/governance/<id>,/governance/libraries: HTML for a browser.
On the public hosts, use explorer.asentum.com, which has its own full index.
What does not exist
Some routes are mentioned in older posts and tools but are not served by any node:
GET /nonce/<address>: use/balance/<address>(nonceandpendingNonce).GET /tx/<hash>as JSON: use/receipts/<hash>, oreth_getTransactionByHashfor the transaction body.- A mempool or pending-transaction listing: there is none, on REST or JSON-RPC.
GET /metrics(Prometheus): not served. Monitor with/v3/heads,/validatorsand the node journal (see Monitoring & Logs).- WebSocket (
/ws,eth_subscribe): not served. Poll instead. - Historical state:
/view,/balanceand JSON-RPC state reads only know the tip and the finalized state, not an older block.
An unknown route answers HTTP 404 {"error":"not found","method":"GET","path":"/nonce/ase1..."}.
History, indexing and backfills
- Nodes do not keep full history. A node serves blocks and receipts from the point it was synced from a snapshot onward. Below that,
/blockanswers not found,/receiptsanswers receipt not found andeth_getTransactionByHashreturns null, even for transactions that did happen. On 20 October 2026 the public pool served roughly the last 40,000 blocks, and the start differs from node to node. - There is no public archive RPC and no bulk export. The explorer keeps its own full index for its pages; it is not a public API.
- For an indexer, page by block range, not by transaction lists. Walk heights with
/block/<n>/receipts(receipts and events, decoded) oreth_getLogs(at most 10,000 blocks per call, finalized blocks only). Block ranges never shift as new transactions arrive. - Stay inside the limits. Block reads on a node are a burst of 240, then 4 a second per IP. Backfilling through the public gateway is slow by design. Run your own node next to your indexer (see Run Your Own RPC) and keep it running so it collects history from then on.
- Contract deployments are receipts with
kind: "deploy";recipientis the new contract andsenderthe deployer. There is no separate registry route. - Live window. To show activity before finality, also read the heights between finalized and tip with
?tip=1and replace them when a block hash changes. The full recipe is in Fast Confirmations: Indexers.
Read next