You need to check if a wallet holds a specific token. Maybe you are building token-gated access, verifying eligibility for a discount, or scoring a wallet for a trust system. The traditional approach means running an RPC node (or paying for one), handling each chain separately, parsing contract ABIs, and managing decimals. Or you can do it in one API call across 33 chains. Here is how.

What you need

That is it. No node. No ABI. No Web3 library.

The endpoint

The attestation endpoint is POST /v1/attest. You send a wallet address and an array of conditions. Each condition specifies a token, a chain, and a threshold. The API verifies on-chain whether the wallet meets each condition and returns a signed boolean result.

The response never reveals the actual amount in the wallet. You get true or false for each condition, plus an ECDSA P-256 signature proving the result is authentic.

Example 1: Verify USDC holdings on Ethereum

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": "token_balance",
        "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        "chainId": 1,
        "threshold": 100,
        "decimals": 6,
        "label": "USDC on Ethereum"
      }
    ]
  }'

Python:

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": "token_balance",
                "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
                "chainId": 1,
                "threshold": 100,
                "decimals": 6,
                "label": "USDC on Ethereum",
            }
        ],
    },
)

data = resp.json()
print(f"Pass: {data['data']['attestation']['pass']}")
for r in data["data"]["attestation"]["results"]:
    print(f"  {r['label']}: {'met' if r['met'] else 'not met'}")

JavaScript (Node.js or browser):

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: "token_balance",
        contractAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        chainId: 1,
        threshold: 100,
        decimals: 6,
        label: "USDC on Ethereum",
      },
    ],
  }),
});

const data = await resp.json();
console.log("Pass:", data.data.attestation.pass);
data.data.attestation.results.forEach((r) => {
  console.log(`  ${r.label}: ${r.met ? "met" : "not met"}`);
});

What comes back

The response looks like this:

{
  "ok": true,
  "data": {
    "attestation": {
      "id": "ATST-A7C3E1B2D4F56789",
      "pass": true,
      "results": [
        {
          "condition": 0,
          "label": "USDC on Ethereum",
          "type": "token_balance",
          "chainId": 1,
          "met": true,
          "evaluatedCondition": {
            "type": "token_balance",
            "chainId": 1,
            "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
            "operator": "gte",
            "threshold": 100,
            "decimals": 6
          },
          "conditionHash": "0x3a7f1b2c...",
          "blockNumber": "0x12f4a80",
          "blockTimestamp": "2026-02-28T12:00:00.000Z"
        }
      ],
      "passCount": 1,
      "failCount": 0,
      "attestedAt": "2026-02-28T12:00:00.000Z",
      "expiresAt": "2026-02-28T12:30:00.000Z"
    },
    "sig": "...",
    "kid": "insumer-attest-v1"
  },
  "meta": { "creditsRemaining": 99, "creditsCharged": 1, "version": "1.0", "timestamp": "2026-02-28T12:00:01.000Z" }
}

Key fields:

Example 2: Multiple conditions, multiple chains

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",
    "conditions": [
      {
        "type": "token_balance",
        "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        "chainId": 1,
        "threshold": 100,
        "decimals": 6,
        "label": "USDC on Ethereum"
      },
      {
        "type": "token_balance",
        "contractAddress": "0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE",
        "chainId": 1,
        "threshold": 1000000,
        "decimals": 18,
        "label": "SHIB on Ethereum"
      },
      {
        "type": "nft_ownership",
        "contractAddress": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
        "chainId": 1,
        "label": "Bored Ape Yacht Club"
      }
    ]
  }'

Each result comes back independently. If the wallet holds USDC and SHIB but not a Bored Ape, 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.

Example 3: NFT ownership check

For NFTs, use type: "nft_ownership". No threshold needed. The API verifies whether the wallet owns at least one NFT from the collection.

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",
            }
        ],
    },
)

result = resp.json()
owns_ape = result["data"]["attestation"]["results"][0]["met"]
print(f"Owns a Bored Ape: {owns_ape}")

Example 4: Solana tokens

Solana works the same way. Pass "solana" as the chain ID instead of a number.

curl -X POST https://api.insumermodel.com/v1/attest \
  -H "X-API-Key: insr_live_YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "solanaWallet": "YOUR_SOLANA_WALLET_ADDRESS",
    "conditions": [
      {
        "type": "token_balance",
        "contractAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
        "chainId": "solana",
        "threshold": 10,
        "decimals": 6,
        "label": "USDC on Solana"
      }
    ]
  }'

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.

Supported chains

The API covers 32 blockchains with a single endpoint. No chain-switching, no different endpoints, no different libraries:

Use the chain ID number for EVM chains and "solana" as a string for Solana. The full list with chain IDs is in the OpenAPI spec.

Pricing

Each attestation call costs 1 credit regardless of how many conditions are included (up to 10). Free tier includes 10 credits. Credits are $0.04 each on paid plans.

For a full breakdown of tiers and pricing, see the developer portal.

What to build with this

Developers are using the attestation endpoint for:

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 token holdings with one API call.

View Developer Portal