Giveaway Verification API: Check Retweets, Replies, Follows
The Problem: Manual Verification Doesn't Scale
Running an airdrop, giveaway or NFT whitelist on X/Twitter? You've probably seen the drill:
Manually checking 5,000 retweets and 5,000 replies is impossible. Many projects resort to random selection (unfair) or trusting participants (exploitable). Bots and fake entries slip through.
XCROP's Interaction Check API solves this. Six dedicated endpoints let you programmatically verify every single participant (retweets, replies, quotes, follow relationships, and account-quality filters) in minutes.
The 6 Interaction Check Endpoints
All endpoints return a simple boolean result, no complex parsing needed.
> Note on likes: X removed public like visibility in June 2024, so like-checking is not offered. Use retweet, reply, and quote checks instead, plus the qualified-account checks above to filter bot entries by account age, follower count, or display-name requirements.
Quick Start: Verify a Single User
import requests import os API_KEY = os.environ["XCROP_API_KEY"] HEADERS = {"Authorization": "Bearer " + API_KEY} BASE = "https://xcrop.io/api/v2" TWEET_ID = "1893045621038174208" # Check retweet rt = requests.get( BASE + "/tweets/" + TWEET_ID + "/check-retweet", headers=HEADERS, params={"username": "alice_crypto"} ).json() print("Retweeted:", rt["data"]["retweeted"]) # Check reply with 3 friend tags required reply = requests.get( BASE + "/tweets/" + TWEET_ID + "/check-reply", headers=HEADERS, params={"username": "alice_crypto", "min_tags": 3} ).json() print("Replied:", reply["data"]["replied"]) print("Tags met:", reply["data"]["tags_met"]) # Check follow relationship follow = requests.get( BASE + "/users/check-follow", headers=HEADERS, params={"source": "alice_crypto", "target": "your_project"} ).json() print("Follows you:", follow["data"]["source_follows_target"])
Bulk Verification: Check All Participants
Here's a complete script to verify an entire airdrop campaign:
import requests import time import os import json API_KEY = os.environ["XCROP_API_KEY"] HEADERS = {"Authorization": "Bearer " + API_KEY} BASE = "https://xcrop.io/api/v2" TWEET_ID = "1893045621038174208" PROJECT_USERNAME = "your_project" MIN_TAGS = 3 # List of participants (from your entry collection system) participants = ["alice_crypto", "bob_defi", "charlie_nft"] # ... thousands more def verify_participant(username): """Check all conditions for a single participant.""" result = {"username": username, "passed": False} # 1. Check retweet rt = requests.get( BASE + "/tweets/" + TWEET_ID + "/check-retweet", headers=HEADERS, params={"username": username} ).json() result["retweeted"] = rt["data"]["retweeted"] # 2. Check reply with friend tags reply = requests.get( BASE + "/tweets/" + TWEET_ID + "/check-reply", headers=HEADERS, params={"username": username, "min_tags": MIN_TAGS} ).json() result["replied"] = reply["data"]["replied"] result["tags_met"] = reply["data"].get("tags_met", False) # 3. Check follow follow = requests.get( BASE + "/users/check-follow", headers=HEADERS, params={"source": username, "target": PROJECT_USERNAME} ).json() result["follows"] = follow["data"]["source_follows_target"] # All conditions met? result["passed"] = ( result["retweeted"] and result["replied"] and result["tags_met"] and result["follows"] ) return result # Verify all participants verified = [] for i, user in enumerate(participants): try: result = verify_participant(user) verified.append(result) status = "PASS" if result["passed"] else "FAIL" print("[" + str(i + 1) + "/" + str(len(participants)) + "] @" + user + ": " + status) except Exception as e: print("[" + str(i + 1) + "] @" + user + ": ERROR - " + str(e)) time.sleep(0.1) # Be gentle with rate limits # Results winners = [r for r in verified if r["passed"]] print("\nTotal checked:", len(verified)) print("Qualified:", len(winners)) print("Disqualified:", len(verified) - len(winners)) # Save results with open("airdrop_results.json", "w") as f: json.dump({"winners": winners, "all": verified}, f, indent=2)
Tag Verification: Prevent Fake Entries
A common giveaway rule is "Reply and tag 3 friends." The check-reply and check-quote endpoints support the min_tags parameter to verify this automatically:
# Require at least 3 friend tags in the reply response = requests.get( BASE + "/tweets/" + TWEET_ID + "/check-reply", headers=HEADERS, params={"username": "alice_crypto", "min_tags": 3} ).json() data = response["data"] print("Replied:", data["replied"]) print("Tags required:", data["tags_required"]) # 3 print("Tags found:", data["tags_found"]) # 4 print("Tags met:", data["tags_met"]) # True
The tag count excludes the original tweet author. So if your tweet is from @project and Alice replies "@project @bob @carol @dave", the tag count is 3 (bob, carol, dave), not 4.
Whitelist Verification at Submission Time
Checking everyone after the deadline suits a giveaway. NFT whitelists usually work the other way: a participant connects a wallet, enters an X handle and expects a pass or fail on the spot. The same endpoints cover that flow, called from your backend when the form is submitted:
// Run this in your form handler on the server. Calling XCROP from the browser would expose your API key. async function checkWhitelistTasks(handle) { const headers = { Authorization: "Bearer " + process.env.XCROP_API_KEY }; const base = "https://xcrop.io/api/v2"; const [follow, retweet, reply] = await Promise.all([ fetch(base + "/users/check-follow?source=" + handle + "&target=your_project", { headers }).then(r => r.json()), fetch(base + "/tweets/" + TWEET_ID + "/check-retweet?username=" + handle, { headers }).then(r => r.json()), fetch(base + "/tweets/" + TWEET_ID + "/check-reply?username=" + handle + "&min_tags=2", { headers }).then(r => r.json()), ]); // Report each task separately so the user knows what to fix. return { follows: follow.data.source_follows_target, retweeted: retweet.data.retweeted, replied: reply.data.verified, // replied AND met min_tags }; }
A few things matter once a real person is waiting on the answer:
JavaScript / Node.js Example
const API_KEY = process.env.XCROP_API_KEY; const BASE = "https://xcrop.io/api/v2"; const TWEET_ID = "1893045621038174208"; async function checkInteractions(username) { const headers = { Authorization: "Bearer " + API_KEY }; const [retweet, reply, relationship] = await Promise.all([ fetch(BASE + "/tweets/" + TWEET_ID + "/check-retweet?username=" + username, { headers }).then(r => r.json()), fetch(BASE + "/tweets/" + TWEET_ID + "/check-reply?username=" + username + "&min_tags=3", { headers }).then(r => r.json()), fetch(BASE + "/users/check-follow?source=" + username + "&target=your_project", { headers }).then(r => r.json()), ]); return { username, retweeted: retweet.data.retweeted, replied: reply.data.replied, tags_met: reply.data.tags_met, follows: relationship.data.source_follows_target, qualified: retweet.data.retweeted && reply.data.replied && reply.data.tags_met && relationship.data.source_follows_target, }; } // Verify participants in parallel (batches of 10) async function verifyAll(participants) { const results = []; for (let i = 0; i < participants.length; i += 10) { const batch = participants.slice(i, i + 10); const batchResults = await Promise.all(batch.map(checkInteractions)); results.push(...batchResults); console.log("Checked " + results.length + "/" + participants.length); } return results; }
Cost Estimation
Each interaction check costs 50 credits. Here's what a typical campaign costs:
That works out to about $0.25 per 1,000 checks on Pro (50,000 credits), or about $0.35 on Basic.
With the Pro plan ($9.9/month, includes 2M credits), you can verify ~13,000 users with all 3 checks included in your monthly allowance.
Pay-as-you-go credit packs start at $2.90 for 400K credits (~2,600 full verifications).
Best Practices
Promise.all (JS) or asyncio (Python) for 3 checks per user simultaneouslyFrequently Asked Questions
How do I check if a user retweeted a tweet with an API?
Call GET /v2/tweets/:tweetId/check-retweet with the username. It returns retweeted as true or false and costs 50 credits. Replies (check-reply), quotes (check-quote) and follows (check-follow) work the same way.
Can I use this to verify an NFT whitelist?
Yes. Call the checks from your backend when someone submits their X handle, tell them which task is still missing, and run the checks again before the final snapshot, since some people unfollow after they are approved.
How much does giveaway verification cost?
Each check costs 50 credits, about $0.25 per 1,000 checks on the Pro plan. Verifying 1,000 entrants on retweet, reply and follow is 150,000 credits, roughly $0.75.
Can the API check likes?
No. X made likes private in June 2024, so no API can verify them reliably. Use retweet, reply, quote and follow checks instead.
One API for X/Twitter data: profiles, tweets, followers, search and real-time streams. Start free with 5,000 credits/month, no card required.