Twitter (X) Search Filters — Programmatic API Guide
Search filtering is the single biggest cost + signal lever in any programmatic Twitter (X) workflow. A broad query on a popular topic can return millions of results; the same query with well-placed filters returns a signal-only stream you can actually process at reasonable cost.
This guide walks the operator grammar, priority-ordered by which filters give the biggest volume-cut per addition, with runnable Python + a cost-math table that shows what each filter saves you.
The 4 operator classes
Engagement threshold (biggest cost lever): min_faves:N, min_retweets:N, min_replies:N. Any positive N cuts the result set by 90%+ on typical queries — most tweets get zero engagement. Start with min_faves:5 for signal-only pulls.
Content class: lang:xx (ISO code — en, es, ja, etc.), filter:media / filter:links / filter:verified / filter:replies, -filter:retweets (exclude retweets — very useful).
Author scope: from:@handle, to:@handle, list:, @mentioned_handle.
Date + phrase: since:YYYY-MM-DD, until:YYYY-MM-DD, "exact multi-word phrase", -term_to_exclude, (term1 OR term2).
Combine any of these space-separated inside the query string. Operators are additive (all conditions must match).
Runnable — filter every layer
One end-to-end query showing each operator class in action:
import os, requests
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"
def search(query: str, max_pages: int = 10):
tweets, cursor = [], None
for _ in range(max_pages):
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()
resp = r.json()
tweets.extend(resp.get("tweets", []))
cursor = resp.get("next_cursor")
if not cursor: break
return tweets
# Broad → narrow, showing volume decay at each filter
queries = [
'openai', # baseline
'openai lang:en', # + language
'openai lang:en min_faves:10', # + engagement
'openai lang:en min_faves:10 -filter:retweets', # - retweets
'openai lang:en min_faves:10 -filter:retweets since:2024-01-01 until:2024-02-01', # + date window
]
for q in queries:
n = len(search(q, max_pages=2))
print(f" {n:>5} {q}")
# Typical output shows 10-100x volume reduction per added filterCost-saving math per filter
Every filter cuts returned volume → cuts your bill. Below is a rough magnitude table for a common baseline query, based on typical result-decay observed on our platform.
Practical rule: for any research-grade pull, always add lang: + min_faves: before running — it usually saves 95%+ vs the raw query.
Common filter patterns
Signal-only sentiment pull: — high-engagement English original posts, ideal for sentiment / opinion analysis.
Cashtag longitudinal: $TICKER lang:en since:2024-01-01 until:2024-12-31 min_faves:5 — one-year window of retail investor mentions above noise floor.
Author-specific archive: from:@handle since:2020-01-01 until:2024-12-31 — full historical posts by a target account.
Media-only pull: — tweets with images/video, deduplicated.
Deliberate exclusion: — brand monitoring with common noise removed.
Verified-only: — narrower signal from verified accounts (analyst / journalist tier).
Alternative — X official /2/tweets/search/recent
Same operator grammar. /2/tweets/search/recent (Basic tier) or /2/tweets/search/all (Full-Archive Enterprise) per docs.x.com/x-api/getting-started.
Pricing: $0.005 per post per docs.x.com/x-api/getting-started/pricing on the Basic tier for recent-search; Full-Archive is Enterprise contract-priced.
Wins when: already on X Developer Console for other workloads. Loses when: cost-sensitive and only need read access — 33× cost delta vs twitterapi.io per direct pricing comparison.
Comparison — 3 filter-capable paths
Debug tips — what to do when 0 results
Over-filtered: too many operators — remove the most restrictive one (usually min_faves if high, or the date window).
Typo in operator: min_favorites: (wrong) vs min_faves: (right) — X's grammar is strict. Copy from a working example.
Language mismatch: lang: uses ISO 639-1 (en, not english; zh, not chinese).
Date-window all in past + tier gate: if your since: is >7 days ago on X official Basic, the tier gates you out. Use twitterapi.io for historical windows.
# Practical example: signal-only brand monitoring with layered filters.
import os, requests, json
from pathlib import Path
from collections import Counter
HEADERS = {"X-API-Key": os.environ["TWITTERAPI_IO_KEY"]}
BASE = "https://api.twitterapi.io"
BRAND = "stripe"
COMPETITORS = ["paypal", "square", "adyen"]
EXCLUDES = " ".join(f"-{c}" for c in COMPETITORS)
# Layered filter: brand + no-competitor + engaged + English + no-retweets
query = f"{BRAND} {EXCLUDES} lang:en min_faves:5 -filter:retweets"
tweets, cursor = [], None
for _ in range(10):
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()
resp = r.json()
tweets.extend(resp.get("tweets", []))
cursor = resp.get("next_cursor")
if not cursor: break
print(f"signal-only pull: {len(tweets):,} tweets")
# Sentiment proxy: engagement distribution
faves = Counter()
for t in tweets:
n = t.get("favorite_count", 0)
bucket = "low(5-20)" if n < 20 else "mid(20-100)" if n < 100 else "high(100+)"
faves[bucket] += 1
for bucket, count in faves.most_common():
print(f" {bucket}: {count}")
# Cost per twitterapi.io/pricing:
# len(tweets) × $0.00015
# Same query WITHOUT filters would return ~50x more tweets → ~50x costQuestions readers ask
Does the operator grammar work identically on twitterapi.io and X official?
Yes — twitterapi.io uses X's native search grammar directly. Any operator that works in X's advanced search UI works in the API query parameter (min_faves, since, until, lang, from, filter:, exclusions with -term, exact phrases with quotes, OR groupings).
Can I combine 10+ filters in one query?
Yes. Query strings can be very long (up to a few KB before X's own parser starts complaining). Practical max is ~15-20 operators combined. Over-filtered queries often return 0 results — remove the most restrictive filter and re-run to debug.
What's the biggest cost-saver filter?
min_faves:N almost always. Most tweets on any topic get zero engagement — adding even min_faves:5 typically cuts 90%+ of results. For signal-only analysis workflows this is the default first filter.
How do I filter out spam and bot accounts?
There's no explicit filter:spam operator. Post-process approach: pull with min_faves:5 + -filter:retweets (already cuts most spam), then filter by author followers_count > 100 in your downstream code.
Does `filter:verified` still work post-Twitter Blue?
Yes but semantics changed — filter:verified now includes anyone with a blue checkmark (paid subscription). For legacy-verified only, use follower-count post-filter or check the verified_type: 'blue' field in the response.
Can I use regex-style filters?
No — the search grammar is strict operator+value pairs. For regex-style matching (case patterns, character classes), pull with your best keyword approximation then regex-filter results in your downstream code.
What happens if my `since:` date is before 2006?
Twitter launched March 21, 2006 — dates before that return zero results (no data exists). Both twitterapi.io and X official return empty result sets rather than error for pre-launch dates.
Continue
- Twitter (X) API — cluster hub
- Twitter (X) advanced search — operators + API
- Twitter (X) historical data + archive API
- Twitter (X) hashtag search API tutorial
- Twitter (X) API pricing breakdown
- twitterapi.io pricing
Stop reading. Start building.
Starter credits cover real testing on real data. Google sign-in, no card, no application queue.
Get an API key