Home / Solutions / NFT Indexing
NFT Indexing

NFT data across every chain: mints, transfers, and marketplace sales

Onchain, an NFT's life is a log stream: a mint is a Transfer from the zero address, a sale is a marketplace event like Seaport's OrderFulfilled riding in the same transaction as the transfer it settles. SQD serves those events decoded and keyed by topic0 across 225+ networks, the history you fold into ownership, collection, and sales feeds.

225+ Networks
Genesis Full history
ERC-721 + 1155 Transfer standards
NDJSON Streaming
The problem

An NFT's whole history, from mint to sale, lives onchain as a log stream

A mint is a Transfer from the zero address, and a sale is a marketplace event that rides in the same transaction as the transfer it settles. Rebuilt the naive way, reading those events back means paginating an archive node over eth_getLogs against per-call caps, then writing a separate decoder for each token standard and marketplace you cover. The events are all public already; the cost is in how you read them back.

How it works

ERC-20 and ERC-721 share a topic0. The topic count separates them.

An event's topic0 is the keccak256 hash of its signature, and both standards define exactly Transfer(address,address,uint256). A USDT payment and a Bored Ape changing hands emit the identical topic0 (the hash in the query below), so a filter on the hash alone cannot tell them apart.

What separates them is the topic count. ERC-20 leaves the amount unindexed: three topics, value in data. EIP-721 requires all three parameters indexed: four topics, tokenId in topic3, empty data. Counted live over 20 pinned blocks:

Event shapeLogs
  • Transfer, 3 topics (ERC-20: amount in data)2,951
  • Transfer, 4 topics (ERC-721: tokenId in topic3)43
  • TransferSingle (ERC-1155)9
  • TransferBatch (ERC-1155)1
Token transfer logs, Ethereum blocks 22,000,000-22,000,019, streamed from the Portal and grouped by topic count.

2,994 logs in the window share the Transfer topic0; only 43 are NFT movements. A feed filtered on the hash alone buries the collection activity in token payments; the four-topic test is the line. ERC-1155 sidesteps the collision with its own signatures but moves ids and amounts into data, singly or as parallel arrays, so the decoder changes even though the filter does not.

A mint is the same event with a fixed sender: a Transfer from the zero address. Pinned to a window from the mint of BAYC (0xbc4c...f13d):

your terminal
curl -s -X POST https://portal.sqd.dev/datasets/ethereum-mainnet/stream \
-H 'content-type: application/json' \
-d '{
"type": "evm",
"fromBlock": 12346000, "toBlock": 12347000,
"logs": [{
"address": ["0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d"],
"topic0": ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],
"topic1": ["0x0000000000000000000000000000000000000000000000000000000000000000"]
}],
"fields": { "log": { "topics": true, "data": true, "transactionHash": true }, "block": { "number": true } }
}'
first line of the stream
{
"header": { "number": 12346000 },
"logs": [{
"transactionHash": "0xfeabe2ecf73d44cd8dd6e9002638b8599ba555de59e25115173330a42c091fb3",
"data": "0x",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000003df03ae2b4e5a2b82192031e13bca98a7e3fcc18",
"0x0000000000000000000000000000000000000000000000000000000000000449"
]
}]
}

topic1 is the zero address, so this is a mint; topic2 is the recipient (0x3df0…cc18); topic3 is the tokenId, 0x449 = BAYC #1097, minted at block 12,346,000 (2021-05-01) in 0xfeab…1fb3. The full 1,001-block window streams back 3,715 mints in one request; the same read over eth_getLogs is a pagination loop against per-call caps on an archive node, measured here.

Marketplace sales

Marketplace sales are logs too

A sale is not a separate data product; it is an event the marketplace contract emits in the same log stream as the transfer it settles. Seaport 1.6 (0x0000...b395) emits OrderFulfilled; one filter on that address and topic0 returns 2,035 fills over blocks 25,490,000 to 25,491,000. Pinned to a single block from that window:

your terminal
curl -s -X POST https://portal.sqd.dev/datasets/ethereum-mainnet/stream \
-H 'content-type: application/json' \
-d '{
"type": "evm",
"fromBlock": 25490002, "toBlock": 25490002,
"logs": [{
"address": ["0x0000000000000068f116a894984e2db1123eb395"],
"topic0": ["0x9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31"]
}],
"fields": { "log": { "topics": true, "data": true, "transactionHash": true }, "block": { "number": true } }
}'

The three logs that come back are one transaction, 0x48d4…9bb7: a sweep that filled three listings from collection 0x6c0d...8107, two sellers, one buyer. Decoded from each log's data field against the Seaport ABI:

Seaport 1.6 fills, block 25,490,002 price (ETH)
Block 25,490,002 (2026-07-08). Each order's consideration legs sum to the price exactly: 0.00021805 ETH to the seller (89%), 0.00000245 ETH (1%) to one fee recipient, and 0.00001225 ETH (5%) to each of two more.

Two things the event does not hand you. OrderFulfilled does not label which consideration leg is a marketplace fee and which is a royalty; attribution is by recipient address. And the ownership change is not in this log: it rides in the same transaction as three ERC-721 Transfer logs carrying the same token ids (2422, 3016, 2423), so price and ownership join on the transaction hash. A curated NFT-sales API makes both calls for you and covers only the venues it chose to parse; the log stream carries every venue that settles onchain, decoded on your side.

Where the data stops: the Portal serves the events and transactions the chain records. Token metadata (tokenURI, images, traits) lives at the URI the contract points to and is fetched downstream; floor prices are marketplace aggregates, not onchain events; and there is no current-owner or ownerOf-at-block lookup. Ownership at any block is a fold you run over the transfer history, per tokenId, last recipient wins.

Capabilities

Built for NFT Indexing at scale

Mints, transfers, and burns from one filter

An ERC-721 mint is a Transfer from the zero address, a burn is one to it, and a sale settles as a transfer between wallets. One topic-filtered query returns all three for any collection, from its deployment block to chain head, with the tokenId in topic3.

ERC-1155 batch semantics

ERC-1155 emits TransferSingle and TransferBatch under their own signatures, with ids and amounts ABI-encoded in the data field rather than in topics. Both arrive in the same log stream as ERC-721 Transfers, so one indexer covers both standards.

Marketplace sales, decoded

A sale is a contract event: Seaport emits OrderFulfilled with the offer and consideration items, price legs included, in one log. Join it to the ERC-721 Transfer in the same transaction and you have the seller, the buyer, the token, and the price.

One query shape, 225+ networks

The same topic0-filtered query runs on Ethereum, Base, Arbitrum, Polygon, and every other supported EVM network; only the dataset name and the contract address change. A cross-chain collection feed is one integration, not one per chain.

Why SQD

What you would build yourself

The mints, transfers, and sales are all public events in one log stream; the difference is in the reading, and it compounds with every token standard and every marketplace.

  • Token vs NFT transfers
    Yourself ERC-20 and ERC-721 share the Transfer topic0, so a filter on the hash alone buries NFT movements in token payments.
    With SQD The topic count separates them in the same stream: three topics is an ERC-20 amount in data, four is an NFT tokenId in topic3.
  • History backfill
    Yourself Replaying a collection's mints and transfers over eth_getLogs is a pagination loop against per-call caps on an archive node.
    With SQD One topic-filtered request streams the whole block range back, from a collection's deployment block to chain head.
  • ERC-1155 batches
    Yourself TransferSingle and TransferBatch use their own signatures and move ids and amounts into data, singly or as parallel arrays, so the decoder changes.
    With SQD Both signatures arrive in the same log stream as ERC-721 Transfers, so one indexer covers both standards.
  • Marketplace sales
    Yourself A curated NFT-sales API parses only the venues it chose and covers no others.
    With SQD One filter on a marketplace address and topic0 returns its OrderFulfilled fills; every venue that settles onchain is queryable the same way, decoded on your side.

See this on your own contracts

Tell us the chains and contracts you care about, and we will show you the query that returns them.

Use cases

What teams build with SQD

  • Collection activity feeds with decoded mints and transfers
  • Marketplace sale and royalty-split analytics
  • Ownership history reconstructed from transfer streams
  • Mint monitoring for collection launches
  • Cross-chain NFT activity dashboards from one query shape
  • Wash-trade screening from sale and transfer joins

Frequently asked questions

How do I query NFT transfers with the SQD API?
Filter logs by the shared Transfer topic0 and keep the rows with four topics: ERC-721 indexes from, to, and tokenId, while the ERC-20 version uses three topics with the amount in data. Add the contract address to scope one collection, and the TransferSingle and TransferBatch signatures to cover ERC-1155. The result streams as NDJSON over HTTP, from the collection's deployment block to chain head. The mechanics are covered in the EVM indexer guide.
Does SQD serve NFT metadata, floor prices, or current owners?
No. The Portal serves what the chain records: logs, transactions, traces, and state diffs. Token metadata (tokenURI, images, traits) lives at the URI the contract points to and is fetched downstream; floor prices are marketplace aggregates, not onchain events; and there is no current-owner or ownerOf-at-block lookup. Ownership at any block is reconstructed from the transfer history the Portal does serve: fold the transfers per tokenId, and the last recipient is the owner.
Which NFT marketplaces are covered?
There is no curated marketplace list: any venue that settles onchain is queryable by its contract address and event signatures. This page reads Seaport 1.6 OrderFulfilled events live; the same pattern applies to any other marketplace contract, on any of the 225+ supported networks, with history back to each chain's genesis.
Get started

Your blockchain data infrastructure, handled.

Private Portal. Dedicated. Validated. Managed. Tell us what you're building, we'll show you what it looks like on SQD.