The NFT market is inefficient in ways that are uniquely favorable to automated agents. Unlike fungible token markets where arbitrage closes within milliseconds, NFT price discrepancies can persist for minutes or hours — enough time for an agent scanning all major marketplaces to identify underpriced items and execute buys before the market corrects. A rare trait combination listed at floor price because a human seller doesn't know its rarity rank, a new collection listing that drops during low-traffic hours, an identical NFT listed 0.3 ETH below its peers on a less-trafficked marketplace — these are the persistent inefficiencies that a well-built NFT trading agent can systematically exploit.
This guide covers how to build such an agent using the Purple Flea Wallet API to hold and manage your NFT inventory, and Purple Flea Escrow for trustless OTC deals when trading large positions directly with other agents.
Floor Price Monitoring: The Real-Time Signal
Floor price — the cheapest listed item in a collection — is the most fundamental signal in NFT trading. It tells you the minimum cost of entry into any collection at any given moment. An agent that monitors floor prices across all major marketplaces simultaneously can act on two categories of opportunity:
Cross-marketplace arbitrage
The same NFT collection is listed on OpenSea, Blur, LooksRare, Magic Eden, and Reservoir simultaneously. The floor price on each platform is often slightly different due to varying user bases, listing fees, and trader populations. When the floor on Blur is 1.8 ETH and the same collection's floor on OpenSea is 2.1 ETH, an agent can buy from Blur and immediately list on OpenSea with a realistic expectation of selling at the spread. The practical edge is usually 2-8% on mid-size collections, reduced by marketplace fees (typically 0.5-2.5%).
Floor dip buying
Collections experience temporary floor drops during panic selling, market-wide corrections, or off-hours low-liquidity windows. An agent with historical floor data can compute a rolling 7-day average floor and trigger buy orders when the current floor falls more than 15% below the average — a signal that the dip is temporary rather than a fundamental repricing. Setting a maximum position size and hard stop-loss protects against buying into a permanent decline.
Data sources for floor price: Reservoir API aggregates floor prices from all major EVM marketplaces in real time. For Solana, Magic Eden's API provides similar aggregated floor data. The Purple Flea Wallet API normalizes both into a unified format so your agent code doesn't need separate integrations per chain.
Rarity Scoring: Finding Undervalued Items
Not all floor items are equal. A floor-priced item with extremely common traits is priced fairly. An item listed at floor with a rare trait combination that the seller failed to notice is dramatically underpriced. Computing rarity scores for every item in a collection and comparing them against listing prices is one of the most reliable NFT alpha strategies.
Statistical Rarity Method
For each trait in a collection, calculate the trait rarity as 1 / (trait_count / collection_size). Sum these values across all traits in a specific token to get its rarity score. Higher scores indicate rarer items. Normalize by dividing by the maximum rarity score in the collection to get a 0-1 rarity rank.
Typical rarity premium distribution for a 10,000-item PFP collection:
The alpha lies in buying items listed at floor (or near it) that actually belong to the Rare or Epic tier by rarity score. This happens when sellers use automated floor-pricing tools that don't account for rarity, or when new holders don't realize what traits their token has.
JavaScript: NFT Price Feed and Rarity Scanner
The following JavaScript module polls Reservoir for new listings in a target collection, computes rarity scores, and flags any items listed below their rarity-adjusted fair value.
import fetch from 'node-fetch' const RESERVOIR_API = 'https://api.reservoir.tools' const RESERVOIR_KEY = 'your_reservoir_key' const PURPLE_FLEA_WALLET_API = 'https://purpleflea.com/wallet-api' const PF_API_KEY = 'your_pf_api_key' // Step 1: Build rarity map for a collection async function buildRarityMap(collectionId) { let allTokens = [] let continuation = null do { const url = new URL(`${RESERVOIR_API}/tokens/v6`) url.searchParams.set('collection', collectionId) url.searchParams.set('limit', '1000') url.searchParams.set('includeAttributes', 'true') if (continuation) url.searchParams.set('continuation', continuation) const res = await fetch(url, { headers: { 'x-api-key': RESERVOIR_KEY } }) const data = await res.json() allTokens = [...allTokens, ...data.tokens] continuation = data.continuation } while (continuation) // Count occurrences of each trait value const traitCounts = {} const total = allTokens.length for (const token of allTokens) { for (const attr of (token.token.attributes || [])) { const key = `${attr.key}:${attr.value}` traitCounts[key] = (traitCounts[key] || 0) + 1 } } // Compute rarity score per token const rarityMap = {} for (const token of allTokens) { let score = 0 for (const attr of (token.token.attributes || [])) { const key = `${attr.key}:${attr.value}` score += 1 / (traitCounts[key] / total) } rarityMap[token.token.tokenId] = score } return rarityMap } // Step 2: Fetch active listings and score them async function findUndervaluedListings(collectionId, rarityMap, floorPriceEth) { const res = await fetch( `${RESERVOIR_API}/orders/asks/v5?collection=${collectionId}&sortBy=price&limit=200`, { headers: { 'x-api-key': RESERVOIR_KEY } } ) const { orders } = await res.json() const rarityScores = Object.values(rarityMap) const maxRarity = Math.max(...rarityScores) const avgRarity = rarityScores.reduce((a, b) => a + b, 0) / rarityScores.length const opportunities = [] for (const order of orders) { const tokenId = order.tokenSetId.split(':')[2] const rarityScore = rarityMap[tokenId] || avgRarity const rarityRank = rarityScore / maxRarity // 0-1, higher = rarer // Expected premium based on rarity rank let expectedPremium = 1.0 if (rarityRank > 0.95) expectedPremium = 8.0 // legendary else if (rarityRank > 0.80) expectedPremium = 3.0 // epic else if (rarityRank > 0.60) expectedPremium = 1.5 // rare const fairValueEth = floorPriceEth * expectedPremium const listingPriceEth = order.price.amount.native const discount = (fairValueEth - listingPriceEth) / fairValueEth if (discount > 0.20) { // at least 20% undervalued opportunities.push({ tokenId, listingPriceEth, fairValueEth, discount: (discount * 100).toFixed(1) + '%', rarityRank: (rarityRank * 100).toFixed(1) + 'th pct', marketplace: order.source?.name }) } } return opportunities.sort((a, b) => b.discount - a.discount) } // Step 3: Execute buy via Purple Flea Wallet API async function executeBuy(order, agentWallet) { const res = await fetch(`${PURPLE_FLEA_WALLET_API}/v1/nft/buy`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-API-Key': PF_API_KEY }, body: JSON.stringify({ wallet: agentWallet, marketplace: order.marketplace, token_id: order.tokenId, max_price_eth: order.listingPriceEth * 1.01, // 1% slippage gas_priority: 'fast' }) }) return res.json() } // Main agent loop async function runNFTScanner(collectionId, agentWallet, maxSpendEth = 5) { console.log(`Building rarity map for ${collectionId}...`) const rarityMap = await buildRarityMap(collectionId) setInterval(async () => { const floorRes = await fetch( `${RESERVOIR_API}/collections/v6?id=${collectionId}`, { headers: { 'x-api-key': RESERVOIR_KEY } } ) const { collections } = await floorRes.json() const floorPrice = collections[0]?.floorAsk?.price?.amount?.native || 0 const opportunities = await findUndervaluedListings(collectionId, rarityMap, floorPrice) console.log(`Found ${opportunities.length} undervalued listings`) for (const opp of opportunities.slice(0, 3)) { if (opp.listingPriceEth <= maxSpendEth) { console.log(`Buying token ${opp.tokenId} at ${opp.listingPriceEth} ETH (${opp.discount} discount)`) const result = await executeBuy(opp, agentWallet) console.log(`Buy result: ${result.status}`) } } }, 5000) // scan every 5 seconds } runNFTScanner('0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d', '0xYourWallet')
Wash Trade Detection: Protecting Your Agent
Wash trading — where the same entity buys and sells a token between wallets they control to inflate apparent trading volume and price history — is endemic in NFT markets. An agent that uses raw volume or price history without wash trade filtering will be misled into buying NFTs with fabricated demand.
Key wash trade detection signals:
- Buyer-seller overlap: If the buying wallet and selling wallet received funds from the same source within the last 30 days, flag as potential wash trade. On-chain graph analysis makes this detectable with high confidence.
- Same-block or low-latency round-trips: A token that was bought and resold within the same block, or within a minute, is almost certainly a wash trade. No legitimate buyer would resell that fast.
- Unusual volume-to-holder ratio: If a collection has 500 unique holders but 10,000 transactions in the last 7 days, the volume is likely fabricated. Compute transactions-per-holder and flag outliers.
- Price disconnected from comparable collections: If a collection's floor is 5x higher than comparable projects with similar holder counts and social metrics, price discovery may be manipulated.
Marketplace LooksRare Historical Note: In 2022, over 95% of LooksRare's volume was identified as wash trading for LOOKS token rewards. Any agent using LooksRare volume data without wash trade filtering would have purchased into collections with entirely synthetic demand. Always verify on-chain buyer diversity before entering a position based on volume signals.
NFT Trade Execution via Purple Flea Wallet API
Once your agent identifies a buy opportunity, execution speed is critical. The Purple Flea Wallet API provides a unified interface for buying and listing NFTs across all major marketplaces from a single API call, with automatic gas optimization and slippage protection.
| Operation | Endpoint | Avg Latency | Notes |
|---|---|---|---|
| Buy NFT | POST /v1/nft/buy | ~2s | Executes on specified marketplace |
| List NFT | POST /v1/nft/list | ~1s | Cross-post to multiple markets |
| Batch offer | POST /v1/nft/offer | ~3s | Floor sweep with single call |
| Portfolio value | GET /v1/nft/portfolio | <200ms | Real-time valuation by floor |
| Transfer NFT | POST /v1/nft/transfer | ~2s | For OTC escrow deals |
OTC Deals with Purple Flea Escrow
For high-value OTC trades between agents — exchanging a rare NFT for a large USDC amount, or swapping two collections — neither party wants to send first. The trustless solution is Purple Flea Escrow.
The workflow is simple: Agent A deposits their NFT into the escrow contract, specifying the price (in USDC) they'll accept. Agent B reviews the terms and deposits the USDC. The escrow contract atomically releases the NFT to Agent B and the USDC to Agent A. Neither party can exit with both assets. The 1% escrow fee on the USDC value is significantly cheaper than the marketplace royalty + listing fee structure on OpenSea (2.5% platform + up to 10% royalty = potentially 12.5% total).
Referral opportunity: If your agent introduces another agent to the Purple Flea Escrow system, you earn 15% of all escrow fees generated by that agent indefinitely. For a referred agent that does $100,000 USDC in NFT OTC trades per month, that is $150/month in passive referral income from a single referral.
Python: Rarity Arbitrage with Trade Execution
import asyncio import httpx from collections import Counter from dataclasses import dataclass, field from typing import Dict, List PURPLE_FLEA = "https://purpleflea.com/wallet-api" RESERVOIR = "https://api.reservoir.tools" @dataclass class NFTOpportunity: token_id: str collection: str listing_price_eth: float rarity_percentile: float estimated_fair_value_eth: float expected_profit_eth: float marketplace: str wash_trade_flag: bool = False attributes: Dict = field(default_factory=dict) def compute_rarity_scores(tokens: List[dict]) -> Dict[str, float]: """Statistical rarity scoring across all tokens in collection.""" total = len(tokens) trait_counts = Counter() for t in tokens: for attr in t.get("attributes", []): trait_counts[f"{attr['key']}:{attr['value']}"] += 1 scores = {} for t in tokens: score = 0.0 for attr in t.get("attributes", []): key = f"{attr['key']}:{attr['value']}" score += 1 / (trait_counts[key] / total) scores[t["tokenId"]] = score return scores def detect_wash_trades(sales_history: List[dict]) -> bool: """Flag if recent sales show wash trade patterns.""" buyers = [s["buyer"] for s in sales_history] sellers = [s["seller"] for s in sales_history] # Check for buyer-seller overlap within the same token's history buyer_set = set(buyers) seller_set = set(sellers) overlap = buyer_set & seller_set # Check for rapid round-trips (same token sold within 60 seconds) rapid_resales = 0 for i in range(len(sales_history) - 1): time_delta = sales_history[i+1]["timestamp"] - sales_history[i]["timestamp"] if time_delta < 60: rapid_resales += 1 return len(overlap) > 0 or rapid_resales > 2 def estimate_fair_value( rarity_rank: float, floor_price_eth: float, comparable_sales: List[float] ) -> float: """Estimate fair value using rarity premium model.""" if rarity_rank > 0.95: premium = 10.0 elif rarity_rank > 0.85: premium = 4.0 elif rarity_rank > 0.70: premium = 2.0 elif rarity_rank > 0.50: premium = 1.3 else: premium = 1.0 model_value = floor_price_eth * premium # Blend with comparable recent sales if available if comparable_sales: avg_comps = sum(comparable_sales) / len(comparable_sales) model_value = (model_value * 0.6) + (avg_comps * 0.4) return model_value async def scan_and_report(collection_id: str, api_key: str): async with httpx.AsyncClient() as client: # Get floor price col_res = await client.get( f"{RESERVOIR}/collections/v6", params={"id": collection_id}, headers={"x-api-key": api_key} ) floor = col_res.json()["collections"][0]["floorAsk"]["price"]["amount"]["native"] # Get listings listings_res = await client.get( f"{RESERVOIR}/orders/asks/v5", params={"collection": collection_id, "sortBy": "price", "limit": 100}, headers={"x-api-key": api_key} ) listings = listings_res.json()["orders"] print(f"Floor: {floor:.4f} ETH | Scanning {len(listings)} listings") for listing in listings[:5]: price = listing["price"]["amount"]["native"] print(f" Token {listing['tokenSetId']}: {price:.4f} ETH") asyncio.run(scan_and_report("0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d", "your_key"))
Position Management: When to Hold vs. Flip
Buying at a discount is only half the strategy. The agent also needs to decide when to exit. Two dominant approaches:
Floor listing strategy
List immediately above floor at the rarity-adjusted fair value. This captures the spread between purchase price and fair value quickly, though it may take time to find a buyer who understands rarity premiums. For highly liquid collections (top 10 by volume), expect fill times of 24-72 hours for properly priced rare items.
Appreciation holding strategy
Hold the position and wait for broader floor appreciation. If you bought during a floor dip with high conviction in the collection, the entire position appreciates as the floor recovers. The risk is that the dip was not temporary — implement a hard stop-loss at 30% below your purchase price to avoid catastrophic drawdowns.
Start NFT Trading with Purple Flea
Use the Wallet API to hold and trade NFTs, Escrow for trustless OTC deals, and get free USDC from the Faucet to bootstrap your first position.