mina-explorer-api

Drop-in, bug-compatible replacement for the Blockberry Mina REST API.

Base URL   https://mina-explorer-api.minaprotocol.com

Path structure   /mina-{network}/v1/... — the network is in the path; auth is the x-api-key header (unchanged from Blockberry).

Networks   active: mina-mainnet, mina-devnet, mina-mesa · dormant (503): mina-pre-mesa

What this is

A drop-in, bug-compatible replacement for the Blockberry Mina REST API. Point your existing client at the base URL above, keep your x-api-key header, and the /mina-{network}/v1/... paths work unchanged. See the migration guide to switch over, and Get an API key to request credentials (key values are re-issued by us — Blockberry keys are not portable).

Where an endpoint diverges, it degrades honestly — documented null fields and window-bounded reads — rather than fabricate or 500. Every divergence is listed below.

Compatibility matrix

55 endpoints · Full 6   Partial 43   Stub 6

Full — shape/byte parity for real on-chain data.Partial — served, with documented null fields or a coverage caveat.Stub — placeholder (empty / 404) with no backing source.
EndpointStatusNotes & null fields
Blocks
/v1/blocks
GET
Partial
null: accountName, accountImg, ledgerHash, parentHash, snarkJobsCount, snarkersCount
Envelope A; totalElements capped at 10 000, totalCount is the true count and a window-bounded lower bound in the fallback path (§A/§I.3). isCanonical is a real boolean here (contrast detail, §D). type=ORPHANED returns empty until the index.
/v1/blocks/{stateHash}
GET
Partial
null: coinbaseReceiver, snarkedLedgerHash, stagedLedgerHash, winnerAddress
isCanonical is INTEGER 0/1 in the detail DTO (§D). Zero snarkersFee/transactionsFee re-emitted as int 0. Not found → empty-body 404, no content-type (§I.10 / §G probe 23).
/v1/blocks/latest-block-state-hash
GET
Full
Bare UNQUOTED string body served as application/json (§H/§I.2). Not found → empty-body 404.
/v1/block-confirmation/{txHash}
GET
Partial
null: txHashNext, txHashPrevious
Window-bounded. Pending-path also nulls blockHeight, failureReason, stateHash until confirmation.
Transactions
/v1/transactions
POST
Partial
null: fromAccountImg, fromAccountName, toAccountImg, toAccountName
blockHeight is a STRING here (bug-compatible, §C). age = block epoch-ms (§B). totalCount present but window-approximate until the index.
/v1/transactions/pending
POST
Partial
null: age, fromAccountImg, fromAccountName, toAccountImg, toAccountName
Envelope A WITHOUT totalCount (§A). status constant 'pending'; receiverScam/sourceScam emitted as all-null objects (Blockberry parity).
/v1/transactions/internal
POST
Partial
null: accountImg, accountName, receiverImg, receiverName, secondarySequenceNo, sequenceNo, txHash
Envelope A WITHOUT totalCount (§A). Coinbase + fee-transfer commands.
/v1/transactions/{hash}
GET
Partial
null: alert, balanceChanges, delegationAmount, delegationAmountUsd, receivedAmount, receivedAmountUsd, receivedDelegationAmount, receiverImg, receiverName, sequenceNumber, sourceImg, sourceName
Pending path also nulls timestamp + failureReason. USD fields degrade to null on a CoinGecko outage (never 5xx). Not found → empty-body 404.
Latest
/v1/latest-transactions
GET
Partial
null: fromAccountImg, fromAccountName, toAccountImg, toAccountName
Bare JSON array of UserCommand items (§H). blockHeight is a STRING (§C), matching POST /v1/transactions.
/v1/latest-zkapp-transactions
GET
Partial
null: proverImg, proverName
Bare JSON array of ZkAppTransactionDto (§H); identical item to /v1/zkapps/txs. isAccountHijack / isZkappAccount constant false.
zkApps
/v1/zkapps/txs
GET
Partial
null: proverImg, proverName
Envelope A WITH totalCount. isAccountHijack / isZkappAccount constant false; updatedAccounts[] off-chain fields (accountImg/accountName/verificationKey/verificationKeyHash/isZkappAccount) null.
/v1/zkapps/accounts/{address}/txs
GET
Partial
null: proverImg, proverName
Envelope A WITHOUT totalCount (§A).
/v1/zkapps/blocks/{stateHash}/txs
GET
Partial
null: proverImg, proverName
Envelope A WITHOUT totalCount (§A).
/v1/zkapps/txs/{txHash}
GET
Partial
null: feePayerImg, feePayerName
isAccountHijack / isZkappAccount constant false; updatedAccounts[] off-chain fields null. USD via CoinGecko (null on outage). Not found → empty-body 404.
/v1/zkapps/txs/raw/{txHash}
GET
Full
JSON string of the raw upstream zkApp command. Miss → empty-body 404.
/v1/zkapps/txs/balance-change/{txHash}
GET
Partial
null: accountImg, accountName, balanceBefore, balanceAfter, coingeckoCoinId, coinmarketCoinId, isBridged, isFeeIncluded, isVerified, isZkappAccount, tokenImage, tokenName, tokenScam, tokenSymbol
accountScam emitted as all-null object (Blockberry parity).
/v1/zkapps/{address}
GET
Partial
null: verificationKeyHash
Invalid / nonexistent / non-zkApp address → HTTP 200 all-null object, NOT 404 (§I.9). accountScam emitted as all-null object (parity). Wire shape UNVERIFIED (no live key, plan §7.1).
/v1/zkapps/{address}/last-verification-key-change
GET
Full
Bare int64 (slot of last vk change), or bare null on miss (§I.9 precedent). Wire shape UNVERIFIED (M3-T7).
/v1/zkapps/{address}/verification-key-history
GET
Partial
null: verificationKeyHash
Bare ascending array of VerificationKeyHistoryDto; empty array on miss. Wire shape UNVERIFIED (M3-T7).
Accounts
/v1/accounts/{address}
GET
Partial
null: accountName, accountImg, description, discordNicknames, firstSeen, lastSeen, isNotActivated, isVerified, socialDiscord, socialEmail, socialGitHub, socialTelegram, socialTwitter, website
Invalid / nonexistent address → HTTP 200 all-null AccountInfo, NOT an error (§I.9). accountScam emitted as all-null object (parity). balance reproduces Blockberry's double-division bug (nanomina ÷ 1e18) on THIS endpoint only; /balance uses the correct ÷1e9.
/v1/accounts/{address}/balance
GET
Full
MINA + USD balance (correct ÷1e9). balanceUsd degrades to null on a CoinGecko outage (never 5xx).
/v1/accounts/{address}/delegatee
GET
Partial
null: epoch1, epoch2
currentEpoch populated (delegate + delegate balance); currentEpoch.delegateeImg/delegateeName null (off-chain); currentEpoch.accountScam all-null object (parity).
/v1/accounts/{address}/txs
GET
Partial
null: accountName, accountImg, receiverName, receiverImg, senderName, senderImg
Envelope A WITHOUT totalCount (§A). blockHeight is an INTEGER here (§C, contrast the tx list's string). isAmountChangeable constant false; receiverScam/sourceScam all-null objects (parity).
/v1/accounts/{address}/timing-info
GET
Partial
null: cliffTimeMillis, vestingTime, vestingTimeMillis, unlockStatus, liquidityRate, startTime
M1-T8. isHasLock semantic UNVERIFIED (no fixture). Absent address → all-null at 200; daemon outage → 503 (M2-T3, never a raw 500).
/v1/accounts/{address}/is-validator
GET
Full
Bare JSON boolean (§I). Window-approximate until the M3 index; wire shape UNVERIFIED.
/v1/accounts/{address}/is-snarker
GET
Full
Bare JSON boolean (§I). Window-approximate until the M3 index; wire shape UNVERIFIED.
/v1/accounts/{address}/statistics
GET
Partial
null: minBalance, maxBalance
M3-T5, index-backed. Counts (incomingTxs/outgoingTxs/zkappTxs/totalTxs) real from the transactions projection. Wire shape UNVERIFIED.
/v1/accounts/{address}/staking-balance
GET
Partial
Bare EpochDotDTO[] from staking_ledger; empty for mesa/pre-mesa (no GCS staking source). Wire shape UNVERIFIED (spec-only).
/v1/accounts/{address}/tx-count-chart
GET
Partial
Bare DataPointDto[] from the transactions projection (real). Wire shape UNVERIFIED (spec-only).
/v1/accounts/{address}/zkapp-tx-count-chart
GET
Partial
Bare ZkTxDataPointDto[] from the transactions projection (real). Wire shape UNVERIFIED (spec-only).
Rich list
/v1/accounts
GET
Partial
null: accountName, accountImg, accountScam, delegateName, delegateImg, delegateScam, receiptChainHash, isZkappAccount
Observed-addresses-only rich list from the #19 seen_accounts projection (NOT a full-ledger snapshot; plan §7.2). Envelope A cut page WITH totalCount (true row count); totalElements capped at 10 000. sortBy BALANCE|NONCE, orderBy ASC|DESC; size>50 / bad sortBy → Spring 400. R8: index off → valid empty page, never 500. Wire shape UNVERIFIED (plan §7.1).
Info
/v1/info
GET
Partial
null: nftsCount, collectionsCount, accountsCount, accountsCountDay, activeSnarkersCount, activeValidatorsCount, avgSnarkFee, blockHeightDay, paymentsCount, paymentsCountDay, snarkersCount, snarkersCountDay, tokensCount, txsCount, txsCountDay, validatorsCount, zkAppAccountsCount, zkappTxsCount, zkappTxsCountDay, zkappsCount, zkappsCountDay
DashboardDto. Real (window-derived from archive/daemon): blockHeight, epoch, slot (post-fork), avgFee, avgFeeDay, maxTxFee, minTxFee. epoch/slot are POST-FORK values (M1-T9).
Validators
/v1/validators
GET
Partial
null: networkShare, stakePercent, validatorFee
Envelope B (true content-keyed Spring Page; totalElements never capped). Real: validatorAddress, canonicalBlocksCount, allBlocksCount, stake, delegatorsCount, nextEpochStake, nextEpochDelegationsCount; scam is the all-null object (parity). Also null (data gap): diff* (e.g. diffStake), epoch2*, and all identity/social/verification fields. stake fields null on mesa/pre-mesa (no GCS staking source). R8: index off → empty/zero/null, never 500.
/v1/validators/{address}
GET
Partial
null: networkShare, stakePercent, validatorFee
Same null set + diff*/epoch2*/identity as the list. Absent/not-indexed → all-fields object with the address echoed + zero block counts, never 500. Wire shape UNVERIFIED (plan §7.1).
/v1/top-validators
GET
Partial
null: networkShare, stakePercent, validatorFee
Serves the SAME ValidatorDTO as /v1/validators via the identical map_validator (8 of 31 fields populated), so diff* (diffStake/diffDelegatorsCount), epoch2* (epoch2Stake/epoch2DelegationsCount), isActive, isVerified, isStakingRewardsVerified, and all identity/social/terms fields are null too — the live devnet recording (tests/fixtures/blockberry/devnet/top-validators.json) shows Blockberry populates networkShare/stakePercent/isActive/isVerified, so these are real divergences. ROUTING is exact per network — config-driven asymmetry (§F, M3-T3 reprobe): devnet → bare ValidatorDTO[] (count 1–50); mainnet + mesa → byte-identical 405 {"error": "Handler is not instance of HandlerMethod"} (route absent upstream); pre-mesa → 503.
Snarks
/v1/snarkers
GET
Partial
null: accountName, accountImg, fee, isZkappAccount, snarkJobsCount, snarkWorksCount, totalJobsSold
Envelope A data-keyed, NO totalCount, NO 10k cap (§I.4). Real: accountAddress, snarkAmountEarned, firstSeen, lastSeen.
/v1/snarks/{stateHash}/jobs
GET
Partial
null: accountName, accountImg, workIds
Envelope B. Real: accountAddress, fee, isCanonical, timestamp. Wire shape UNVERIFIED.
/v1/snarks/jobs/{address}
GET
Partial
null: workIds
Envelope B. Real: blockHeight, fee, isCanonical, stateHash, timestamp. Wire shape UNVERIFIED.
/v1/snarks/jobs/{address}/snarker-by-address
GET
Partial
null: accountName, accountImg, avgAllTimeFee, avgMonthFee, currentFee, delegateeAddress, jobsProduction, snarkJobsCount, worksProduced
Real: feesEarned, firstSeen, lastSeen. Unknown prover → 200 all-null. Wire shape UNVERIFIED.
Tokens
/v1/tokens
GET
Partial
null: lockedBalance, unlockPercent, isZkappAccount, ownerAddress, ownerName, ownerImg, ownerTokenId, ownerScam, tokenName, tokenImage, isVerified, isBridged, coingeckoCoinId, coinmarketCoinId, tokenScam, tokenSymbol
Envelope A WITHOUT totalCount. Wire shape UNVERIFIED (plan §7.1).
/v1/tokens/symbols
GET
Partial
Flat {tokenId: SYMBOL} string map, NO envelope (§E/§I.6, PINNED). Returns an EMPTY map in production until #19 populates token symbols.
/v1/tokens/{tokenId}
GET
Partial
null: lockedSupply, unlockedSupply, unlockPercent, tokenHoldersCountDay, tokenAdminAddress, ownerAddress, tokenOwnerPk, ownerName, ownerImg, ownerScam, tokenName, tokenImage, isVerified, isBridged, coingeckoCoinId, coinmarketCoinId, tokenScam
Unknown tokenId → empty-body 404 (§I.10, UNVERIFIED). Wire shape UNVERIFIED (plan §7.1).
/v1/tokens/{tokenId}/holders
GET
Partial
null: lockedBalance, movableBalance, holderName, holderImg, accountScam
Holder isZkappAccount IS real (seen_accounts join). Wire shape UNVERIFIED.
/v1/tokens/{tokenId}/txs
GET
Partial
null: fee, memo, nonce, proverAddress, proverImg, proverName, status
Shares the ZkAppTransactionDto item mapper (map_token_tx → to_list_item), so it inherits that DTO's off-chain prover nulls on top of the token_activity gaps. proverScam/accountScam all-null objects (parity). Wire shape UNVERIFIED.
/v1/tokens/accounts/{address}
GET
Partial
null: nonce, tokenName, tokenImage, isVerified, isBridged, coingeckoCoinId, coinmarketCoinId, tokenScam
Per-account token holdings (TokenByPkDto). Wire shape UNVERIFIED.
Time-locks
/v1/time-locks
POST
Partial
null: name, img, totalSupply, isZkAppAccount
Vesting math is real, live-verified (M1-T8; app/vesting.py). Envelope A WITHOUT totalCount; totalElements capped 10 000. lockTypes body accepted + enum-validated (invalid → Spring 400) but NOT yet used to filter (LOCKED/UNLOCKED semantics UNVERIFIED). Date fields (cliffTimeMillis/vestingTimeMillis/lockDate/fullyUnlockedDate) computed via the original-genesis anchor; exact wire semantics UNVERIFIED. R8: index off → empty page, never 500.
/v1/time-locks/day
GET
Partial
Per-day unlock aggregation (LocksEntity). Vesting/unlock totals are real + live-verified (M1-T8), property-tested for conservation; envelope + date-field wire semantics UNVERIFIED. R8: index off → empty page, never 500.
/v1/time-locks/month
GET
Partial
Per-month unlock aggregation (LocksMonthEntity). Vesting/unlock totals real + live-verified (M1-T8); envelope + date-field wire semantics UNVERIFIED.
/v1/time-locks/year
GET
Partial
Per-year unlock aggregation (LocksYearEntity). Vesting/unlock totals real + live-verified (M1-T8); envelope + date-field wire semantics UNVERIFIED.
Security
/v1/security/scams
GET
Stub
No scam registry backs this deployment (§4) → bare empty [] (ScamDto[]) for every query. onChainIds kept REQUIRED (missing → Spring 400), matching the upstream contract (M1-T9).
/v1/security/scams/tx-body
POST
Stub
No scam registry (§4) → bare empty [] (ScamDto[]). Body validated as WalletTransactionDto (malformed → Spring 400), upstream-parity (M1-T9).
MIPs
/v1/mips
GET
Stub
Governance-metadata group not served (feasibility tier T4, plan §4). 404 {"error": "Not available on this network"} — a KNOWN-but-stubbed group, so a 404 stub, NOT the 405 unknown-path body (§F/§G/§I.8, M1-T9).
/v1/mips/votes
GET
Stub
404 stub (T4). Registered BEFORE /mips/{id} so the literal is not captured by the int path param (M1-T9).
/v1/mips/{id}
GET
Stub
404 stub (T4, M1-T9).
/v1/mips/{id}/votes
GET
Stub
404 stub (T4, M1-T9).