How to Filter Out Spam NFTs

Learn how to identify and filter spam NFTs using the Alchemy API.

If you’re building a wallet or an NFT marketplace like OpenSea, chances are you'll want to display NFTs that belong to a particular collection or are owned by a particular wallet.

Most blockchains, including Ethereum, allow anyone to airdrop NFTs into any wallet. Unfortunately, this means that users can sometimes receive NFTs that they do not actually want but are still visible on their profiles.

28802880

OpenSea identifies and hides certain NFTs by default

Identifying spam NFTs is a challenging problem to solve. Usually, it involves parsing the entire blockchain for ERC-721 and ERC-1155 contracts, tracking the NFT's activity, then labeling the activity as spam if it is suspicious (e.g., a copy of a popular project, airdropped without consent, etc.). This method typically requires an enormous amount of engineering resources and time.

Fortunately, you can bypass much of the above effort by using Alchemy’s NFT API. Alchemy has already completed much of the heavy lifting of identifying spam contracts and makes that data available through easy-to-use API endpoints.

In this article, we will retrieve a list of all spam smart contracts, then extract all NFTs owned by a particular wallet and compare it to our list to identify NFTs that belong to spam contracts.

📘

If you are new to using the NFT API, check out the NFT API Quickstart Guide to learn more about how the NFT API works.

Creating the Spam NFT Script

Step 1: Install Node and npm

To start, install node and npm on your local machine. If you already have them installed, ensure that your Node version is at least v14 or higher. To check, type the following in your terminal:

node -v

Step 2: Create a new Alchemy app

In case you haven't already, sign up for a free Alchemy account.

14401440

Alchemy dashboard where NFT developers can easily create a new app on a specific blockchain and network

Alchemy dashboard where NFT developers can easily create a new app on a specific blockchain and network.

Next, navigate to the Alchemy Dashboard and create a new app (Apps > Create App). Make sure you set the Chain to Ethereum and Network to Mainnet.

Once you create the app:

  1. Click on your app's View Key button.
  2. Save the HTTP URL (e.g., something like https://eth-mainnet.alchemyapi.io/nft/v2/xxxxxxxxx).

You will need this URL later in this tutorial.

Step 3: Create a Node Project

Next, create an empty repository and install all node dependencies.

To make requests to the NFT API, we recommend using the Alchemy SDK.

Alternatively, you can also use either axios or fetch libraries. From your terminal, run the following commands:

mkdir nft-spam && cd nft-spam
npm init -y
npm install --save alchemy-sdk
touch main.js
mkdir nft-spam && cd nft-spam
npm init -y
npm install --save axios
touch main.js
mkdir nft-spam && cd nft-spam
npm init -y
touch main.js

The above creates a repository named nft-spam that holds all the files and dependencies we need and the main.js file, where we will write our code. Open this repo in your preferred code editor (e.g., VS Code).

Step 4: Get all Spam Contracts

Next, retrieve a list of all smart contracts on Ethereum that Alchemy has classified as spam. To do this, we will use the getSpamContracts endpoint. This function does not take in any parameters.

For more information about how Alchemy creates this list, check out the NFT API FAQ.

Add the following code to the main.js file, being sure to replace your HTTP URL where prompted:

const { Alchemy } = require('alchemy-sdk');

const apiKey = "<-- ALCHEMY APP API KEY -->";
const settings = {
    apiKey: apiKey
};

const alchemy = new Alchemy(settings);

const main = async () => {

    // Get spam contracts
    const response = await alchemy.nft.getSpamContracts();
    console.log(response);

}

const runMain = async () => {
    try {
        await main();
        process.exit(0);
    }
    catch (error) {
        console.log(error);
        process.exit(1);
    }
};

runMain();
const axios = require('axios');

// Alchemy HTTP URL
const alchemyURL = "<--ALCHEMY APP HTTP URL -->";

const baseURL = `${alchemyURL}/getSpamContracts`;

var config = {
    method: 'get',
    url: `${baseURL}`,
    headers: {}
};

axios(config)
    .then(response => {
        const spamContracts = response.data;
        console.log(spamContracts)
    })
    .catch(error => console.log(error));
import fetch from 'node-fetch';

var requestOptions = {
    method: 'GET',
    redirect: 'follow'
};

// Alchemy HTTP URL
const alchemyURL = "<-- ALCHEMY APP HTTP URL -->"

const baseURL = `${alchemyURL}/getSpamContracts`;

fetch(baseURL, requestOptions)
  .then(response => response.json())
  .then(result => console.log(result))
  .catch(error => console.log('error', error));

Run this script using the following command:

node main.js

This outputs the following in your terminal:

[
  '0x000000000000d0151e748d25b766e77efe2a6c83',
  '0x0000000005756b5a03e751bd0280e3a55bc05b6e',
  '0x0000000010c38b3d8b4d642d9d065fb69bc77bc7',
  '0x000386e3f7559d9b6a2f5c46b4ad1a9587d59dc3',
  '0x000440f08436a7b866d1ae42db5e0be801da722a',
  '0x000a682feeeffc5e56a58a3b015fb07665d8a979',
  '0x0015f391949f25c3211063104ad4afc99210f85c',
  '0x0017a48686659fd40c0c71b57267d3eb506aa434',
  ...
  '0x0482878e808aa5dc3d16839a2c0d3d50468b0577',
  '0x04844d3d90e5043e42adcbef54361ccb761a121c',
  '0x048974558a74595c7847f6c0d0cf5f511cb58d34',
  ... 4752 more items
]

Step 5: Identifying Spam NFTs in a Wallet

Alchemy has identified close to 5000 smart contracts to be spam. If an NFT originates from any of these spam contracts, it is likely to be spam as well.

Now, let’s retrieve a list of all NFTs owned by a particular wallet (if you’re not sure how to do this, check out this tutorial). Then, we will check if the NFTs belong to a spam contract. To do this, we will check if the smart contract in question belongs to the list we generated above.

An alternative is to use the isSpamContract function that reads a contract address as a parameter and returns a boolean to indicate whether the contract is spam or not.

An example of the isSpamContract would be the following:

const { Alchemy } = require('alchemy-sdk');

const apiKey = "<-- ALCHEMY APP API KEY -->";
const settings = {
    apiKey: apiKey
};

const alchemy = new Alchemy(settings);

const main = async () => {

        const contractAddr = "0x000440f08436a7b866d1ae42db5e0be801da722a";

    // Check if contract is spam
    const response = await alchemy.nft.isSpamContract(contractAddr);
    console.log(response);

}

const runMain = async () => {
    try {
        await main();
        process.exit(0);
    }
    catch (error) {
        console.log(error);
        process.exit(1);
    }
};

runMain();
const axios = require('axios')

// Alchemy HTTP URL
const alchemyURL = "<-- ALCHEMY APP HTTP URL -->"
const baseURL = `${alchemyURL}/isSpamContract`;

const contractAddr = "0x000440f08436a7b866d1ae42db5e0be801da722a";

var config = {
  method: 'get',
  url: `${baseURL}?contractAddress=${contractAddr}`,
  headers: { }
};

axios(config)
.then(response => console.log(response.d)
.catch(error => console.log(error));
import fetch from 'node-fetch';

var requestOptions = {
    method: 'GET',
    redirect: 'follow'
};

// Alchemy HTTP URL
const alchemyURL = "<-- ALCHEMY APP HTTP URL -->"
const baseURL = `${alchemyURL}/isSpamContract`;

const contractAddr = "0x000440f08436a7b866d1ae42db5e0be801da722a";
const fetchURL = `${baseURL}?contractAddress=${contractAddr}`;

fetch(fetchURL, requestOptions)
  .then(response => response.json())
  .then(result => console.log(result))
  .catch(error => console.log('error', error));

Running the above script will return a boolean:

true

However, if we're dealing with a large number of NFTs, it may make sense to limit the number of API calls. We can extract all spam contracts using getSpamContracts at once and perform the check locally.

To implement this method, replace the contents of main.js with the following:

const { Alchemy } = require('alchemy-sdk');

const apiKey = "<-- ALCHEMY APP API KEY -->";
const settings = {
    apiKey: apiKey
};

const alchemy = new Alchemy(settings);

const main = async () => {

    const address = 'elanhalpern.eth';

    // Get spam contracts
    let spamContracts = await alchemy.nft.getSpamContracts();

    // Get NFTs of owner
    let nfts = await alchemy.nft.getNftsForOwner(address);

    console.log("Spam NFTs:")
    counter = 1

    // Print out titles of spam NFTs
    for (let i = 0; i < nfts['ownedNfts'].length; i++) {
        contractAddr = nfts['ownedNfts'][i]['contract']['address'];
        if (spamContracts.includes(contractAddr)) {
            console.log(`${counter}. ${nfts['ownedNfts'][i]['title']}`);
            counter += 1;
        }
    }

}

const runMain = async () => {
    try {
        await main();
        process.exit(0);
    }
    catch (error) {
        console.log(error);
        process.exit(1);
    }
};

runMain();
const axios = require('axios');

// Alchemy API URL
const alchemyURL = `<-- ALCHEMY APP HTTPL URL -->`;

// Wallet address
const address = 'elanhalpern.eth';

// Define NFT API endpoints
const allNftsUrl = `${alchemyURL}/getNFTs/?owner=${address}`;
const spamUrl = `${alchemyURL}/getSpamContracts`;

const allNftsConfig = {
    method: 'get',
    url: allNftsUrl,
};

const spamConfig = {
    method: 'get',
    url: spamUrl,
}

const getSpamNfts = async () => {

    // Get all spam contracts
        let spamContracts = await axios(spamConfig);
    spamContracts = spamContracts['data']

    // Get all NFTs in wallet (top 100)
    let nfts = await axios(allNftsConfig)
    nfts = nfts['data']['ownedNfts']

    console.log("Spam NFTs:")
    counter = 1

    // Print out titles of spam NFTs
        for (let i = 0; i < nfts.length; i++) {
        contractAddr = nfts[i]['contract']['address'];
        if (spamContracts.includes(contractAddr)) {
            console.log(`${counter}. ${nfts[i]['title']}`);
            counter += 1;
        }
    }
}

getSpamNfts()
import fetch from 'node-fetch';

// Alchemy API URL
const alchemyURL = `<-- ALCHEMY APP HTTPL URL -->`;

// Wallet address
const address = 'elanhalpern.eth';

// Define NFT API endpoints
const allNftsUrl = `${alchemyURL}/getNFTs/?owner=${address}`;
const spamUrl = `${alchemyURL}/getSpamContracts`;

var requestOptions = {
    method: 'GET',
    redirect: 'follow'
};

const getSpamNfts = async () => {

    // Get all spam contracts
        let spamContracts = await fetch(spamUrl, requestOptions);
    spamContracts = spamContracts['data']

    // Get all NFTs in wallet (top 100)
        let nfts = await axios(allNftsUrl, requestOptions);
    nfts = nfts['data']['ownedNfts']

    console.log("Spam NFTs:")
    counter = 1

    // Print out titles of spam NFTs
        for (let i = 0; i < nfts.length; i++) {
        contractAddr = nfts[i]['contract']['address'];
        if (spamContracts.includes(contractAddr)) {
            console.log(`${counter}. ${nfts[i]['title']}`);
            counter += 1;
        }
    }
}

getSpamNfts()

Run the code with the following command:

node main.js

This code should produce output that looks like this:

Spam NFTs:
1. #4842
2. MAVION Gems - The OG

Conclusion

Congratulations! You now know how to use the Alchemy NFT API to retrieve identified spam contracts as well as spam NFTs in a particular wallet.

If you enjoyed this tutorial on how to get all NFTs owned by an address, tweet us at @AlchemyPlatform and give the authors @rounak_banik and @ankg404 a shoutout!

Don't forget to join our Discord server to meet other blockchain devs, builders, and entrepreneurs!

Ready to start using the Alchemy NFT API?

Create a free Alchemy account and share your project with us!


Did this page help you?