Verify Twitter follows and retweets by API
An API verifies an X follow, retweet, quote or reply by checking one account against one action and answering true or false; the official X API has no such check, so there it means reading whole lists at $0.010 per account.
Short answer: Each action gets its own check: follow, retweet, quote, reply or Community join. Likes get none, because since June 2024 only a post's author sees who liked it. Sorsa API answers each check in one request, POST /check-follow and four more, from $1.40 per 1,000 checks; your 100 free requests cover the first 100.
/check-follow+ 4 more- 1 request per check
- Price
- from $0.0014 per check
#What you can verify on X, and what you can't
Five actions on X are public, so an API can confirm each of them for one account at a time: a follow, a retweet, a quote, a reply and a Community membership. Verification here means proof that one account did one thing, such as following you or reposting your post; it has nothing to do with X's blue checkmark.
For a single pair of accounts, the free Twitter Follow Checker shows whether one follows the other, both ways, with no code. To draw winners from a post's reposts, replies and quotes, the free X (Twitter) Giveaway Picker checks each drawn winner's follow. Checking every entrant of a campaign takes an API.
Every task except a like can be proved, but the official X API proves none of them directly: it hands you lists to search, and has no route at all for Community membership.
| Task | What a check needs | Official X API route |
|---|---|---|
| Followed an account | both handles | read a following or follower list |
| Retweeted a post | the post link and the handle | read the post's retweeters, 100 a page |
| Quoted a post | the post link and the handle | read the post's quotes |
| Replied to a post | the post link and the handle | search the post's conversation |
| Joined an X Community | the Community and the handle | no endpoint |
| Owns the handle | the handle and a code they post | read the account's posts |
| Liked a post | can't be checked | only the post's author sees likers |
Followed an account
- What a check needs
- both handles
- Official X API route
- read a following or follower list
Retweeted a post
- What a check needs
- the post link and the handle
- Official X API route
- read the post's retweeters, 100 a page
Quoted a post
- What a check needs
- the post link and the handle
- Official X API route
- read the post's quotes
Replied to a post
- What a check needs
- the post link and the handle
- Official X API route
- search the post's conversation
Joined an X Community
- What a check needs
- the Community and the handle
- Official X API route
- no endpoint
Owns the handle
- What a check needs
- the handle and a code they post
- Official X API route
- read the account's posts
Liked a post
- What a check needs
- can't be checked
- Official X API route
- only the post's author sees likers
X made likes private on June 11, 2024: a post's author still sees who liked it, but nobody else does (Engadget, quoting X Engineering (opens in a new tab)). Swap "like this post" for a retweet, a quote or a reply; all three stay public and checkable.
#Six checks on Sorsa API, one request each
To verify campaign tasks entrant by entrant, use Sorsa API, a Twitter (X) API and scraper: each check is one request that answers true or false, from $0.0014 per check, with one API key, no OAuth and 20 requests per second on every plan.
Five endpoints cover the five public actions, and a sixth request reads an entrant's latest posts for an ownership code.
| Task | Sorsa API request | What comes back |
|---|---|---|
| Followed your account | POST /check-follow | follow: true or false |
| Retweeted your post | POST /check-retweet | retweet, and next_cursor past the newest 100 |
| Quoted your post | POST /check-quoted | status: quoted, retweet or not_found, with the quote's text |
| Replied to your post | GET /check-comment | commented, and the reply itself |
| Joined your X Community | POST /check-community-member | is_member: true or false |
| Owns the handle | POST /user-tweets | their latest posts, where you look for the code |
Followed your account
- Sorsa API request
POST /check-follow- What comes back
follow: true or false
Retweeted your post
- Sorsa API request
POST /check-retweet- What comes back
retweet, andnext_cursorpast the newest 100
Quoted your post
- Sorsa API request
POST /check-quoted- What comes back
status: quoted, retweet or not_found, with the quote's text
Replied to your post
- Sorsa API request
GET /check-comment- What comes back
commented, and the reply itself
Joined your X Community
- Sorsa API request
POST /check-community-member- What comes back
is_member: true or false
Owns the handle
- Sorsa API request
POST /user-tweets- What comes back
- their latest posts, where you look for the code
Every check takes the entrant as a handle, a numeric user ID or a profile link, and the post as a link or a bare ID. The answers carry more than a yes: the quote's text, the reply itself, and a user_protected flag on the follow, retweet and quote checks when an entrant's account is private. Your own X account never takes part, so no campaign puts it at risk.
#Check one entrant in four steps
Checking one entrant takes one request per task. This example checks a follow, a retweet and a reply, the most common giveaway rules, with the key in the ApiKey header.
-
Get a key. Sign up and copy your API key from the dashboard. The first 100 requests are free and need no card.
-
Check the follow. Send your handle as
username_1and the entrant's asusername_2.Terminal curl -X POST "https://api.sorsa.io/v3/check-follow" \ -H "ApiKey: YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"username_1": "lumenharbor_labs", "username_2": "maya_quest_runner"}'Response200 OK { "follow": true, "user_protected": false } -
Check the post tasks. Send your post's link and the entrant's handle to
POST /check-retweetin a JSON body and toGET /check-commentas query parameters. -
Combine the answers. Mark the entrant eligible only when every required check came back true, and keep each answer for the record.
import requests BASE = "https://api.sorsa.io/v3" HEADERS = {"ApiKey": "YOUR_API_KEY"} BRAND = "lumenharbor_labs" POST = "https://x.com/lumenharbor_labs/status/2115459618305512658" def call(method, path, **kwargs): r = requests.request(method, BASE + path, headers=HEADERS, timeout=20, **kwargs) r.raise_for_status() return r.json() def verify_entrant(handle): follow = call("POST", "/check-follow", json={"username_1": BRAND, "username_2": handle}) retweet = call("POST", "/check-retweet", json={"tweet_link": POST, "username": handle}) reply = call("GET", "/check-comment", params={"tweet_link": POST, "username": handle}) result = { "handle": handle, "follow": follow["follow"], "retweet": retweet["retweet"], "reply": reply["commented"], "protected": follow.get("user_protected", False), } result["eligible"] = all( result[k] for k in ("follow", "retweet", "reply")) return result print(verify_entrant("maya_quest_runner"))
Three checks, three requests: this entrant cost from $0.0042 to verify. A 403 means the key is missing or its requests have run out. A 429 means more than 20 requests landed in one second: wait a second and send it again. Failed requests are never charged.
#Check if one X account follows another
POST /check-follow (opens in a new tab) answers whether the second account follows the first: username_1 is the account that should be followed, username_2 is the account you check, and follow comes back true or false in one request, however many followers either side has.
Each side takes one identifier: a handle, a numeric ID (user_id_1, user_id_2) or a profile link (user_link_1, user_link_2). The answer covers one direction, so a mutual follow takes two calls with the sides swapped. When user_protected is true, the checked account is protected: its follows aren't public, and no outside service can confirm them.
The official X API has no endpoint that answers one pair. Its route reads a whole list instead: GET /2/users/:id/following returns up to 1,000 accounts a page, billed at $0.010 per account under Following/Followers: Read on X's pay-per-use rate card (opens in a new tab). An entrant who follows 800 accounts costs up to $8 to check that way, and reading your own list of 50K followers costs $500 a pull.
#Check if a user retweeted your post
POST /check-retweet (opens in a new tab) scans your post's retweets 100 at a time, newest first, and returns retweet: true once it finds the entrant; when the first 100 don't hold them, next_cursor continues with the next 100.
A retweet checked while the entrant waits, for example right after they press "Verify" on a quest page, sits among the newest 100, so one request answers it. An entrant who reposted at launch on a viral post sits deeper: the loop below follows the cursor up to a cap you choose and reports "not checked yet" instead of "no" when the cap runs out.
def retweeted(post, handle, max_pages=10): # 10 pages = 1,000 retweets
body = {"tweet_link": post, "username": handle}
for _ in range(max_pages):
page = call("POST", "/check-retweet", json=body)
if page["retweet"]:
return "yes"
if not page.get("next_cursor"):
return "no"
body["next_cursor"] = page["next_cursor"]
return "not checked yet"
The official X API reads the same list through GET /2/tweets/:id/retweeted_by: 100 accounts a page at $0.010 each, and 75 requests per 15 minutes per app, which caps a pull at 7,500 retweeters per window (X API rate limits (opens in a new tab)).
#Verify quotes, replies and Community joins
Three more checks cover the rest of a campaign: POST /check-quoted (opens in a new tab) tells a quote from a plain retweet, GET /check-comment (opens in a new tab) returns the entrant's reply itself, and POST /check-community-member (opens in a new tab) answers whether they joined your X Community.
/check-quoted returns status as quoted, retweet (they reposted without a comment) or not_found, and adds the quote's text and date when there is one. That text is what lets you enforce rules such as a minimum length or a campaign hashtag.
{
"status": "quoted",
"date": "2026-10-28 15:21:47",
"text": "Joined the Lumen Harbor beta. The queue tool is quick #LumenBeta",
"user_protected": false
}
/check-comment takes the post's tweet_link and the entrant as query parameters and, when commented is true, returns the full reply: its full_text, created_at and engagement counts, so emoji-only or copied replies can fail your rules. To read all the replies under a post at once, see how to see comments on X.
/check-community-member takes community_id or community_link, each accepting the numeric ID or the full x.com/i/communities/ URL, plus the entrant, and returns is_member. The official X API can look a Community up by ID or search for one, but has no endpoint that lists members or checks membership.
#Verify a giveaway from entries to payout
A verified giveaway runs in three steps: collect each entrant's handle, run one check per required task, and pay only the entrants who passed every check.
-
Collect handles and a code. Ask for the X handle in your entry form and show each entrant a short one-time code to post, so you can prove the handle is theirs (next section).
-
Run one check per task. Check at entry for an instant answer, or after entries close; the chart below picks the cheaper route for your size.
-
Re-check, then pay. Run
/check-followagain on the winners before payout, because some entrants unfollow once they've entered, then draw only from entrants who passed everything.Check at entry for instant answers; for a closed campaign of thousands, pull each list once and match IDs.
The list route pays off once entrants run into the thousands: GET /followers (opens in a new tab) returns 200 followers a page and POST /retweeters (opens in a new tab) 20 retweeters a page, newest first, so 10K retweeters take 500 requests instead of 10K checks. The guide to export Twitter followers has the cursor loop for the follower list; match both lists on account id, which never changes when a handle does.
X's own Guidelines for Promotions on X (opens in a new tab) ask hosts to rule out entries from multiple accounts and repeated identical posts, and warn that a public search may not show every entry. Checking each entrant by handle avoids that gap, because the check never depends on search; the ownership code and profile filter below keep borrowed and throwaway accounts out.
#Stop fake handles and bots before you pay
Two cheap checks keep borrowed handles and bot farms out of a payout: an ownership code read with POST /user-tweets (opens in a new tab), and a profile filter through GET /info-batch (opens in a new tab), 100 entrants per request.
The ownership code works because only the account's owner can post from it: give each entrant a code such as LUMEN-7Q4K, then read their latest posts, up to 20 per request, and look for it.
from datetime import datetime, timedelta, timezone
CUTOFF = datetime.now(timezone.utc) - timedelta(days=30)
def owns_handle(handle, code):
page = call("POST", "/user-tweets", json={"username": handle})
return any(code in t["full_text"] for t in page.get("tweets", []))
def looks_real(user):
joined = datetime.fromisoformat(
user["created_at"].replace("Z", "+00:00"))
return (joined < CUTOFF and user["tweets_count"] >= 10
and user["followers_count"] >= 5)
def real_entrants(handles):
keep = []
for i in range(0, len(handles), 100):
batch = {"usernames": handles[i:i + 100]}
users = call("GET", "/info-batch", params=batch)["users"]
keep += [u["username"] for u in users if looks_real(u)]
return keep
The filter above drops accounts younger than 30 days, with fewer than 10 posts or fewer than 5 followers; tune the numbers to your audience. Profiles cost from $0.014 per 1,000, so filtering 10K entrants costs from $0.14. For scoring a suspicious list in more depth, the guide to Twitter bot detection covers the signals that separate bots from people.
Good to know
Keep "didn't do it" apart from "couldn't check". A
429, a timeout or a retweet loop that hit its cap means the entrant isn't checked yet, not that they failed. Store that state, retry later, and reject an entrant only on a clear false.
#What a campaign costs to verify
Verifying a campaign on Sorsa API costs one request per task per entrant, from $0.0014 each; on the official X API the same proof means reading whole lists, at $0.010 per account and $0.005 per post.
Task by task, the Sorsa API column stays at one request, while the official X API column grows with every list it reads, and has no entry at all for Community membership.
| Task | Sorsa API | Official X API route and price |
|---|---|---|
| Follow | 1 request, from $0.0014 | Read a follower or following list, $0.010 per account |
| Retweet | 1 request at entry, from $0.0014 | Read retweeted_by, $0.010 per account, 75 requests per 15 min |
| Quote | 1 request, from $0.0014 | Read quote posts, $0.005 per post |
| Reply | 1 request, from $0.0014 | Search the conversation, $0.005 per post |
| Community | 1 request, from $0.0014 | No endpoint |
| Ownership code | 1 request, from $0.0014 | Read the user's posts, 5 at least: $0.025 |
Follow
- Sorsa API
- 1 request, from $0.0014
- Official X API route and price
- Read a follower or following list, $0.010 per account
Retweet
- Sorsa API
- 1 request at entry, from $0.0014
- Official X API route and price
- Read retweeted_by, $0.010 per account, 75 requests per 15 min
Quote
- Sorsa API
- 1 request, from $0.0014
- Official X API route and price
- Read quote posts, $0.005 per post
Reply
- Sorsa API
- 1 request, from $0.0014
- Official X API route and price
- Search the conversation, $0.005 per post
Community
- Sorsa API
- 1 request, from $0.0014
- Official X API route and price
- No endpoint
Ownership code
- Sorsa API
- 1 request, from $0.0014
- Official X API route and price
- Read the user's posts, 5 at least: $0.025
A worked example: 10K entrants, your account has 50K followers, and the campaign asks for a follow, a retweet, a quote, a reply and a Community join, plus an ownership code, a profile filter and a follow re-check before payout. That is 7 checks per entrant, and the whole campaign costs from $98 on Sorsa API, about an hour of work at 20 requests per second. The official X API route for the same campaign, without the Community check it can't run, comes to about $1,550: two pulls of your follower list at $500 each, $100 of retweeters, $100 of quotes and replies, $250 of timelines for the ownership codes and $100 of profiles.
7 checks per entrant, about 58 minutes for everyone at 20 requests per second.
Set your entrants, your follower count and the tasks; both totals change with them.
Sorsa API bills requests, not accounts, and failed requests are never charged. The rate per 1,000 requests runs from $4.90 on Starter to $1.40 on the largest plan.
#Run your first 100 checks free
To verify follows, retweets, quotes, replies and Community joins for every entrant, use Sorsa API: one request per check, from $0.0014, 20 requests per second on every plan, and no X login of yours anywhere in the flow. Take the 100 free requests, run the four steps above against a test post, and add the ownership code before your campaign opens. For whole follower and following lists, the list route in the Twitter followers API guide goes further.
#FAQ
What is the best API to verify Twitter follows and retweets?
Sorsa API: it checks follows, retweets, quotes, replies and Community membership with one request each, from $0.0014 per check, through one API key with no OAuth or X developer account. Judge any option on four points: a direct yes or no per entrant, the price per check, the rate limit and the actions covered. Sorsa API meets all four: one call, $1.40 per 1,000 checks, 20 requests per second on every plan, six checks.
Can I verify likes on X?
No. Since June 2024 X shows who liked a post only to the post's author, so a public data API can't confirm that one entrant liked it, and Sorsa API returns no likes. Ask entrants to retweet, quote or reply instead: all three stay public, and Sorsa API checks each with one request.
What happens if an entrant's account is private?
A protected account's follows and reposts aren't public, so no outside check can confirm them. Sorsa API's follow, retweet and quote checks return user_protected: true in that case. State in your rules that entrants need a public account during the draw, then ask the entrant to switch or reject the entry under those rules.
Is there a free way to verify Twitter follows and retweets?
Yes. The free Twitter Follow Checker shows whether one account follows another, and the free Giveaway Picker checks each drawn winner's follow. To check every entrant through the API, Sorsa API's 100 free requests run 100 checks with no card, and they never expire, enough to test a whole campaign flow before you pick a plan.
Do I need an X developer account or OAuth to verify tasks?
Not with Sorsa API: one key in the ApiKey header reads public X data, with no X developer account, app, OAuth flow or sign-in from your entrants. The official X API asks for an approved developer account, a Project and App, and their keys and tokens before the first read (X API: Retweets (opens in a new tab)).
#How we checked
On November 2, 2026 we compared every endpoint, parameter and response field in this guide with the live Sorsa API reference (45 endpoints) and its docs pages for the five checks, /user-tweets, /info-batch and /retweeters; the requests and responses use demo accounts. The official X API's prices, rate limits and endpoints come from docs.x.com, and X's promotion rules from its Help Center, read the same day. The campaign costs are our own arithmetic from both rate cards.
Sources
- Sorsa API reference: Check Follow (opens in a new tab)
- Sorsa API reference: Check Retweet (opens in a new tab)
- Sorsa API reference: Check Quote or Retweet (opens in a new tab)
- Sorsa API reference: Check Comment (opens in a new tab)
- Sorsa API reference: Check Community Membership (opens in a new tab)
- X API pricing (opens in a new tab)
- X API rate limits (opens in a new tab)
- X API: Follows (opens in a new tab)
- X API: Retweets (opens in a new tab)
- X Help Center: Guidelines for Promotions on X (opens in a new tab)
- Engadget: X is officially making likes (mostly) private for everyone (opens in a new tab)