REST API endpoints for programmatic access to Zenith EVM Explorer data
Base URL
https://explorer.testnet.zenith.network/apiAdd the network to a wallet (MetaMask → Add network) or point tooling at it with these values.
| Network name | Zenith Testnet |
| Chain ID | 936485 (0xe4a25) |
| RPC URL | http://127.0.0.1:9200 |
| Currency symbol | ZTH |
| Block explorer URL | https://explorer.testnet.zenith.network |
Chain ID is 936485 — verify anytime with curl "https://explorer.testnet.zenith.network/api?module=proxy&action=eth_chainId". Need testnet ZTH for gas? Use the faucet.
This is a public testnet explorer with no API key. Limits are per client IP (a shared or proxied IP shares its bucket) and are set to meet or exceed a stock Blockscout's defaults, so testing here is never more constrained. Exceeding a limit returns HTTP 429 with a Retry-After header (or the Etherscan-style {status:"0"} envelope).
proxy module — matches Blockscout's most generous default (its no-key IP tier is 3/s).txlist, tokentx) return up to 10,000 rows; getLogs up to 1,000 — paginate with page/offset./blocksFetch paginated EVM blocks. Returns an array of block objects along with a total count for pagination.
| Name | Type | Required | Description |
|---|---|---|---|
page | number | Optional | Page number, starting from 1. Defaults to 1. |
pageSize | number | Optional | Number of results per page. Defaults to 25, max 100. |
Example Request
curl "https://explorer.testnet.zenith.network/api/blocks?chain=evm&page=1&pageSize=10"Example Response
{
"data": [
{
"id": 1,
"blockNumber": "13800",
"blockHash": "0x01a6ee89...c893aa04fd",
"parentHash": "0xf3c2a1b4...d8e9f01234",
"timestamp": "2026-02-27T10:30:00.000Z",
"txCount": 1,
"gasUsed": "21000",
"gasLimit": "30000000",
"stateRoot": "0xab12cd34...ef567890ab"
}
],
"total": 13800
}/transactionsFetch paginated EVM transactions. Returns transaction objects with decoded data when available.
| Name | Type | Required | Description |
|---|---|---|---|
page | number | Optional | Page number, starting from 1. Defaults to 1. |
pageSize | number | Optional | Number of results per page. Defaults to 25, max 100. |
Example Request
curl "https://explorer.testnet.zenith.network/api/transactions?chain=evm&page=1&pageSize=5"Example Response
{
"data": [
{
"id": 1,
"txHash": "0x160bd584...747f1a2b3c",
"blockNumber": "13800",
"txIndex": 0,
"fromAddress": "0x742d35Cc...5f2bD18",
"toAddress": "0x1234567890Ab...12345678",
"value": "0",
"gasUsed": "21000",
"gasPrice": "1000000000",
"inputData": "0x...",
"status": 1,
"timestamp": "2026-02-27T10:30:00.000Z"
}
],
"total": 13800
}/gasReturns the current gas price, base fee, and priority fee from the Zenith EVM RPC. All values are in wei, returned as strings.
No parameters required.
Example Request
curl "https://explorer.testnet.zenith.network/api/gas"Example Response
{
"gasPrice": "1000000000",
"baseFee": "875000000",
"priorityFee": "125000000"
}/healthHealth check endpoint returning the status and latency of the explorer's backing services (database, EVM RPC, and internal indexing services). Returns HTTP 200 when all critical services are online, 503 when degraded.
No parameters required.
Example Request
curl "https://explorer.testnet.zenith.network/api/health"Example Response
{
"status": "ok",
"timestamp": "2026-02-27T10:30:00.000Z",
"services": {
"database": {
"status": "online",
"latency": 2
},
"evm": {
"status": "online",
"latency": 12,
"blockNumber": "13800"
}
}
}/faucetFaucet status: whether it is enabled, the target network, the per-claim ZTH amount, and the cooldown window (minutes + a human label).
No parameters required.
Example Request
curl "https://explorer.testnet.zenith.network/api/faucet"Example Response
{
"enabled": true,
"networkId": "final-testnet",
"networkLabel": "Testnet",
"zthAmount": "100",
"cooldownMinutes": 30,
"cooldownLabel": "30 min"
}/faucetRequest testnet ZTH (native gas token) for an address. Subject to a per-address and per-IP cooldown (HTTP 429 with Retry-After when on cooldown).
| Name | Type | Required | Description |
|---|---|---|---|
address | string | Required | Recipient 0x address (JSON body: { "address": "0x…" }). |
Example Request
curl -X POST "https://explorer.testnet.zenith.network/api/faucet" -H "Content-Type: application/json" -d '{"address":"0x0000000000000000000000000000000000000000"}'Example Response
{
"ok": true,
"zthTxHash": "0x0076230d...247bc762",
"zthAmount": "100"
}/streamReal-time event stream using Server-Sent Events (SSE). Subscribe to a channel to receive live updates as new blocks and transactions are indexed. Events are delivered via PostgreSQL LISTEN/NOTIFY.
| Name | Type | Required | Description |
|---|---|---|---|
channel | string | Optional | The event channel to subscribe to. Defaults to "evm:block". |
| Channel | PG Channel | Description |
|---|---|---|
evm:block | evm_block | New EVM blocks and transactions indexed from the Zenith EVM eth-rpc |
evm:transaction | evm_block | Alias for evm:block (transactions are included in block events) |
Events follow the standard SSE format. Each event contains a JSON payload in the data field. Only events with a type or correlationId field are forwarded. The server sends periodic keepalive comments (lines starting with :) to maintain the connection.
Example: Connect with curl
curl -N "https://explorer.testnet.zenith.network/api/stream?channel=evm:transaction"Example: Connect with JavaScript
const source = new EventSource("/api/stream?channel=evm:transaction");
source.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("New event:", data);
};
source.onerror = (err) => {
console.error("SSE error:", err);
};Example: EVM Transaction Event
data: {"type":"evm","id":"0x160bd584...","hash":"0x160bd584...","timestamp":"2026-02-27T10:30:00.000Z","description":"EVM tx 0x742d35...→ 0x123456..."}/apiAn Etherscan-compatible endpoint so standard EVM tooling can verify contracts against this explorer. Submitted source is compiled with solc and matched against the on-chain runtime bytecode (tolerating trailing metadata & immutables). Verified contracts decode across the explorer and expose their source on the address page.
module=contract)| action | Description |
|---|---|
| verifysourcecode | POST source (solidity-single-file or solidity-standard-json-input) → returns a GUID. Compiles + bytecode-matches, then stores the ABI & source. |
| checkverifystatus | Poll a GUID: “Pass - Verified”, “Pending in queue”, or a failure reason. |
| getabi | Returns the verified ABI as a JSON string. |
| getsourcecode | Returns source, ContractName, CompilerVersion, OptimizationUsed, Runs, ConstructorArguments, etc. |
| getcontractcreation | For contractaddresses (CSV, up to 10): returns each contract's creator address and creation txHash. |
| verifyproxycontract | Detects a proxy's implementation (EIP-1967 / EIP-1822 / beacon slots, or expectedimplementation) and links it → GUID. |
| checkproxyverification | Poll a proxy-verification GUID; on success getsourcecode reports Proxy:"1" + Implementation. |
Verify with Foundry
forge verify-contract \
--verifier etherscan \
--verifier-url https://explorer.testnet.zenith.network/api \
--compiler-version v0.8.35+commit.47b9dedd \
<ADDRESS> src/MyContract.sol:MyContractOr call the API directly
# Submit
curl -X POST "https://explorer.testnet.zenith.network/api" \
-d "module=contract&action=verifysourcecode&codeformat=solidity-single-file" \
--data-urlencode "[email protected]" \
--data-urlencode "contractaddress=0x..." \
--data-urlencode "contractname=MyContract" \
--data-urlencode "compilerversion=v0.8.35+commit.47b9dedd" \
-d "optimizationUsed=0&runs=200"
# → {"status":"1","result":"<GUID>"}
# Poll
curl "https://explorer.testnet.zenith.network/api?module=contract&action=checkverifystatus&guid=<GUID>"
# → {"status":"1","message":"Perfect match","result":"Pass - Verified"}solc 0.8.35 is bundled; any other valid compiler version is fetched on demand (the native solc binary, run server-side) and cached, so contracts built with any solc release verify. First use of a new version adds a few seconds while it downloads. Verification is limited to 30/hour per IP with a 500KB source cap (see “Rate limits” above); an admin token bypasses the limits.
Hardhat 3 config (hardhat.config.ts)
// Requires an up-to-date @nomicfoundation/hardhat-verify — Hardhat 3.0.6
// had a bug where chainDescriptors.apiUrl was ignored (fixed upstream).
chainDescriptors: {
936485: {
name: "Zenith",
blockExplorers: {
etherscan: {
name: "Zenith Explorer",
url: "https://explorer.testnet.zenith.network",
apiUrl: "https://explorer.testnet.zenith.network/api",
},
},
},
},
verify: { etherscan: { apiKey: "unused" } }, // any non-empty string; this API ignores itHardhat 2 config (hardhat.config.js)
etherscan: {
apiKey: { zenith: "unused" },
customChains: [{
network: "zenith",
chainId: 936485,
urls: { apiURL: "https://explorer.testnet.zenith.network/api", browserURL: "https://explorer.testnet.zenith.network" },
}],
}Chain ID is 936485 — not 1337. Hardhat refuses to verify when the connected chain id doesn't match its config (and treats 1337/31337 as local dev chains), so configs copied from older docs with chainId 1337 fail. Confirm with curl "https://explorer.testnet.zenith.network/api?module=proxy&action=eth_chainId".
Tooling notes: Hardhat 3 verification works via chainDescriptors (above) on an up-to-date hardhat-verify; versions around 3.0.6 ignored the custom apiUrl and silently called etherscan.io — upgrade if verification seems to hit the wrong host. The @wagmi/cli etherscan plugin is hardcoded to etherscan.io — use its blockExplorer plugin with this API instead. The chainid and apikey params sent by Etherscan-v2-style clients are accepted and ignored.
viem / wagmi chain config
blockExplorers: {
default: {
name: "Zenith Explorer",
url: "https://explorer.testnet.zenith.network",
apiUrl: "https://explorer.testnet.zenith.network/api",
},
}/api— account & proxy modulesBeyond the contract module, the API implements the Etherscan account and proxy modules, so tools like Foundry cast, Brownie, ApeWorX, truffle-plugin-verify, and ethers' EtherscanProvider work directly.
module=account| txlist | Normal transactions for an address (incl. the contract-creation tx, for constructor-arg recovery). Params: address, startblock, endblock, page, offset, sort. |
| tokentx / tokennfttx | ERC-20 / ERC-721 transfer events involving an address, with token name/symbol/decimals. |
| balance / balancemulti | Native balance for one or many (comma-separated) addresses. |
| tokenbalance | ERC-20 balanceOf. Params: contractaddress, address. Returns the raw (undivided) unit string. |
| txlistinternal | Returns empty — internal-call traces are not indexed. |
module=proxyJSON-RPC passthrough to the network node — eth_blockNumber, eth_getTransactionByHash, eth_getCode, eth_call, etc. Returns the raw JSON-RPC response ({jsonrpc,id,result}).
module=stats & module=blockstats/tokensupply — live ERC-20 totalSupply (param: contractaddress). stats/ethprice — canonical shape with zero values (testnet token has no market price; lets hardhat-gas-reporter and similar degrade gracefully). block/getblocknobytime — closest block number for a unix timestamp (params: timestamp, closest=before|after).
module=logsgetLogs — event logs by address, fromBlock/toBlock, and topic0–topic3 (numeric fields hex-encoded, Etherscan shape). Used by OpenZeppelin Upgrades proxy verification.
Examples
# address transaction history
curl "https://explorer.testnet.zenith.network/api?module=account&action=txlist&address=0x...&sort=desc"
# ERC-20 transfers for an address
curl "https://explorer.testnet.zenith.network/api?module=account&action=tokentx&address=0x..."
# proxy JSON-RPC
curl "https://explorer.testnet.zenith.network/api?module=proxy&action=eth_blockNumber"
# → {"jsonrpc":"2.0","id":1,"result":"0x..."}
# event logs
curl "https://explorer.testnet.zenith.network/api?module=logs&action=getLogs&address=0x...&topic0=0xddf2..."
# link a proxy to its implementation
curl -X POST "https://explorer.testnet.zenith.network/api" -d "module=contract&action=verifyproxycontract&address=0x<proxy>"
curl "https://explorer.testnet.zenith.network/api?module=contract&action=checkproxyverification&guid=<GUID>"MetaMask and Etherscan-style tooling build links with these paths. They are served directly at HTTP 200 (internal rewrites, so the URL stays put), so configure a wallet's block-explorer URL as https://explorer.testnet.zenith.network (no trailing slash).
| Route | Serves |
|---|---|
| /tx/:hash | /transactions/:hash |
| /address/:address | /addresses/:address |
| /block/:number | /blocks/:number |
| /token/:address | /addresses/:address |
Number.MAX_SAFE_INTEGER (such as wei values and block numbers) are serialized as strings.account endpoints return up to 10,000 via offset.error field.