A "NEAR scan" is any workflow that reads state and history from the NEAR Protocol blockchain, whether through a hosted explorer, a block explorer API, or direct JSON-RPC calls. Explorers are convenient for humans, but reproducible analysis needs the underlying RPC methods: block, chunk, transaction status, account state, and contract calls. This article explains the mental model behind NEAR scanning, gives runnable RPC procedures, shows how to verify contract source against deployed code, and covers the failure modes that make scan results look wrong. It separates documented protocol behavior from recommendations and from measurements you should run yourself.
What a NEAR scan actually reads
The phrase "NEAR scan" describes reading data from the NEAR Protocol network: blocks, chunks, transactions, receipts, account balances, access keys, and contract state. A block explorer is a UI over that data. Underneath, explorers and indexers call NEAR JSON-RPC endpoints, often supplemented by their own databases for historical aggregation and search.
This matters because an explorer view is a product decision, not the protocol. If a transaction is missing from one explorer, the transaction may still be final on-chain. If a balance looks different between two tools, one may be showing liquid balance while another includes staked or locked amounts. A reliable scan workflow starts from the RPC response and treats the explorer as a presentation layer.
Scope: this article covers read-only scanning of NEAR mainnet and testnet data, contract source verification concepts, and diagnostic procedures. It does not cover trading, token price analysis, or wallet custody.
- Explorer: human-oriented search and display.
- RPC: machine-oriented access to canonical node state.
- Indexer: a database built from RPC or streaming data for fast historical queries.
- Contract verification: comparing deployed Wasm with source compiled from a repository.
Prerequisites and assumptions
You need a NEAR RPC endpoint URL, which this article refers to as RPC_URL. You can use a public endpoint for light testing, but public endpoints are rate-limited and may lag. For repeated scans or production indexers, use a managed or dedicated endpoint; see the multi-chain RPC endpoints guide and RPC pricing for how capacity is provisioned.
You also need curl or a scripting runtime such as Node.js or Python. Examples use JSON-RPC 2.0 over HTTPS with the method name in the body. NEAR RPC methods are documented in the NEAR API reference; method names and parameter shapes below follow that documented interface.
Assumption: you are scanning public chain data. Do not send private keys to an RPC endpoint. Read methods require no signing.
The mental model: blocks, chunks, transactions, and receipts
NEAR produces blocks. Each block contains one or more chunks, and each chunk belongs to a shard. A transaction is included in a chunk, but its effects are carried by receipts, which can execute in later blocks and different shards. This is why a transaction can show a final status while its receipts are still being processed, and why a scan that only looks at the transaction's own block can miss downstream effects.
Account state is stored per shard. An account has a balance, locked amount, storage usage, code hash, and access keys. Contract state is a key-value store accessed through view calls or state queries. When you scan an account, decide which of these you need; a single "balance" number is rarely the whole story.
Finality: NEAR has optimistic and final block references. For irreversible reporting, query with a finality parameter rather than a specific block height, or wait until the block is final. Explorers usually label this, but raw RPC responses require you to request it.
- Block: container with a header and chunk headers.
- Chunk: shard-specific portion of a block.
- Transaction: signed request included in a chunk.
- Receipt: cross-contract or cross-shard effect generated by execution.
- Outcome: execution result attached to a transaction or receipt.
Runnable scan: block, transaction, and account queries over RPC
The following curl calls use placeholders. Replace RPC_URL with your endpoint. The block query asks for the latest final block. The transaction query uses a transaction hash and the sender account, because NEAR's transaction status method requires both. The account query returns balance and storage fields.
Run these in order and compare the block height in the transaction response with the block height you fetched. If the transaction is newer than your block query, re-query with finality.
curl -s -X POST "$RPC_URL" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"block","method":"block","params":{"finality":"final"}}'
curl -s -X POST "$RPC_URL" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"tx","method":"tx","params":{"tx_hash":"TRANSACTION_HASH","sender_account_id":"SENDER.near","wait_until":"FINAL"}}'
curl -s -X POST "$RPC_URL" \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":"acct","method":"query","params":{"request_type":"view_account","finality":"final","account_id":"ACCOUNT.near"}}'Expected verification result and how to read it
A successful block response contains a result with a header and chunks. The header includes height, hash, and timestamp. A successful transaction response contains a final execution status and one or more outcome receipts. A successful account query contains amount, locked, code_hash, storage_usage, and storage_paid_at.
Verification checklist: the transaction's included block height should be less than or equal to the final block height you fetched. The transaction's final execution status should be SuccessValue or SuccessReceiptId for a completed call. If it is Failure, the response includes an error object; read it before assuming the scan is broken.
For a results table, record one row per query with columns: query type, request ID, block height, finality, status, and latency measured by your client. Do not copy latency numbers from another environment; measure from the machine and network path you will actually use.
- Block height increases monotonically; a lower height after a higher one indicates a lagging endpoint.
- Transaction status FINAL means the transaction is irreversible under the requested finality.
- Account code_hash changes when a contract is deployed or upgraded; use it to detect upgrades.
Scripted scanning with JavaScript and Python
For repeated scans, wrap the RPC call in a function with retries and explicit finality. The JavaScript example uses fetch and a small retry loop. The Python example uses urllib and prints a compact summary. Both use RPC_URL and ACCOUNT_ID placeholders.
Keep the request ID unique per call so logs can be correlated. Treat HTTP 429 and 5xx responses as retryable with backoff; treat JSON-RPC errors with a code and message as application errors that need inspection.
// scan.mjs
const RPC_URL = process.env.RPC_URL;
const ACCOUNT_ID = process.env.ACCOUNT_ID || 'ACCOUNT.near';
async function rpc(method, params, id) {
const res = await fetch(RPC_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', id, method, params })
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
if (json.error) throw new Error(`${json.error.code}: ${json.error.message}`);
return json.result;
}
const block = await rpc('block', { finality: 'final' }, 'b1');
const acct = await rpc('query', {
request_type: 'view_account', finality: 'final', account_id: ACCOUNT_ID
}, 'a1');
console.log({ height: block.header.height, account: ACCOUNT_ID, amount: acct.amount, code_hash: acct.code_hash });
# scan.py
import json, os, urllib.request
RPC_URL = os.environ['RPC_URL']
ACCOUNT_ID = os.environ.get('ACCOUNT_ID', 'ACCOUNT.near')
def rpc(method, params, req_id):
body = json.dumps({'jsonrpc': '2.0', 'id': req_id, 'method': method, 'params': params}).encode()
req = urllib.request.Request(RPC_URL, data=body, headers={'Content-Type': 'application/json'})
with urllib.request.urlopen(req, timeout=15) as resp:
data = json.load(resp)
if 'error' in data:
raise RuntimeError(data['error'])
return data['result']
block = rpc('block', {'finality': 'final'}, 'b1')
acct = rpc('query', {'request_type': 'view_account', 'finality': 'final', 'account_id': ACCOUNT_ID}, 'a1')
print({'height': block['header']['height'], 'account': ACCOUNT_ID, 'amount': acct['amount'], 'code_hash': acct['code_hash']})Contract verification: comparing source with deployed Wasm
Contract verification answers a different question from transaction scanning: does the source code in a repository compile to the Wasm currently deployed at an account? Explorers that offer a verify-contract feature typically compile the submitted source in a controlled environment and compare the resulting Wasm hash or bytecode with the on-chain code hash.
You can reproduce the core check without an explorer. Fetch the account's code_hash and the contract code via RPC, then compile the repository locally with the same toolchain and build flags the project documents. Compare hashes. A mismatch can come from a different compiler version, different feature flags, or a different source revision, not necessarily from malicious code.
Documented behavior: NEAR accounts expose a code_hash field, and contract code can be retrieved through RPC. The exact compilation recipe is project-specific; do not assume a generic build reproduces a deployed artifact.
- Pin the compiler and build tool versions in the repository.
- Record the source commit and build command alongside the deployed code hash.
- Compare hashes, not file sizes, as the primary signal.
- Treat a mismatch as a reproducibility problem first and a security problem second.
Troubleshooting common NEAR scan problems
Most scan discrepancies fall into a few categories: finality, sharding, indexing lag, and presentation choices. Work through them in that order before assuming data loss.
If a transaction is not found, confirm the hash and sender account. NEAR's transaction status method requires the sender, and a hash alone is insufficient. If the transaction is very recent, query with wait_until set to FINAL or retry after a short delay.
If balances differ between tools, check whether the tool shows liquid amount, locked amount, or both, and whether it includes storage costs. If a contract call returns unexpected state, confirm the block height or finality used for the view call; a view at an older block can legitimately differ from current state.
If an endpoint returns rate-limit or timeout errors, reduce concurrency and add backoff. For sustained scanning, move to a dedicated endpoint; see dedicated node and the blockchain node hosting guide.
- Missing transaction: verify hash plus sender, then retry with finality.
- Lagging height: compare two endpoints and prefer the one closer to final.
- Unexpected view result: pin block height or finality explicitly.
- Frequent 429: lower request rate or use a managed endpoint.
Limitations and tradeoffs of explorer-based scanning
Explorers are optimized for human lookup, not bulk extraction. They may paginate, hide internal receipts, or aggregate token transfers in ways that are convenient but not canonical. Their historical databases can lag the chain tip, and their search indexes may not cover every account or contract method.
Direct RPC is canonical but not a database. It answers point queries well; it does not provide efficient full-history search across all accounts. For analytics, pair RPC with an indexer that you control or whose data model you understand. For one-off checks, an explorer is usually faster.
Cost and reliability tradeoffs matter at scale. Public endpoints are free but shared; managed endpoints cost money but provide predictable capacity. Measure your own latency and error rate rather than relying on published claims.
Next steps for reliable NEAR data workflows
Start by standardizing on finality in every query, then add retries and request IDs. Build a small scan script that records block height, account code hash, and transaction status for the accounts you care about. Run it on a schedule and store the results so you can detect changes over time.
When you need production throughput, choose an endpoint through OnFinality's API service and review supported networks. For endpoint selection guidance rather than implementation detail, RPC Assistant covers concrete provider and network recommendations; see the RPC endpoints guide for the general framework.
Finally, document your verification recipe: compiler version, build command, source commit, and expected code hash. That record is what makes a NEAR scan reproducible months later.