The user's full NFT collection, with artwork and details, displayed in the 'Collectibles' tab.

Your wallet now displays token balances, calculates total portfolio value, and tracks detailed account activity. To give users a holistic view of their onchain assets, the final piece is to showcase their NFT collections. In this third and final guide of our wallet series, we will focus on implementing the Collectibles tab.
This guide assumes you have completed the previous guides:
  1. Build a Realtime Wallet
  2. Add Account Activity

Explore the NFT Collection

See the collectibles feature in action with the live demo below. Click on the “Collectibles” tab to browse the sample wallet’s NFT collection:

Fetch NFT Collectibles

Let’s add a new asynchronous getWalletCollectibles function to server.js to fetch a user’s NFT collection using the Collectibles API.
server.js (getWalletCollectibles function)
async function getWalletCollectibles(walletAddress, limit = 50) {
    if (!walletAddress) return [];

    const url = `https://api.sim.dune.com/v1/evm/collectibles/${walletAddress}?limit=${limit}`;

    try {
        const response = await fetch(url, {
            headers: {
                'X-Sim-Api-Key': SIM_API_KEY,
                'Content-Type': 'application/json'
            }
        });

        if (!response.ok) {
            const errorBody = await response.text();
            console.error(`Collectibles API request failed with status ${response.status}: ${response.statusText}`, errorBody);
            throw new Error(`Collectibles API request failed: ${response.statusText}`);
        }

        const data = await response.json();
        const collectibles = data.entries || [];
        
        // Use Sim APIs data directly
        return collectibles.map(collectible => {
            return {
                ...collectible,
                // Use collection_name field, fallback to name if not available
                collection_name: collectible.name || `Token #${collectible.token_id}`
            };
        }).filter(collectible => collectible.image_url); // Only show collectibles with images
        
    } catch (error) {
        console.error("Error fetching wallet collectibles:", error.message);
        return [];
    }
}
The NFT data is extracted from the entries array within this response, providing comprehensive information including contract addresses, token IDs, chain data, and rich metadata.
The Collectibles API supports pagination using limit and offset query parameters. For wallets with many NFTs, you can implement logic to fetch subsequent pages using the next_offset value returned by the API to provide a complete view of the user’s collection.

Rich NFT Data and Images

The Sim APIs Collectibles API provides comprehensive NFT data including images, metadata, and collection information directly in the response. The Collectibles API includes:

Image URLs

Direct access to NFT artwork via image_url field

Metadata

Rich metadata including attributes, descriptions, and collection details

Collection Information

Names, symbols, and collection-specific data

Acquisition Data

When the NFT was last acquired and sale price information

Token Details

Contract addresses, token IDs, token standards (ERC-721, ERC-1155)

Multichain Support

NFTs from Ethereum, Polygon, Optimism, Base, and other supported chains

Add Collectibles into the Server Route

Next, we update our main app.get('/') route handler in server.js to call this new function:
server.js (app.get('/') updated for collectibles)
app.get('/', async (req, res) => {
    const { 
        walletAddress = '',
        tab = 'tokens'
    } = req.query;

    let tokens = [];
    let activities = [];
    let collectibles = []; // Initialize collectibles array
    let totalWalletUSDValue = 0;
    let errorMessage = null;

    if (walletAddress) {
        try {
            // Fetch balances, activities, and collectibles concurrently for better performance
            [tokens, activities, collectibles] = await Promise.all([
                getWalletBalances(walletAddress),
                getWalletActivity(walletAddress, 25), // Fetching 25 recent activities
                getWalletCollectibles(walletAddress, 50) // Fetching up to 50 collectibles
            ]);

            // Calculate total portfolio value from token balances (Guide 1)
            if (tokens && tokens.length > 0) {
                totalWalletUSDValue = tokens.reduce((sum, token) => {
                    const value = parseFloat(token.value_usd);
                    return sum + (isNaN(value) ? 0 : value);
                }, 0);
            }
        } catch (error) {
            console.error("Error in route handler fetching all data:", error);
            errorMessage = "Failed to fetch wallet data. Please try again.";
        }
    }
    
    res.render('wallet', {
        walletAddress: walletAddress,
        currentTab: tab,
        totalWalletUSDValue: `$${totalWalletUSDValue.toFixed(2)}`,
        tokens: tokens,
        activities: activities,
        collectibles: collectibles, // Pass collectibles to the template
        errorMessage: errorMessage
    });
});
The route handler now fetches balances, activities, and collectibles data concurrently for optimal performance. The collectibles array, containing comprehensive blockchain data and image URLs directly from Sim APIs, is passed to the wallet.ejs template.

Display Collectibles in the Frontend

The final step is to modify views/wallet.ejs to render the fetched collectibles within the “Collectibles” tab. We will use a grid layout to display NFT images with their collection names and token IDs. In views/wallet.ejs, find the section for the “Collectibles” tab (you can search for id="collectibles"). It currently contains a placeholder paragraph. Replace that entire div with the following EJS:
views/wallet.ejs (Collectibles tab content)
<!-- Collectibles Tab Pane -->
<div id="collectibles" class="tab-pane <%= currentTab === 'collectibles' ? 'active' : '' %>">
    <% if (collectibles && collectibles.length > 0) { %>
        <div class="collectibles-grid">
            <% collectibles.forEach(collectible => { %>
                 <div class="collectible-item-link">
                    <div class="collectible-item">
                        <div class="collectible-image-container">
                            <% if (collectible.image_url) { %>
                                <img src="<%= collectible.image_url %>" alt="<%= collectible.collection_name || collectible.name || 'NFT' %>" class="collectible-image">
                            <% } else { %>
                                <div class="collectible-image-placeholder">
                                    NFT
                                </div>
                            <% } %>
                        </div>
                        <div class="collectible-info-static">
                            <div class="collectible-name">
                                <%= collectible.collection_name || collectible.name || `Token #${collectible.token_id}` %>
                            </div>
                            <div class="collectible-collection">
                                #<%= collectible.token_id.length > 10 ? collectible.token_id.substring(0, 8) + '...' : collectible.token_id %>
                            </div>
                        </div>
                    </div>
                </div>
            <% }); %>
        </div>
    <% } else if (walletAddress) { %>
        <p style="text-align: center; padding-top: 30px; color: var(--color-text-muted);">No collectibles found for this wallet.</p>
    <% } else { %>
        <p style="text-align: center; padding-top: 30px; color: var(--color-text-muted);">Enter a wallet address to see collectibles.</p>
    <% } %>
</div>
The EJS template iterates through the collectibles array and displays each NFT with its comprehensive metadata directly from Sim APIs. Each collectible shows the image_url, the collection_name or fallback name, and a truncated token_id for identification.
Restart your server using node server.js and navigate to your wallet app in the browser. When you click on the “Collectibles” tab, and if the wallet has NFTs, you should see the NFT collection displayed with rich visual metadata.

Conclusion

That concludes this three-part series! With just three API requests - Balances, Activity, and Collectibles - you’ve built a fully functional, multichain wallet that displays token balances, calculates portfolio value, tracks detailed transaction activity, and showcases NFT collections with rich visual displays. This project serves as a solid foundation for a wallet. You can now expand upon it by exploring other Sim API features. Whether you want to add more sophisticated analytics, deeper NFT insights, or advanced transaction tracking, Sim APIs provides the blockchain data you need to build the next generation of onchain apps.