You need to verify whether a wallet holds an NFT before granting access, applying a discount, or making a trust decision. Most approaches require running your own node, indexing contract events, or calling chain-specific APIs. InsumerAPI does it with one POST request across 32 blockchains. Here is the working code.
The simplest NFT ownership check
The attestation endpoint is POST /v1/attest. You send a wallet address and an array of conditions. For NFT ownership, use type: "nft_ownership" with the collection contract address and chain ID. No threshold is needed. The API verifies whether the wallet owns at least one NFT from the collection.
curl:
curl -X POST https://api.insumermodel.com/v1/attest \
-H "X-API-Key: insr_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"wallet": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"conditions": [
{
"type": "nft_ownership",
"contractAddress": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
"chainId": 1,
"label": "Bored Ape Yacht Club"
}
]
}'
The response returns met: true or met: false. It never reveals how many NFTs the wallet holds or which token IDs it owns.
Python example
import requests
API_KEY = "insr_live_YOUR_KEY_HERE"
BASE = "https://api.insumermodel.com"
resp = requests.post(
f"{BASE}/v1/attest",
headers={
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
json={
"wallet": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"conditions": [
{
"type": "nft_ownership",
"contractAddress": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
"chainId": 1,
"label": "Bored Ape Yacht Club",
}
],
},
)
data = resp.json()
owns_nft = data["data"]["attestation"]["results"][0]["met"]
print(f"Owns a Bored Ape: {owns_nft}")
The boolean result is at data["data"]["attestation"]["results"][0]["met"]. The overall pass field at data["data"]["attestation"]["pass"] is true only when all conditions are met.
JavaScript example
const API_KEY = "insr_live_YOUR_KEY_HERE";
const BASE = "https://api.insumermodel.com";
const resp = await fetch(`${BASE}/v1/attest`, {
method: "POST",
headers: {
"X-API-Key": API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
wallet: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
conditions: [
{
type: "nft_ownership",
contractAddress: "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
chainId: 1,
label: "Bored Ape Yacht Club",
},
],
}),
});
const data = await resp.json();
const ownsNft = data.data.attestation.results[0].met;
console.log("Owns a Bored Ape:", ownsNft);
What comes back
The response looks like this:
{
"ok": true,
"data": {
"attestation": {
"id": "ATST-B8D4F6A7E9C01234",
"pass": true,
"results": [
{
"condition": 0,
"label": "Bored Ape Yacht Club",
"type": "nft_ownership",
"chainId": 1,
"met": true,
"evaluatedCondition": {
"type": "nft_ownership",
"chainId": 1,
"contractAddress": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
"operator": "gt",
"threshold": 0
},
"conditionHash": "0x7e2f...",
"blockNumber": "0x13a1b40",
"blockTimestamp": "2026-03-06T10:00:00.000Z"
}
],
"passCount": 1,
"failCount": 0,
"attestedAt": "2026-03-06T10:00:01.000Z",
"expiresAt": "2026-03-06T10:30:01.000Z"
},
"sig": "...",
"kid": "insumer-attest-v1"
},
"meta": { "creditsRemaining": 97, "creditsCharged": 1, "version": "1.0", "timestamp": "2026-03-06T10:00:01.000Z" }
}
Key fields:
metis a boolean. No NFT count. No token IDs. Justtrueorfalse.passistrueonly when all conditions in the request are met.sigis an ECDSA P-256 signature. You can verify it against the public key atGET /v1/jwksor the static/.well-known/jwks.json.expiresAtis 30 minutes from attestation time.- The
evaluatedConditionshows the resolved check:operator: "gt"andthreshold: 0, meaning "owns more than zero."
Multiple NFT collections in one call
You can verify up to 10 conditions in a single call, each on a different chain. This costs 1 credit total regardless of how many conditions you include.
curl -X POST https://api.insumermodel.com/v1/attest \
-H "X-API-Key: insr_live_YOUR_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"wallet": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
"solanaWallet": "YOUR_SOLANA_WALLET_ADDRESS",
"conditions": [
{
"type": "nft_ownership",
"contractAddress": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
"chainId": 1,
"label": "Bored Ape Yacht Club"
},
{
"type": "nft_ownership",
"contractAddress": "0xBd3531dA5CF5857e7CfAA92426877b022e612cf8",
"chainId": 1,
"label": "Pudgy Penguins"
},
{
"type": "nft_ownership",
"contractAddress": "YOUR_SOLANA_NFT_COLLECTION_ADDRESS",
"chainId": "solana",
"label": "Solana NFT Collection"
}
]
}'
Each result comes back independently. If the wallet holds a Bored Ape and a Pudgy Penguin but not the Solana NFT, you get met: true for the first two and met: false for the third. The overall pass will be false because not all conditions were met.
For Solana NFTs, pass the Solana wallet address in the solanaWallet parameter and use "solana" as the chain ID. EVM and Solana conditions can be mixed in the same request.
NFT check without exposing what they hold
This is the key privacy property of the API. A standard NFT ownership check on a block explorer or indexer reveals the wallet's entire collection: every token ID, every contract, every transfer history. The InsumerAPI attestation returns only a boolean.
The question the API answers is: "Does this wallet own at least one NFT from this collection?" The answer is yes or no, following the same privacy-by-design principle as every other endpoint. Nothing else leaks. Not which specific token IDs the wallet holds. Not how many. Not when they were acquired.
This matters for privacy-preserving verification. A token-gated community can confirm membership eligibility without learning the member's full portfolio. A checkout system can verify discount eligibility without cataloging assets.
Verifying the signature
Every response includes an ECDSA P-256 signature in the sig field. To verify it independently, use the insumer-verify npm package:
npm install insumer-verify
import { verifyAttestation } from "insumer-verify";
// Pass the full API response envelope, not apiResponse.data
const result = await verifyAttestation(apiResponse);
console.log("Signature valid:", result.valid);
console.log("Signer:", result.kid);
The package fetches the public key from /.well-known/jwks.json and verifies the signature locally. No API call needed for verification. You can also pass a custom jwksUrl if you want to pin the key.
What to build with NFT ownership checks
- Token-gated content and features. Verify a wallet holds an NFT from a specific collection before granting access to premium content, exclusive channels, or gated features.
- NFT-based discounts. Verify collection ownership at checkout and apply discounts automatically. One API call replaces manual screenshot verification. See real-world examples from NBA Top Shot, Socios, and Pudgy Penguins.
- DAO membership gates. Verify NFT collection ownership as a prerequisite for governance participation, voting, or proposal submission.
- AI agent identity. An agent proves it controls a wallet holding specific NFTs as part of a trust evaluation or capability proof. Businesses can also attract NFT holders as customers through targeted verification.
- Cross-chain NFT verification. The same API and the same request format works on Ethereum, Polygon, Base, Arbitrum, Solana, and 27 other chains. No chain-specific logic needed.
The full API reference covers additional endpoints including wallet trust profiles (POST /v1/trust), merchant discovery (GET /v1/merchants), and compliance templates (GET /v1/compliance/templates).
Get a free API key in 30 seconds
10 free credits. 32 blockchains. No credit card. Start verifying NFT ownership with one API call.
View Developer Portal