What owns what, right now
The ERC-721 Inspector reads any ERC-721 NFT contract and returns the name, symbol, tokenURI, and current owner from one expression. It is built for developers and collectors who need to inspect a specific token without writing ABI files or chasing IPFS gateways by hand.
Key facts
- Reads an ERC-721 token’s
name(),symbol(),tokenURI(id), andownerOf(id)in one expression, batched into a single round trip via Multicall3. - Unlike ERC-1155’s
uri(),tokenURI(id)returns the final metadata URL already resolved by the contract, with no{"{id}"}placeholder substitution needed. - Resolves EIP-1967 transparent and beacon proxies automatically; roughly a third of production contracts use proxies, including most upgradeable ERC-721 collections.
- Covers Ethereum, Base, BNB Smart Chain, and Polygon, with more EVM chains being added.
Reading the owner, tokenURI, and metadata
- Paste the ERC-721 contract address into the form above.
- Enter the token ID you want to inspect, then pick a chain.
- Run the query and read the name, symbol, owner, tokenURI, and metadata in the result panel.
The result panel shows the collection name and symbol, the current owner address, the raw tokenURI(id) returned by the contract, the resolved metadata URL, and the read block number. Everything happens in one round trip to the contract.
One call, no placeholder to substitute
ERC-721 is simpler to query than ERC-1155: every token ID has its own owner and its own tokenURI, already resolved by the contract with no {id} placeholder to substitute. If you are not sure which standard a contract actually implements, the ERC-1155 Inspector is the one to reach for instead when a single contract needs to hold many token IDs, some fungible, some not. evmquery handles the contract reads in one expression. The frontend handles the gateway fetch for the metadata JSON.
{ "name": dyn(nft.name()), "symbol": dyn(nft.symbol()), "tokenURI": dyn(nft.tokenURI(id)), "owner": dyn(nft.ownerOf(id))}name()andsymbol()read the collection-level metadata exposed by the ERC-721 Metadata extension.tokenURI(id)returns the final metadata URL for that specific token. evmquery resolves the contract ABI from verified source and decodes the typed result.ownerOf(id)returns anaddress, while the other fields are strings. CEL maps need a single value type, so every field is wrapped indyn(...), which yields amap<string, dyn>where each value serializes as its natural type (the owner comes back as a hex address).- Multiple reads are auto-batched by Multicall3, so the cost is one round trip regardless of how many fields you ask for.
“Immutable metadata” is a contract choice, not a guarantee
ownerOf(id) is settled the moment a transfer confirms: there is no ambiguity about who owns a token ID at a given block, and ownerOf reverts outright for a burned or never-minted ID rather than returning a stale answer. tokenURI(id) is a different story. EIP-721 only requires the function to return a URL; it says nothing about whether that URL, or the JSON behind it, can change. Plenty of production contracts store a baseURI string in ordinary storage and expose an owner-gated setter for it, which means the same token ID can point at different metadata before and after that call, with no event required beyond whatever the contract author chose to emit. “On-chain provenance, off-chain metadata” is the accurate way to describe most ERC-721 collections, not “fully immutable.”
This matters most right after a transfer. If you decoded a safeTransferFrom call with the Calldata Decoder and want to confirm it actually landed, re-running this inspector against the same token ID shows the current ownerOf result directly, which is a stronger check than trusting an indexer that may not have caught up to the latest block yet.
Build this yourself
Each of the snippets below makes the same POST call to the evmquery REST API. The expression is the one shown above, with your contract address and inputs filled in.
REST (curl)
curl -X POST https://api.evmquery.com/api/v1/query \ -H "x-api-key: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "chain": "evm_ethereum", "schema": { "contracts": { "nft": { "address": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D" } }, "context": { "id": "sol_int" } }, "expression": "{ \"name\": dyn(nft.name()), \"symbol\": dyn(nft.symbol()), \"tokenURI\": dyn(nft.tokenURI(id)), \"owner\": dyn(nft.ownerOf(id)) }", "context": { "id": 1 } }'Python
import requests
resp = requests.post( "https://api.evmquery.com/api/v1/query", headers={"x-api-key": "YOUR_API_KEY"}, json={ "chain": "evm_ethereum", "schema": { "contracts": {"nft": {"address": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D"}}, "context": {"id": "sol_int"}, }, "expression": ( '{ "name": dyn(nft.name()), "symbol": dyn(nft.symbol()),' ' "tokenURI": dyn(nft.tokenURI(id)), "owner": dyn(nft.ownerOf(id)) }' ), "context": {"id": 1}, }, timeout=10,)print(resp.json()["result"])TypeScript
const resp = await fetch("https://api.evmquery.com/api/v1/query", { method: "POST", headers: { "x-api-key": process.env.EVMQUERY_API_KEY!, "Content-Type": "application/json", }, body: JSON.stringify({ chain: "evm_ethereum", schema: { contracts: { nft: { address: "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D" }, }, context: { id: "sol_int" }, }, expression: '{ "name": dyn(nft.name()), "symbol": dyn(nft.symbol()), "tokenURI": dyn(nft.tokenURI(id)), "owner": dyn(nft.ownerOf(id)) }', context: { id: 1n }, }),});const { result } = await resp.json();The free tier has no monthly cap. Get a free API key to drop these snippets into your project.
Where owner and metadata checks actually get used
- Verifying provenance. Confirm who currently owns a specific token before you buy, accept as collateral, or feature it.
- Auditing a new collection. Quickly verify a contract really implements ERC-721 and inspect the metadata URI before you list or integrate it. If the contract address is unfamiliar, run it through the Contract Inspector first to see its full method schema and confirm it actually exposes the ERC-721 interface rather than a look-alike.
- Debugging an indexer. Compare the on-chain
tokenURI(id)andownerOf(id)results with what your indexer cached to find where it drifted. - Checking an upgradeable collection. Some larger NFT projects deploy behind an EIP-1967 proxy so the team can patch logic after launch. The Proxy Contract Detector confirms whether a given collection is one of them and which implementation your
tokenURIandownerOfcalls actually run against. - Resolving a name before you look up a token. If you have an ENS name rather than a raw address for a collection or a minter, the ENS Resolver turns it into the address this inspector needs. (ENS names are themselves ERC-721 tokens under the ENS Base Registrar, a separate contract from the resolver that this tool queries.)
- Writing ERC-721 docs. Show your readers a working
tokenURIandownerOfexample without making them set up a Hardhat console.
FAQ
What is ERC-721?
ERC-721 is the standard for non-fungible tokens. Each token ID on a contract is unique, has its own owner tracked by ownerOf(id), and typically points to its own metadata URI via tokenURI(id).
What is the difference between ERC-721 and ERC-1155?
ERC-721 gives every token ID its own owner and its own tokenURI, with no placeholder substitution needed. ERC-1155 lets one contract hold many token IDs that can be fungible or non-fungible, and its uri(id) returns a template with a literal {id} placeholder the client must substitute.
Why does tokenURI have no {id} placeholder to substitute?
The EIP-721 metadata extension defines tokenURI(uint256) as returning the final URL for that specific token, already resolved server-side by the contract. There is no client-side substitution step like the {id} template in EIP-1155.
Which chains are supported?
Ethereum, Base, BNB Smart Chain, and Polygon today. More EVM chains are being added on the evmquery backend. If you need a specific chain prioritized, contact support.
Does evmquery work with proxy contracts?
Yes. evmquery resolves EIP-1967 transparent and beacon proxies to their implementation automatically, so you call nft.tokenURI(id) against the proxy address and it works. Roughly a third of production contracts use proxies, including most upgradeable ERC-721 collections.
What if the metadata URI uses an IPFS gateway that times out?
The Inspector tries the URI as returned by the contract first. For ipfs:// URIs it falls back through a small list of public gateways. If none respond, the resolved URL is shown so you can open it directly or point your own application at a gateway you control.
Can I use this from my own application?
Yes. The same expression and contract address work against the public REST API. The free tier has no monthly cap, and a single inspection counts as one read because Multicall3 batches all the underlying calls into one round trip. Bring your own API key and the rate limit applied to this page no longer applies.
Limits and accuracy
ownerOfandtokenURIrevert for a token ID that was never minted or has since been burned. On an OpenZeppelin contract that surfaces asERC721NonexistentToken. A revert here is a real answer about the token, not a failure of the read.- Plenty of collections build
tokenURIby concatenating abaseURIwith the token ID, and the owner can point that base somewhere else later. What you read is today’s value, not a permanent one. totalSupplyonly exists if the contract pulls in the optionalERC721Enumerableextension. Most large collections leave it out to save gas on every mint and transfer.- The read reflects the latest block. There is no historical replay, so you cannot ask who owned a token last week.
- Image previews load straight from the metadata image URL. A throttling gateway or a missing file hides the preview.
- Floor prices, royalty configuration, and anything else that lives on a marketplace rather than in the contract are out of scope.
The metadata fetch runs from your browser. Some collections gate metadata behind authenticated origins, in which case only the on-chain values are shown.
Related
- ERC-1155 Inspector: the multi-token standard, for when one contract holds many token IDs instead of one owner per ID
- Contract Inspector: the full resolved method schema, for confirming a contract really implements ERC-721
- Proxy Contract Detector: check whether an NFT collection sits behind an upgradeable proxy
- Calldata Decoder: decode a
transferFromorsafeTransferFromcall before you check where it landed - ENS Resolver: resolve a
.ethname to the address this inspector needs - Multicall3 batching for EVM contract reads: why one expression can replace dozens of
eth_callround trips - Read smart contracts in n8n: wire the same query into a no-code workflow
- Building an EVM blockchain MCP server: let Claude or Cursor inspect ERC-721 collections in chat
- evmquery for developers: the full integration story