æternity Node & Middleware
The Superhero API is an index over the æternity chain, not the chain itself. For anything it does not carry (an arbitrary transaction, an AEX-9 balance, a name's pointers, a contract's event log, or submitting a transaction at all), talk to an æternity node or its middleware.
Node versus middleware
| Node | Middleware | |
|---|---|---|
| Path prefix | /v3/... | /mdw/v3/... |
| Answers | Current state | Historical and indexed state |
| Use it for | Contract calls, dry-runs, balances, submitting transactions, current height | Transactions by account or contract, AEX-9 transfers and balances, AENS names, contract event logs |
| Can it write? | Yes, transactions are submitted here | No, read-only |
The æternity SDK's Node class wraps the node API. Everything that signs something (posting,
creating a token, swapping, generating invites, voting) goes through a Node instance. The
middleware is read with plain fetch.
Hosts
| Mainnet | Testnet | |
|---|---|---|
| Node | https://mainnet.aeternity.io | https://testnet.aeternity.io |
| Middleware | https://mainnet.aeternity.io/mdw | https://testnet.aeternity.io/mdw |
| Middleware WebSocket | wss://mainnet.aeternity.io/mdw/v2/websocket | wss://testnet.aeternity.io/mdw/v2/websocket |
| Explorer | https://aescan.io | https://testnet.aescan.io |
| Compiler | https://v7.compiler.aepps.com | https://v7.compiler.aepps.com |
api.superhero.com indexes from these hosts. The web app reaches mainnet through a
Superhero-operated node and middleware at https://mdw.wordcraft.fun and
https://mdw.wordcraft.fun/mdw, which serve the same ae_mainnet chain; a transaction submitted
to either host is visible from the other.
Node endpoints
Current height, read directly:
curl -s "https://mainnet.aeternity.io/v3/headers/top"Everything else through the SDK. Contract initialisation, dry-run reads (has_community,
calculate_buy_price, bonding_curve, supported_decimals), balance lookups and transaction
submission all go through new Node(NODE_URL) and an AeSdk instance:
import { AeSdk, Node, MemoryAccount } from '@aeternity/aepp-sdk';
const node = new Node('https://mainnet.aeternity.io');
const aeSdk = new AeSdk({
nodes: [{ name: 'mainnet', instance: node }],
accounts: [new MemoryAccount(process.env.AE_PRIVATE_KEY)],
});See Calling contracts for the contract side.
Middleware endpoints
| Endpoint | Purpose |
|---|---|
GET /mdw/v3/transactions?contract={ct}&account={ak}&limit={n} | Read an account's posts from the social contract |
GET /mdw/v3/transactions?contract={ct}&caller_id={ak} | Find invite-creation transactions |
GET /mdw/v3/transactions/{hash} | Poll a transaction to confirmation |
GET /mdw/v3/names?owned_by={ak}&state=active&limit=100 | List the .chain names an account owns |
GET /mdw/v3/names/{name} | Resolve a .chain name to an address |
GET /mdw/v3/aex9/{address} | AEX-9 token metadata |
GET /mdw/v3/accounts/{ak}/aex9/balances?limit=100 | An account's AEX-9 balances |
GET /mdw/v2/aex9/account-balances/{ak}?limit=100 | The v2 form of the same |
GET /mdw/v3/status | Middleware version and its distance from the chain tip |
/v3/ is current; /v2/ paths still work against the same deployment.
Reading posts from the index
curl -s "https://mainnet.aeternity.io/mdw/v3/transactions?contract=ct_2Hyt9ZxzXra5NAzhePkRsDPDWppoatVD7CtHnUoHVbuehwR8Nb&account=ak_YOUR_ADDRESS&limit=20"The response is { data: [...], next }. Each element carries hash, block_height,
micro_time (milliseconds) and a tx object with function, arguments (an array of
{ type, value }) and return. Filter to tx.function === 'post_without_tip', then read the
post body from arguments[0].value and the attached links from arguments[1].value.
next is a path, not a full URL. Concatenate it onto the middleware origin to page.
Name resolution
GET /mdw/v3/names/{name} returns a name record; the address is the pointer whose key is
account_pubkey: pointers.find(p => p.key === 'account_pubkey').id. Check active and that
the returned name matches what you asked for, and treat 404 as "no such name".
For chain names in bulk, prefer GET /api/accounts/chain-names?addresses=ak_a,ak_b, which
resolves up to 25 at a time, and fall back to the middleware for names it does not know. See
Accounts.
Transaction confirmation
GET /mdw/v3/transactions/{hash} returning 404 means pending, not failed. Poll every 5
seconds and compute confirmations as currentHeight - blockHeight + 1.
The middleware mirror on api.superhero.com
The Superhero API re-indexes a slice of middleware data under its own paths:
| Method & path | Returns |
|---|---|
GET /api/v2/mdw/txs | Transactions, with type, contract_id, function, caller_id, sender_id, recipient_id filters |
GET /api/v2/mdw/txs/{hash} | One transaction |
GET /api/v2/mdw/key-blocks | Key blocks, filterable by hash, height, beneficiary, miner, version |
GET /api/v2/mdw/key-blocks/{hash} | One key block |
GET /api/v2/mdw/micro-blocks | Micro blocks |
GET /api/v2/mdw/micro-blocks/{hash} | One micro block |
GET /api/v2/mdw/sync-state | Indexer sync state |
GET /api/v2/mdw/plugin-sync-state | Per-plugin sync state |
GET /api/mdw/health | Middleware health check |
These take an includes parameter for nested relations: keyBlock, keyBlock.txs,
microBlocks, txs.keyBlock and similar, comma-separated. An array relation is truncated to
50 items per parent. Response shapes are the mirror's own Tx, KeyBlock and MicroBlock
records, not the upstream middleware's.
- Rate-limited. 10 requests per minute per IP per route path, returning
429with aretryAfterfield in the body. See Conventions. limitis capped at 100 with400 Maximum limit is 100 items per request; anorder_byoutside the route's sortable set is a400.pagegoes up to 1,000,000. Deep paging still costs the database a scan of everything before your page, so prefer aheightorblock_heightfilter where you can.
Sortable fields on sync-state: id, last_synced_height, tip_height, is_bulk_mode,
backward_synced_height, live_synced_height, created_at, updated_at. On txs: hash,
block_height, micro_time, type, contract_id, function, caller_id, sender_id,
recipient_id.
GET /api/mdw/health
How far behind Superhero's own index is. Not rate-limited; no parameters.
curl -s "https://api.superhero.com/api/mdw/health"| Key | Contents |
|---|---|
status | healthy, warning or critical |
totalTxs | Rows in the mirrored transaction table |
backwardSync | status, syncedHeight, tipHeight, remainingBlocks, progressPercent, mode (bulk or normal), isRunning |
liveSync | status, syncedHeight, tipHeight, lag, progressPercent, gapFromBackwardSync, isActive |
validation | status, isValidating, lastValidationTime |
comparison | syncGap, backwardSyncProgress, liveSyncProgress |
plugins | Per-plugin name, lastSyncedHeight, backwardSyncedHeight, liveSyncedHeight, startFromHeight |
registeredPlugins | The plugin names that are loaded |
liveSync.lag is tipHeight - liveSyncedHeight, in key blocks. The server grades it as:
| Condition | status |
|---|---|
Backfill complete and lag at most 100 | healthy |
Backfill complete and lag above 100 | warning |
lag above 1000, or more than 10,000 blocks of backfill left | warning |
lag above 5000, more than 50,000 blocks of backfill left, or the live indexer is not active | critical |
Two syncs run independently: a live one following the tip, and a backward one walking
from the tip towards genesis. remainingBlocks counts down to 0, so it stays large on a fresh
deployment while live data is already current. Judge freshness from liveSync.
GET /api/stats gives a shorter summary of the same picture plus mdwConnected. See
Factory & Contracts.
When to use which
| You need | Go to |
|---|---|
| Token prices, holders, trades, trending, portfolio value | Superhero API: Tokens, Accounts |
| Posts, comments, topics, tips | Superhero API: Posts |
| Claim status of invites you issued | Superhero API: Invitations |
| A raw transaction by hash, or "is my transaction confirmed?" | Middleware GET /v3/transactions/{hash} |
| Every call an account made to a specific contract | Middleware GET /v3/transactions?contract=&caller_id= |
| AEX-9 balances for an arbitrary token | Middleware GET /v3/accounts/{ak}/aex9/balances |
| AENS names owned by, or resolving to, an address | Superhero API batch first, then middleware /v3/names |
| Contract event logs | Middleware |
| Reading contract state, or writing anything at all | Node, through the SDK |
The Superhero API knows about Superhero. The moment your question is about the chain rather than
the product, it belongs to the middleware or the node. The middleware indexes after the fact and
the Superhero API indexes after that, so for anything where staleness matters read the node, and
check GET /api/mdw/health before expecting a recent event to be in the API.
Related
- API Conventions: how the Superhero API's conventions differ from the middleware's
- Transactions API: Superhero trade records, and how
tx_hashlinks back to the chain - Accounts API: the batch chain-name resolver
- Invitations & Affiliations: combining both sources
- Networks: mainnet and testnet, and what a network id means
- æternity node API documentation (opens in a new tab): the node's own reference
- ae_mdw on GitHub (opens in a new tab): the middleware's endpoint list