Twitter (X) Advanced Search API — Operators, Code, Cost
Twitter (X) advanced search lets you query the firehose with operators — from:elonmusk Tesla since:2024-01-01 min_faves:100 lang:en — and it works programmatically the same way. This guide is the developer reference for running advanced search from code: which operators are supported, three practical API paths to call them, runnable code per path, and the per-call cost from each provider's public pricing page.
Most of the top-ranking advanced-search articles on Google are end-user tutorials for the x.com/search-advanced form. That's useful if you're a human typing a query. If you're a developer integrating advanced search into an analytics pipeline or a customer-facing product, you need API-level documentation — that's what this page is.
The operator set — what advanced search actually supports
Operators are the building blocks. They all combine with normal keyword terms and with each other; you can quote multi-word phrases and use boolean AND (implicit), OR, and - (negation).
User filters — from:username restricts to tweets posted by that user. to:username restricts to tweets directed at that user. @username (without operator) matches mentions anywhere in the text.
Date range — since:YYYY-MM-DD and until:YYYY-MM-DD bracket the time window. since:2024-01-01 until:2024-06-30 finds tweets from H1 2024.
Engagement minimums — min_faves:N, min_retweets:N, min_replies:N. Useful for filtering to high-signal content (e.g. min_faves:1000 to find tweets that actually broke through).
Content filters — filter:media (any embedded media), filter:images, filter:videos, filter:links (contains URLs), filter:replies (is a reply), filter:verified (from a verified account).
Language + region — lang:en / lang:ja / etc. Language detection is X's, not yours; for marginal cases (multilingual tweets, code blocks) results vary.
Boolean combinators — implicit AND between terms, explicit OR (uppercase), - for negation. Parentheses group: (Tesla OR SpaceX) -elon.
Phrase match — quoted strings: "machine learning" matches the literal phrase, not the individual words.
Path 1 — twitterapi.io /twitter/tweet/advanced_search
twitterapi.io exposes a REST endpoint at /twitter/tweet/advanced_search. Authentication is X-API-Key header; pricing follows twitterapi.io/pricing — search read calls follow the per-tweet rate model.
The query parameter is the full advanced-search expression (the same string you'd type into x.com/search-advanced).
import os, requests
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"
def advanced_search(query: str, cursor: str | None = None):
"""GET /twitter/tweet/advanced_search — pass the advanced-search expression as `query`."""
params = {"query": query}
if cursor:
params["cursor"] = cursor
r = requests.get(
f"{BASE}/twitter/tweet/advanced_search",
headers=HEADERS,
params=params,
timeout=15,
)
r.raise_for_status()
return r.json()
# Example: high-engagement Tesla mentions from a specific account
result = advanced_search("from:elonmusk Tesla since:2024-01-01 min_faves:1000 lang:en")
for t in result.get("tweets", [])[:5]:
print(t.get("id"), t.get("text", "")[:100])
# Paginate with the cursor returned in result
next_cursor = result.get("next_cursor")
if next_cursor:
result2 = advanced_search("from:elonmusk Tesla since:2024-01-01 min_faves:1000 lang:en", cursor=next_cursor)
Path 2 — X official via xdk / tweepy / raw requests
X's docs.x.com/en/docs/twitter-api/tweets/search/integrate/build-a-query is the canonical operator reference for the official surface. Tweepy wraps it; xdk wraps it. Pricing per docs.x.com/x-api/getting-started/pricing is $0.005 per post read — search calls bill at the per-post read rate.
Operator set is functionally the same as twitterapi.io. Auth is a bearer token issued from the X Developer Console.
Note: docs.x.com's recent search docs may use slightly different parameter names (max_results, tweet.fields) — verify against the live spec for the field set you want returned.
# pip install tweepy
import tweepy
client = tweepy.Client(bearer_token="YOUR_X_BEARER")
query = "from:elonmusk Tesla since:2024-01-01 min_faves:1000 lang:en"
# Paginated full-archive (or recent) search via Paginator
for page in tweepy.Paginator(
client.search_recent_tweets,
query=query,
max_results=100,
tweet_fields=["created_at", "public_metrics"],
limit=5,
):
for t in page.data or []:
print(t.id, t.text[:100])
# Raw requests equivalent (when you don't want tweepy as a dep):
# r = requests.get(
# "https://api.x.com/2/tweets/search/recent",
# headers={"Authorization": f"Bearer {X_BEARER}"},
# params={"query": query, "max_results": 100, "tweet.fields": "created_at,public_metrics"},
# )
Path 3 — x.com web form (no code, useful as a query-builder)
x.com/search-advanced renders a UI form for the same operators. You enter filters in dropdowns and inputs; the form generates a URL whose q= parameter is the advanced-search expression.
This is the fastest way to sanity-check a query before putting it into code. Build the query in the UI, copy the q= value from the URL, paste it into the query parameter of either API path above.
Side-by-side comparison — 3 paths, 5 dimensions
Same job (run an advanced-search query, paginate, parse results) across the three paths. Costs are derived from each provider's published pricing page.
Three honest patterns: (a) operator semantics are identical across all three — the X advanced-search grammar is the source of truth; (b) the cost ratio for batch read workloads is 33.33× per returned tweet ($0.005 / $0.00015 = 33.33), derivable from the two cited pricing pages; (c) most teams use the web form for query design + an API path for execution at scale.
Practical patterns — paginating, deduplicating, rate-limiting
Pagination: results larger than one page require following the cursor / next_token returned by the API. Loop until cursor is null or you've collected enough.
Deduplication: a tweet that matches your query may appear in multiple subsequent windowed runs. Track tweet IDs you've already seen and skip duplicates downstream.
Rate limits: both twitterapi.io and X publish rate limits in their respective docs. Wrap each call with retry-on-429 with jittered backoff. A 5xx response is similar.
Query craft: advanced search has limits on query length and complexity. Very long disjunctions (OR a OR b OR c OR ... with dozens of terms) get truncated or error. If you need very wide coverage, run multiple queries and union results client-side.
If you need the full contract for cursor, has_next_page and next_cursor, including what the last page looks like and how long the cursor gets, see our Twitter (X) API pagination guide.
Picking a path — the decision rule
Need to run advanced search at any meaningful volume on a per-call budget? → twitterapi.io. $0.00015 per returned tweet compounds favorably against X's $0.005 per post read (math from each provider's published rate).
Already on the X bill (you write posts, you read profiles via xdk / tweepy)? → use the X official search endpoint; auth is unified, marginal cost is small at low call counts.
One-off exploration or query design? → x.com/search-advanced UI. Free, immediate, and the URL q= is portable to either API path.
Production pipelines often combine these — design the query in the web form, port to twitterapi.io for batch execution, keep the X official path for write workflows that require X auth anyway.
# Practical example: collect all high-signal tweets matching a query, paginate, dedupe.
import os, requests
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"
def advanced_search(query: str, cursor: str | None = None):
params = {"query": query}
if cursor:
params["cursor"] = cursor
r = requests.get(
f"{BASE}/twitter/tweet/advanced_search",
headers=HEADERS, params=params, timeout=15,
)
r.raise_for_status()
return r.json()
def collect_all(query: str, max_pages: int = 20):
seen, rows = set(), []
cursor = None
for _ in range(max_pages):
resp = advanced_search(query, cursor=cursor)
for t in resp.get("tweets", []):
if t["id"] not in seen:
seen.add(t["id"])
rows.append(t)
cursor = resp.get("next_cursor")
if not cursor:
break
return rows
rows = collect_all('"vector database" min_faves:50 lang:en since:2024-01-01')
print(f"collected {len(rows)} unique tweets")
# Cost framing (math from cited pricing pages):
# This run hits the API once per page. At 20 pages × 20 tweets/page = 400 tweets:
# twitterapi.io: 400 tweets × $0.00015 = $0.06
# X official: 400 posts × $0.005 = $2.00
# Multiply by N runs per day for your monitoring workload.Questions readers ask
Does advanced search return the same results as the x.com UI?
Largely yes — the operator grammar is the same. Subtle differences can appear around language detection (lang:) for marginal tweets and around the exact ordering algorithm. For exact-result-parity in compliance-sensitive workloads, validate a sample programmatically against the UI for your specific query.
Is there a limit on min_faves / min_retweets values?
Operators accept large integer values. Practically, min_faves:100000 will narrow results dramatically; very high thresholds may return no results for niche queries. Start narrower and broaden, not the other way around.
How far back does advanced search reach?
Depth varies by provider. The X official search_recent_tweets covers a recent window (commonly ~7 days); search_all_tweets (where available) covers further back. twitterapi.io's advanced-search depth is documented on its endpoint reference. For historical archive work, plan around what each path supports.
Can I combine from: with to:?
Yes — from:userA to:userB finds tweets from userA directed at userB. Useful for reply-graph extraction. Combine with since: / until: for windowed views.
What's the difference between filter:replies and just searching for @username?
filter:replies returns tweets that ARE replies (any reply, any thread). @username matches the mention substring anywhere — first-of-thread tweets that happen to mention the user, retweets quoting them, etc. They overlap but aren't identical; pick by your actual intent.
Does cost per call depend on result count?
Models vary. twitterapi.io's per-tweet billing means results returned drive the cost — empty result sets cost only the 15-credit ($0.00015) per-request minimum, larger results are billed per returned tweet (twitterapi.io/pricing). X's $0.005-per-post-read model is per resource read; a search that returns N posts is N reads. Check the live pricing page for the exact metering rule before committing to a high-volume workload.
Continue
- X API — pricing (docs.x.com, 2026 verified)
- twitterapi.io — pricing
- X official — Build a query
- Tweepy documentation
- Twitter advanced search operators (community reference)
- Twitter (X) API — cluster hub
- Twitter (X) API in Python — complete guide
- Search a tweet — operator reference
- Tweet search by date — since/until guide
- twitterapi.io pricing
- Twitter (X) search by location: operators and API
- Twitter (X) API pagination guide
- 01How to Get a Grok API Key: Setup, Pricing and Live X Data for the xAI API
- 02The X Developer Portal (X Developer Console) for the Twitter (X) API: Sign-Up, Apps and Keys, Billing, Limits, and When You Do Not Need It
- 03SKILL.md explained: the Agent Skills format, X's own skill, and a Twitter (X) API skill you can install
- 04Twitter (X) Auto Post: Schedule in X, RSS or Sheets to X, and Posting from Code
- 05How to Search and Track Mentions on Twitter (X), Including Someone Else's
- 06Searching for a Tweet — Operators, Filters, and API
- 07How to Search Tweets by Date via API — A Developer's Guide
- 08Twitter (X) Search Filters — Programmatic API Guide
Stop reading. Start building.
Starter credits cover real testing on real data. Google sign-in, no card, no application queue.
Get an API key