# Twitter Search API and Advanced Operators (2026 Guide) > Twitter Search API guide for 2026, every advanced search operator (from:, to:, min_faves:, since:, until:) with working code in curl, Python, JavaScript. - **URL:** https://www.twitterapis.com/blogs/twitter-advanced-search-operators - **Published:** 2026-03-28 - **Updated:** 2026-09-05T00:00:00.000Z - **Author:** Emma - **Tags:** Twitter Search API, Tutorial, Search, Twitter API --- Most developers waste their first week on the Twitter Search API chasing operators that quietly do nothing. The reason is rarely a typo. Twitter ships two separate operator vocabularies, X's official docs only describe the smaller one, and the bigger, more useful set is documented mostly by the community. This page is the practical, tested reference for the operators that still work on Twitter/X in 2026, organized so you can copy a query, run it, and trust the result. The operator definitions here are cross-checked against the community-maintained [igorbrigadir/twitter-advanced-search](https://github.com/igorbrigadir/twitter-advanced-search) repo and live calls. > **TL;DR:** The web operator vocabulary (`from:`, `to:`, `since:`, `until:`, `min_faves:`, `min_retweets:`, `filter:`, exact phrases, boolean logic) lets you pre-filter exactly which tweets you pull before you spend a cent. X's official v2 endpoint honors only a slice of it. The TwitterAPIs Advanced Search endpoint accepts the entire web vocabulary, including the engagement thresholds the official API refuses, at $0.0008 per call (roughly 20 tweets a call). > **Want the product, the endpoint spec, and pricing tiers instead?** Those live on the [Twitter Search API page](/twitter-search-api). What follows is the operator manual: each filter and how to wire it into code. These operators run in two places. You can type them straight into Twitter's search box, or you can pass them programmatically to the TwitterAPIs Advanced Search endpoint (`GET /twitter/tweet/advanced_search`) at [$0.0008 per call](/pricing). The official X API only honors a fraction of them; TwitterAPIs forwards the full web set, which is why engagement gates like `min_faves:` and `min_retweets:` work here but never on v2. If a query built from these operators comes back empty for a post you are certain exists, the operator list is usually not the problem. We measured which query forms actually recover known posts in [why X advanced search misses posts you know exist](/blogs/why-x-advanced-search-misses-posts-2026), and 15 of 17 came back on a plain exact phrase once the phrase itself was well formed. ## The split that quietly kills queries Before any operator table, internalize one thing, because it explains most of the "my filter is being ignored" tickets: Twitter runs two different operator dialects. 1. **Web search operators** are the full vocabulary on this page. They power twitter.com/search, the old TweetDeck, and scraper-style providers such as TwitterAPIs. 2. **[Official v2 operators](https://docs.x.com/x-api/posts/search/integrate/build-a-query)** are a trimmed subset. They lack the heavy hitters, `min_faves:`, `min_retweets:`, `within_time:`, and `filter:blue_verified` among them. So if you point a query full of engagement gates at X's v2 `/2/tweets/search/recent`, half of it evaporates with no warning. Send the same string to TwitterAPIs Advanced Search and every operator fires. Pick the lane before you debug a "broken" filter. The lane confusion is exactly why threads on the engagement gates keep resurfacing in community forums, where people compare what `min_faves:` returns against what they expected and discover the operator behaves differently depending on which search index they hit.
Twitter Advanced Search inaccurate? "min_faves" doesn't show all the tweets it should. from r/Twitter
--- ## Three ways to run a Twitter search in 2026 "Twitter Search API" is a loose label for any programmatic route into Twitter's search index. There are three real choices today, and they differ wildly on cost and operator coverage: | Route | Endpoint | Price | Operators honored | | --- | --- | --- | --- | | **TwitterAPIs** | `GET /twitter/tweet/advanced_search` | [$0.0008 per call](/pricing) (~20 tweets) | **Entire** web set, `min_faves:` and `min_retweets:` included | | X API v2 | `GET /2/tweets/search/recent` | [$0.005 per post read](https://docs.x.com/x-api/getting-started/pricing) | **Trimmed**, no engagement gates; `from:`, `to:`, lang, and basics only | | X API v2 full-archive | `GET /2/tweets/search/all` | Enterprise tier only | Full set, but X publishes no price for that tier | For the everyday jobs (research, monitoring, dashboards, ETL backfills), **the TwitterAPIs endpoint is the cheapest pay-as-you-go production Twitter Search API in 2026** at $0.0008 a call, [about $0.04 per 1,000 tweets](/pricing) on a full 20-tweet page, with no developer-account review and the complete operator set live from your first request. The syntax is identical to what you would type into Twitter's web search, so throughout this guide "Twitter Search API" and the TwitterAPIs advanced search passthrough mean the same thing. --- ## 1. Engagement thresholds (the operators v2 will never give you) Start here, because this is the category developers leave the official API for. Minimum and maximum gates on likes, retweets, and replies let you skip the noise floor before you pay to pull anything. **None of these exist on X API v2 at any tier.** | Operator | Effect | Try it | |---|---|---| | `min_faves:N` | N likes or more | `openai min_faves:2000` | | `min_retweets:N` | N retweets or more | `ethereum min_retweets:250` | | `min_replies:N` | N replies or more | `tesla min_replies:75` | | `-min_faves:N` | Fewer than N likes (a ceiling) | `-min_faves:25` | | `-min_retweets:N` | Fewer than N retweets | `-min_retweets:10` | | `-min_replies:N` | Fewer than N replies | `-min_replies:1` | | `filter:has_engagement` | Any likes, RTs, replies, or quotes | `from:nasa filter:has_engagement` | | `-filter:has_engagement` | Tweets with zero engagement | `from:me -filter:has_engagement` | **Worked example:** pull the genuinely viral AI tweets of the year with `AI since:2026-01-01 min_faves:5000 lang:en`. The floor alone strips out an estimated 95% of the volume. The engagement gate is old enough to have been passed around the platform for years, yet most users have never typed it once. One of the most-shared explanations frames `from:handle min_faves:500` as the single most underused feature on Twitter, and the reaction it drew is a fair measure of how few people knew the operator existed at all. https://x.com/dickiebush/status/1376914228288847875 --- ## 2. Authors and mentions Narrow a query by who wrote a tweet, who it answers, or who it names. | Operator | Effect | Try it | |---|---|---| | `from:handle` | Posts authored by that account | `from:nasa` | | `to:handle` | Replies pointed at that account | `to:nasa` | | `@handle` | Any tweet that names the account | `@nasa` | | `@handle -from:handle` | Mentions only, dropping the account's own posts | `@nasa -from:nasa` | | `list:owner/slug` | Posts from members of a named list | `list:twitter/team` | | `list:` | Same, addressed by the list's numeric id | `list:84839422` | | `filter:verified` | Legacy (pre-2023) verified accounts | `space filter:verified` | | `filter:blue_verified` | Paid X Premium accounts | `space filter:blue_verified` | | `filter:follows` | Restricted to accounts you follow | `news filter:follows` | **Heads up:** you cannot negate `list:` or `filter:follows`. There is no `-list:twitter/team`. **Isolating old verification:** to find accounts that were verified before X Premium existed, stack `filter:verified -filter:blue_verified`. --- ## 3. Time windows Date filters are what turn a casual search into a reproducible dataset, and they are your main weapon against Twitter's flaky pagination (covered further down). | Operator | Effect | Try it | |---|---|---| | `since:YYYY-MM-DD` | From this day onward (inclusive) | `since:2026-02-01` | | `until:YYYY-MM-DD` | Up to this day (exclusive) | `until:2026-11-30` | | `since:YYYY-MM-DD_HH:MM:SS_UTC` | A timezone-aware timestamp | `since:2026-02-01_09:00:00_UTC` | | `since_time:` | Start bound as epoch seconds | `since_time:1769904000` | | `until_time:` | End bound as epoch seconds | `until_time:1769990400` | | `since_id:` | From a tweet id forward (Snowflake-ordered) | `since_id:1888888888` | | `max_id:` | Up to a tweet id | `max_id:1999999999` | | `within_time:2d` | A rolling 2-day window | `bitcoin within_time:2d` | | `within_time:3h` / `5m` / `30s` | Rolling hours, minutes, or seconds | `within_time:5m` | **The gotcha that traps everyone:** a time operator on its own returns nothing. `since:2026-01-01` must ride alongside a keyword, a handle, or a hashtag. Twitter treats a bare date filter as an empty query. **For fast-moving topics:** when a trend shifts minute to minute, switch to the `_HH:MM:SS_UTC` form so you can slice below the day boundary. **Two id-based bounds worth understanding:** `since_id:` and `max_id:` lean on the fact that tweet ids are [Snowflake ids](https://en.wikipedia.org/wiki/Snowflake_ID), monotonically increasing and time-sortable, so a higher id is always a later tweet. The `since_time:` and `until_time:` operators take [Unix epoch seconds](https://en.wikipedia.org/wiki/Unix_time) instead, which is handy when your upstream system already stores timestamps that way. If you are pulling a window by tweet id, the [scrape tweet history guide](/blogs/scrape-tweet-history-api-2026) shows how to anchor a backfill on a known id rather than a calendar date. --- ## 4. Keywords and boolean logic The plumbing under every query. These compose with everything above and below. | Operator | Effect | Try it | |---|---|---| | `term1 term2` | Both terms required (AND is assumed) | `mars rover` | | `term1 OR term2` | Either side; OR must be capitalized | `mars OR moon` | | `"locked phrase"` | Exact wording in order, spell-fix off | `"machine learning"` | | `"start * end"` | Asterisk stands in for any one word | `"best * of 2026"` | | `+word` | Pin the literal spelling, no autocorrect | `+kubernetes` | | `-word` | Exclude tweets carrying the word | `apple -fruit` | | `-"phrase"` | Exclude tweets carrying the phrase | `-"hot take"` | | `#tag` | Match a hashtag | `#opensource` | | `$SYM` | Cashtag for a ticker | `$NVDA` | | `url:host.com` | Tokenized domain anywhere in the body | `url:github.com` | | `:)` / `:(` | Crude positive or negative sentiment | `airlines :(` | **Tip:** AND is implicit, so `(mars rover)` and `mars rover` resolve to the same set. **Tip:** hyphenated domains need underscores: write `url:t_mobile.com`, not `url:t-mobile.com`. --- ## 5. Tweet kind Sort by whether a tweet is original, a reply, a retweet, a quote, or part of a self-thread. | Operator | Effect | |---|---| | `filter:replies` | The tweet is a reply | | `-filter:replies` | Strip replies out | | `filter:self_threads` | Author's own reply threads | | `filter:quote` | Carries a quoted tweet | | `filter:retweets` | Old-style "RT" plus quote tweets | | `filter:nativeretweets` | Retweet-button RTs (last 7-10 days only) | | `include:nativeretweets` | Adds RTs back in (off by default) | | `conversation_id:` | Every tweet in one conversation | | `quoted_tweet_id:` | Tweets quoting a given tweet | | `quoted_user_id:` | Tweets quoting a given account | **Note:** `filter:nativeretweets` and `include:nativeretweets` reach back only about 7 to 10 days. For anything older, fall back to `filter:retweets`. --- ## 6. Attached media Keep only tweets carrying a particular attachment type. | Operator | Effect | |---|---| | `filter:media` | Any attachment | | `filter:images` | Has an image, from any source | | `filter:twimg` | Native Twitter images only (pic.twitter.com) | | `filter:videos` | Any video, embeds included | | `filter:native_video` | Twitter-hosted video | | `filter:consumer_video` | Standard native video | | `filter:pro_video` | Amplify / pro video | | `filter:spaces` | Twitter Spaces audio | | `filter:links` | Contains any URL | | `filter:hashtags` | Contains a hashtag | | `filter:mentions` | Contains an @mention | | `filter:news` | Links to whitelisted news domains | | `filter:safe` | Drops NSFW and sensitive content | **Handy combination:** `from:NASA filter:media -filter:images` returns NASA's videos and GIFs while excluding stills. --- ## 7. Language codes Restrict by Twitter's auto-detected language using [ISO 639-1 two-letter codes](https://en.wikipedia.org/wiki/ISO_639-1). The detection is Twitter's own classifier, not yours, so on short or mixed-language tweets it occasionally guesses wrong, which is why the pseudo-codes below exist for the cases the classifier cannot resolve. | Operator | Effect | |---|---| | `lang:en` | English | | `lang:es` | Spanish | | `lang:ja` | Japanese | | `lang:hi` | Hindi | | `lang:fr`, `lang:de`, `lang:pt`, `lang:ru`, `lang:zh`, `lang:ar`, `lang:ko`, and so on | Standard two-letter codes | ### The pseudo-language codes nobody documents Twitter also exposes a handful of codes that sort by content shape rather than spoken language: | Code | Matches tweets that contain only | |---|---| | `lang:und` | Language it could not classify | | `lang:qam` | @mentions, nothing else | | `lang:qct` | Cashtags, nothing else | | `lang:qht` | Hashtags, nothing else | | `lang:qme` | Media links, nothing else | | `lang:qst` | Very short text (often under 3 chars) | | `lang:zxx` | Media or cards with no text at all | **Why bother:** to grab an account's image-only posts with zero text noise, run `from:user lang:zxx filter:images`. --- ## 8. Posting client Filter by the app that published a tweet. Spaces and hyphens in the client name become underscores. | Operator | Effect | |---|---| | `source:twitter_for_iphone` | Sent from iPhone | | `source:twitter_for_android` | Sent from Android | | `source:twitter_web_app` | Sent from the web | | `source:tweetdeck` | Sent from TweetDeck | | `source:twitter_ads` | A paid, promoted tweet | **Syntax note:** `Twitter for iPhone` becomes `twitter_for_iphone`. Underscores everywhere a space or dash would sit. --- ## 9. Location Match tweets by attached geo data. Treat this category as best-effort, not authoritative (see the warning). | Operator | Effect | |---|---| | `near:"City Name"` | Geotagged to a city | | `near:me` | Around your own location | | `within:10km` | A radius modifier (km or mi) | | `geocode:37.7764,-122.4172,10km` | Latitude, longitude, radius | | `place:` | A Twitter Place object | **Warning:** X retired exact-coordinate geotagging for the bulk of text tweets, so geo coverage is thin. Never make a location filter the backbone of a query you need to be complete. --- ## 10. Stacking operators The leverage is in combinations. One string can constrain topic, engagement, language, and date at once, for example `AI min_faves:5000 lang:en since:2026-01-01`. Here are high-signal stacks worth saving: | Query | What it surfaces | |---|---| | `from:nasa min_faves:50000 since:2026-01-01` | NASA's most viral 2026 posts | | `(bitcoin OR eth) min_faves:1000 lang:en -filter:retweets` | Popular English crypto talk, no RTs | | `"$NVDA" filter:blue_verified min_replies:20` | NVDA chatter from paid-verified accounts with discussion | | `#AI filter:images min_faves:100 since:2026-01-01` | Popular 2026 AI tweets carrying images | | `from:NASA filter:media -filter:images` | NASA videos and GIFs | | `list:twitter/team since:2026-01-01 lang:en` | Recent posts from a list's members | | `to:openai min_faves:10` | Replies aimed at OpenAI that got traction | | `conversation_id:1888888888` | A full thread around one tweet | ### How grouping and precedence actually resolve When you mix `OR` into a longer string, parentheses are not optional polish, they decide what the query means. Twitter evaluates an unparenthesized `OR` loosely, so `bitcoin OR eth min_faves:1000` does not read the way most people expect: the engagement floor binds to the `eth` side only, leaving the `bitcoin` side ungated. Wrap the alternation to fix it: `(bitcoin OR eth) min_faves:1000` applies the floor to both. The same logic governs exclusions. `apple -fruit OR juice` will not exclude what you think; group the intent explicitly as `apple (-fruit) (juice OR drink)`. The safe habit is to parenthesize every `OR` cluster and treat each parenthesized group as a single unit toward the 22-to-23-operator ceiling, because a grouped alternation counts as one expression rather than several. Build long queries from the inside out, validate each group on web search first, then assemble, and you sidestep the most common cause of a query that parses cleanly but returns the wrong set. --- ## What is flaky or dead in 2026 Plenty of operators that appear in old reference lists no longer behave. Knowing the dead ends up front saves an afternoon. | Operator | State | Reason | |---|---|---| | `filter:vine` | Dead | Vine closed in 2017 | | `filter:periscope` | Dead | Periscope closed in 2021 | | `near:`, `within:`, `geocode:` | Thin coverage | Exact geo retired for most tweets | | `filter:nativeretweets` | 7-10 day window | Twitter's retention cap | | `card_name:*` | 7-8 day window | Short retention | | `filter:verified` | Inconsistent | Tangled with Blue after the rebrand | **Operator ceiling:** Twitter caps a query at roughly 22 to 23 operators. Go past it and the whole query fails silently with no error. The silent-failure behavior is a frequent source of frustration, and the broader complaint that the native search index drops results or behaves inconsistently comes up often enough that it has its own running threads.
Twitter Search is Broken from r/Twitter
A few of these deserve a sentence of context. The `filter:vine` and `filter:periscope` operators still parse without error, which is the trap: they return nothing rather than failing loudly, so a pipeline can sit on an empty result set for days before anyone notices. The geo trio (`near:`, `within:`, `geocode:`) is not fully dead, it simply matches a shrinking pool of tweets, so use it only as a secondary narrowing filter on top of a keyword, never as the primary lever. And `filter:verified` is the subtle one: since the rebrand folded legacy verification and paid verification together in places, the same query can return different account mixes depending on Twitter's current handling, which is exactly why the explicit `filter:verified -filter:blue_verified` stack from the authors section is the safer way to isolate legacy accounts. --- ## Calling the endpoint from code The TwitterAPIs Advanced Search endpoint forwards every web operator at [$0.0008 per call](/pricing). Put your URL-encoded operator string in `query`, set `product` to `Latest` or `Top`, and walk pages with `cursor` until `next_cursor` comes back empty. No $200 floor ([X API pricing](https://docs.x.com/x-api/getting-started/pricing)), no developer-account approval, and the engagement gates are live on request one. Nothing caps the endpoint per minute or per day; the only ceiling is your credit balance and the concurrency your own client drives. For how that sits against X's published per-window ceilings in the [X rate-limit reference](https://docs.x.com/x-api/fundamentals/rate-limits), the [Twitter API rate limit guide](/blogs/twitter-api-rate-limit-guide) runs the side-by-side. | Detail | Value | |---|---| | Endpoint | `GET /twitter/tweet/advanced_search` | | Base URL | `https://api.twitterapis.com` | | Query param | `query` (URL-encoded operator string) | | Product param | `product=Latest` (default) or `product=Top` | | Price | $0.0008 per call (~20 tweets) | | Rate limit | None per window; bounded by your credit balance and client concurrency | | Pagination | Cursor-based; feed `next_cursor` back into `cursor` | | Auth | `Authorization: Bearer ` | ### curl ```bash curl "https://api.twitterapis.com/twitter/tweet/advanced_search?query=ethereum+min_faves%3A500+since%3A2026-02-01&product=Top" \ -H "Authorization: Bearer YOUR_API_KEY" ``` ### Python The [requests library](https://requests.readthedocs.io/en/latest/) is all you need; there is no SDK to install and no OAuth dance, since the endpoint authenticates on a single Bearer header. If you do not yet have a key, the [Twitter API key walkthrough](/blogs/how-to-get-twitter-api-key) covers signup, and the [complete Python tutorial](/blogs/python-twitter-api-tutorial) extends this snippet into a full client. ```python import requests ENDPOINT = "https://api.twitterapis.com/twitter/tweet/advanced_search" API_KEY = "YOUR_API_KEY" response = requests.get( ENDPOINT, params={"query": "ethereum min_faves:500 since:2026-02-01", "product": "Top"}, headers={"Authorization": f"Bearer {API_KEY}"}, ) payload = response.json() print(f"got {len(payload['tweets'])} tweets; more pages: {payload.get('next_cursor') is not None}") ``` ### JavaScript / Node.js The same call from Node uses only the built-in [`fetch`](https://nodejs.org/api/globals.html), with query params assembled via [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams), no client library required. For the full server-side build with paging, environment-based key handling, and error retries, the [Node.js Twitter API tutorial](/blogs/twitter-api-nodejs-tutorial) takes this snippet to production. ```javascript const ENDPOINT = "https://api.twitterapis.com/twitter/tweet/advanced_search"; const query = new URLSearchParams({ query: "ethereum min_faves:500 since:2026-02-01", product: "Top", }); const reply = await fetch(`${ENDPOINT}?${query}`, { headers: { Authorization: `Bearer ${API_KEY}` }, }); const payload = await reply.json(); console.log(`got ${payload.tweets.length} tweets; more pages: ${payload.next_cursor != null}`); ``` Every response comes back as `{ tweets: [...], next_cursor }`. To advance a page, hand the prior response's `next_cursor` to the next call's `cursor` param. ### Reading the response payload Each item in `tweets` carries the fields you would expect off a tweet object: the tweet `id`, its `text`, the `created_at` timestamp, the engagement counts (`favorite_count`, `retweet_count`, `reply_count`, `quote_count`, `view_count`), and a nested `author` block with `username`, `name`, `followers_count`, and the verification flags. Two practical habits save downstream pain. First, key your storage on the tweet `id` rather than position, so a duplicate that slips through during deep paging gets overwritten instead of double-counted. Second, treat `next_cursor` as the stop signal: it is the token you send to keep going, and when it comes back null or empty you have reached the end of the chain and stop, do not retry the same cursor. ### What a search job actually costs The pricing makes back-of-envelope math easy, so size a job before you run it. Reads bill at [$0.0008 per call](/pricing), and a call returns roughly 20 tweets, which works out to $0.04 per 1,000 tweets. A few common jobs, modeled out: - Pulling 10,000 tweets for a one-off study: about 500 calls, an estimated $0.40. - A daily brand-monitoring cron that grabs 2,000 tweets a day: about 100 calls a day, an estimated $0.08 daily, under $2.50 a month. - A 100,000-tweet historical backfill split into date windows: about 5,000 calls, an estimated $4.00. Every new account starts with [$0.50 in free credits](/pricing), which is about 625 calls or 12,500 tweets, enough to run a real pilot before you ever attach a card. These figures are a planning model, not a quote; actual call counts move with how dense each window is and how many tweets a given query returns per page. For a wider price comparison against other providers on a real per-1,000-tweet basis, the [cheapest Twitter API ranking](/blogs/cheapest-twitter-api-2026-8-providers-ranked-by-real-per-1000-tweet-cost) and the [Twitter API cost benchmark](/blogs/twitter-api-cost-benchmark-2026) both model the same workloads end to end. --- ## Why deep pagination breaks, and the fix Twitter's own cursor pagination for Advanced Search is unreliable upstream in 2026. Push past ten or so pages on a single cursor chain and you start seeing repeats or an early stop. This is not a TwitterAPIs bug; it is a Twitter-side defect that every scraping provider inherits the same way. The mechanics of cursor tokens are spelled out in X's own [pagination documentation](https://docs.x.com/x-api/posts/search/integrate/paginate), but the stability ceiling on deep chains is a real-world limit no provider can document away. **The fix:** stop leaning on deep cursors. Slice the query into date windows instead. Rather than one query that grinds through 50 pages, fire several narrower queries, each covering a shorter span: - `query=AI min_faves:100 lang:en since:2026-01-01 until:2026-01-08` - `query=AI min_faves:100 lang:en since:2026-01-08 until:2026-01-15` - `query=AI min_faves:100 lang:en since:2026-01-15 until:2026-01-22` Each window opens a fresh cursor chain. When a window is still too dense, drop to hourly slices with timestamp precision: `since:2026-01-01_12:00:00_UTC until:2026-01-01_13:00:00_UTC`. The result is steadier and fully reproducible, which deep pagination never is. --- ## Production code patterns The three patterns below cover what teams actually build: a paginator that backs off on transient errors, a window-splitter that sidesteps the cursor bug, and an async fetcher that runs windows side by side. ### Paginate with retry (Python) ```python import requests import time ENDPOINT = "https://api.twitterapis.com/twitter/tweet/advanced_search" API_KEY = "YOUR_API_KEY" def collect_pages(query: str, page_cap: int = 20) -> list[dict]: """Walk every page, backing off exponentially on transient errors.""" harvested: list[dict] = [] cursor = None auth = {"Authorization": f"Bearer {API_KEY}"} for _ in range(page_cap): args = {"query": query, "product": "Latest"} if cursor: args["cursor"] = cursor for try_num in range(3): reply = requests.get(ENDPOINT, params=args, headers=auth, timeout=15) if reply.status_code == 200: break if reply.status_code in (429, 502, 503): time.sleep(2 ** try_num) else: reply.raise_for_status() body = reply.json() harvested.extend(body.get("tweets", [])) cursor = body.get("next_cursor") if not cursor: break return harvested # Pull every viral AI tweet from January 2026 batch = collect_pages("AI min_faves:1000 lang:en since:2026-01-01 until:2026-02-01") print(f"pulled {len(batch)} tweets") ``` ### Split a long range into windows (Python) ```python from datetime import date, timedelta def split_by_window(base_query: str, opens: date, closes: date, span_days: int = 7) -> list[dict]: """Break a wide date range into weekly windows so the cursor stays stable.""" harvested: list[dict] = [] cursor_day = opens while cursor_day < closes: window_end = min(cursor_day + timedelta(days=span_days), closes) windowed_q = f"{base_query} since:{cursor_day.isoformat()} until:{window_end.isoformat()}" harvested.extend(collect_pages(windowed_q, page_cap=5)) cursor_day = window_end return harvested q1 = split_by_window("openai min_faves:500 lang:en", date(2026, 1, 1), date(2026, 4, 1)) print(f"Q1 2026: {len(q1)} viral OpenAI tweets") ``` ### Run windows concurrently (Python + httpx) This pattern uses [httpx](https://www.python-httpx.org/) for its native async client, so independent date windows fetch in parallel instead of one after another. Keep concurrency modest to stay under the rate ceiling, and if you are running heavy concurrent jobs from one machine, route them through rotating egress per the [residential proxy guide for Twitter scraping](/blogs/best-residential-proxies-twitter-scraping-2026) so a single IP does not absorb every request. ```python import asyncio import httpx from datetime import date, timedelta ENDPOINT = "https://api.twitterapis.com/twitter/tweet/advanced_search" API_KEY = "YOUR_API_KEY" async def pull_window(client: httpx.AsyncClient, query: str) -> list[dict]: reply = await client.get( ENDPOINT, params={"query": query, "product": "Latest"}, headers={"Authorization": f"Bearer {API_KEY}"}, ) reply.raise_for_status() return reply.json().get("tweets", []) async def gather_windows(base_query: str, opens: date, weeks: int = 4) -> list[dict]: queries = [] for week in range(weeks): start = opens + timedelta(weeks=week) finish = start + timedelta(weeks=1) queries.append(f"{base_query} since:{start.isoformat()} until:{finish.isoformat()}") async with httpx.AsyncClient(timeout=15) as client: windows = await asyncio.gather(*[pull_window(client, q) for q in queries]) return [tweet for window in windows for tweet in window] harvest = asyncio.run(gather_windows("bitcoin min_faves:200 lang:en", date(2026, 1, 1), weeks=12)) print(f"{len(harvest)} tweets across 12 weeks, fetched in parallel") ``` --- ## Field-tested workflows Operators on their own are trivia. The value shows up when you wire them into a repeating job. Five patterns cover most of what teams actually run. ### Brand monitoring Watch what people say about your brand, gated to meaningful engagement and stripped of your own posts: ``` "YourBrand" OR "@yourhandle" min_faves:5 lang:en -from:yourhandle -filter:retweets since:2026-01-01 ``` Chunk it into weekly windows, run it on a cron, and store each pull with its window date. Comparing week to week is how you spot a sentiment swing before it turns into a PR fire. To actually score the tone of the results, the [Twitter sentiment analysis guide](/blogs/twitter-sentiment-analysis-python) walks through that exact pipeline. ### Influencer discovery Surface accounts posting high-engagement content in a niche, with a paid-verification gate: ``` #niche OR "keyword1" OR "keyword2" min_faves:500 min_retweets:100 lang:en filter:blue_verified -filter:retweets since:2026-01-01 ``` The `min_faves:500 min_retweets:100` floor clears the noise, and `filter:blue_verified` keeps it to accounts that pay for verification. Page through, lift `author.username` off each tweet, and you have a seed list. The pagination code for this is in the [Python Twitter API tutorial](/blogs/python-twitter-api-tutorial). Once you have the seed accounts, the [export Twitter followers guide](/blogs/how-to-export-twitter-followers-api-2026) shows how to expand each one into its audience for deeper network mapping. ### Competitor intelligence Track conversation about a competitor while excluding the competitor's own posts: ``` "CompetitorName" OR "@competitor" -from:competitor min_faves:10 lang:en -filter:retweets ``` Bolt on `filter:replies` to zero in on complaint threads, or `-filter:replies` to see organic mentions. The `min_faves:10` floor clears the low-signal chatter that would otherwise drown the dataset. To turn this query into a recurring pull rather than a one-off, the [how to scrape tweets walkthrough](/blogs/how-to-scrape-tweets) covers the storage and dedupe scaffolding around it. ### Historical event research Rebuild the public reaction to a launch, a headline, or a market move with window slicing: ``` "event keyword" lang:en since:2026-03-01 until:2026-03-02 "event keyword" lang:en since:2026-03-02 until:2026-03-03 ... (go hourly when volume is high) ``` Each window's `until:` is an exclusive upper bound. For sub-hour detail during a fast event, switch to timestamps: `since:2026-03-01_09:00:00_UTC until:2026-03-01_10:00:00_UTC`. That rebuilds the reaction hour by hour. The math on a large historical sweep is in the [Twitter API cost guide](/blogs/twitter-api-cost). ### Scraping one specific thread Have a tweet id and want every reply and quote on it: ``` conversation_id:1888888888 ``` Want every tweet that quoted a given post: ``` quoted_tweet_id:1888888888 ``` Add `min_faves:5` to keep the thread to the parts that got traction. This captures a full public reaction to a single post, which on the official X API would take several separate calls to assemble. ### Buyer-intent lead generation Find people actively asking for what you sell, before a competitor answers them first: ``` ("looking for" OR "any recommendations" OR "anyone know a") "your category" min_faves:1 lang:en -filter:retweets within_time:3d ``` The phrase cluster catches intent language, `min_faves:1` clears empty noise without setting the bar so high you miss fresh posts, and `within_time:3d` keeps the list to tweets recent enough to still be worth a reply. Run it a few times a day on a tight window, dedupe on tweet `id`, and route new hits to whoever owns replies. Because the volume on a sharp intent query is usually low, a single window normally fits inside one or two pages, so you rarely need the chunker here. If you want this to fire automatically and post replies on a schedule, the [guide to building a Twitter bot](/blogs/how-to-build-a-twitter-bot-2026) wires the search into a send loop, and the [bot detection guide](/blogs/twitter-bot-detection-guide) covers the pacing and behavior limits that keep an automated account healthy. --- ## Operators people get wrong A few operators do something other than what their name suggests, and that mismatch causes most of the off-target datasets. **`filter:blue_verified` is not `filter:verified`.** `filter:verified` catches the pre-Blue legacy badge, the journalists, public figures, and organizations verified before 2023. `filter:blue_verified` catches anyone paying for X Premium. For editorial signal in a brand watch, you usually want `filter:verified`, not the paid pool. **`-filter:retweets` already covers both retweet styles.** It drops manual "RT @user" text and native retweet-button RTs together. `include:nativeretweets` puts native RTs back (most queries exclude them by default). For a clean original-content set, `-filter:retweets`. To trace how far one tweet spread, `filter:nativeretweets` inside a tight window. **`within_time:Xd` is relative, not anchored.** A window like `within_time:7d` counts back from the moment you run the query, so the same string returns a different set next week. For a dataset you need to reproduce, use explicit `since:` and `until:` dates instead. **`@user` is broader than a reply filter.** `from:user` returns only that account's own tweets. `@user` returns anything naming the account, including other people's replies and quotes. Use `from:user` for timeline work and `@user` for mention tracking, and `OR` them for full context. If the handle is also a common word, add `lang:en` or `min_faves:1` so unrelated tweets do not flood the stream. --- ## A test routine before you trust a query Validating a new operator combination before it goes into a pipeline takes three quick steps and prevents the classic failure: a query that looks right but returns empty or off-target at scale. **Step 1: eyeball it on web search, free.** Paste the string into `twitter.com/search` and check the results match your intent. Web search runs the same operator set TwitterAPIs forwards, so this costs nothing and catches obvious logic errors. **Step 2: fire one API call.** Run the string once and read the response. Skim the first five tweet texts and confirm they are what you expected. This is where you catch a query whose syntax is valid but whose logic is subtly wrong. **Step 3: check the count before paging.** On call one, look at `next_cursor` and the tweet count. If `next_cursor` comes back null or empty and you got fewer than five tweets, the query is too narrow or the window is off. Fix it before you spin up a full paginated job. --- ## Cheat sheet The categories developers reach for most: authors (`from:`, `to:`, `@`, `list:`), time (`since:`, `until:`, `within_time:`), engagement (`min_faves:`, `min_retweets:`, `min_replies:`), tweet kind (`filter:replies`, `filter:media`, `filter:quote`), language (`lang:en`, `lang:zxx`), and exclusions (`-word`, `-"phrase"`, `-filter:retweets`). **Quick picks:** - Authors: `from:`, `to:`, `@`, `list:` - Time: `since:`, `until:`, `within_time:` - Engagement: `min_faves:`, `min_retweets:`, `min_replies:` - Kind: `filter:replies`, `filter:media`, `filter:quote` - Language: `lang:en`, `lang:zxx` (media-only) - Exclude: `-word`, `-"phrase"`, `-filter:retweets` **Lead-generation stack:** `#yourkeyword min_faves:10 lang:en -filter:retweets since:2026-01-01` **Sentiment stack:** `$TICKER (":)" OR ":(") min_faves:5 lang:en since:2026-01-01` **Competitor-watch stack:** `@competitor -from:competitor filter:has_engagement since:2026-01-01` If you want the whole vocabulary on a single card you can screenshot and keep, this widely-shared cheat sheet collects the operators most people actually reach for, the author gate, the boolean OR, the engagement floors, the media filters, and the date bounds, in one compact list. https://x.com/thatroblennon/status/1554935283736403970 For anyone who would rather watch the operators typed into the search box before wiring them into code, this walkthrough builds advanced-search queries step by step and shows what each filter does to the result set in real time, which is the fastest way to develop an intuition for how the gates stack. https://www.youtube.com/watch?v=5z3rlmFmeLI --- ## Get started These operators hand you roughly 10x the filtering reach of the official X API, including the engagement floors and full boolean logic that v2 simply will not expose. TwitterAPIs gives every new account $0.50 in free credits at signup, no developer account and no card, enough to exercise every category on this page before you commit a dollar. 1. [Sign up at twitterapis.com](/signup), instant key, no developer-account review 2. Hit `GET /twitter/tweet/advanced_search` with your operator string in `query` 3. Read the [full API docs](https://docs.twitterapis.com) for paging, rate limits, and the response schema For the exhaustive operator list, including the obscure ones this page skips, the community-maintained [twitter-advanced-search repository](https://github.com/igorbrigadir/twitter-advanced-search) is the canonical reference. For production scraping technique, cost control, and proxy strategy, see the [Twitter scraping best practices guide](/blogs/twitterapis-best-practices). For a precise account of what v2 supports versus the full web set, [Twitter API v2 vs TwitterAPIs](/blogs/twitter-api-v2-vs-twitterapis) lays it out. And the per-call cost math at scale is in the [Twitter API cost guide](/blogs/twitter-api-cost). If date bounding is the only part you need, [how to search tweets by date](/answers/how-to-search-tweets-by-date) covers the day, timestamp, epoch and identifier forms without the rest of the operator set. --- *Operator behavior cross-checked against the community-maintained [igorbrigadir/twitter-advanced-search](https://github.com/igorbrigadir/twitter-advanced-search) repository (updated regularly), the [official X API documentation](https://docs.x.com/x-api/posts/search/integrate/build-a-query) as of May 2026, and the [X Developer Community forums](https://devcommunity.x.com) for operator-behavior changes.* ## Frequently Asked Questions ### What is the Twitter Search API? It is the umbrella label for any programmatic route into Twitter's search index. The cheapest pay-as-you-go production option in 2026 is the TwitterAPIs /twitter/tweet/advanced_search endpoint at $0.0008 per call (about 20 tweets), forwarding the full web operator set. X's own /2/tweets/search/recent is also a search API, but at $0.005 per post read and a trimmed operator list that drops the engagement gates entirely. ### How do I call the Twitter Search API in Python? Install the requests library with pip install requests, then send a GET to https://api.twitterapis.com/twitter/tweet/advanced_search with your operator string in the query param and your key in an Authorization Bearer header. Read tweets and next_cursor off the JSON response, and feed next_cursor back into cursor to walk pages. The full tutorial with retry, paging, and async patterns moves you from a single call to a production paginator step by step. ### Is there a free tier? X's official API has no real free tier in 2026; search reads run $0.005 per post. TwitterAPIs drops $0.50 in free credits on every new account at signup with no card, which buys about 625 search calls (roughly 12,500 tweets), plenty to validate the API before you commit a dollar to it. ### How does this differ from Twitter API v2 search? The v2 /2/tweets/search/recent endpoint is one specific search API. The TwitterAPIs /twitter/tweet/advanced_search endpoint is a separate one with very different economics (about 100x cheaper) and a much wider operator set, the full web vocabulary versus v2's slice. The biggest practical gap is that v2 silently ignores engagement gates such as min_faves:, while the TwitterAPIs passthrough honors them on the first request. ### Why does min_faves: do nothing on the official API? Engagement gates (min_faves:, min_retweets:, min_replies:) work on Twitter's web search and on the TwitterAPIs advanced search endpoint, but not on X API v2 at any tier. It is a long-running gap that makes v2 far weaker for engagement filtering. The TwitterAPIs endpoint forwards these gates intact, which is the main reason teams leave v2 for it in the first place. ### How do I page through results? Cursor paging: feed each response's next_cursor into the next call's cursor, and stop when next_cursor comes back null or empty. For deep pulls (50-plus pages), split the query into date windows with since: and until: to dodge duplicates and early stops, since Twitter's upstream cursor chain grows unreliable past about ten pages on a single chain. ### What is the rate limit? X's official API allows 450 search requests per 15 minutes on a Bearer token, dropping to 300 under user-context OAuth. The TwitterAPIs advanced_search endpoint runs under the same flat ceiling as every other route, 600 requests a minute and 20 concurrent per key, so analytics, monitoring, and research jobs plan against one number plus their credit balance. If you want to go faster, split the query into parallel date windows across separate processes. ### How do I page without duplicates? Cursor paging is the only dependable method: hand each response's next_cursor back as cursor, stop when next_cursor comes back null or empty, and set a maxPages cap so a runaway loop cannot happen. For deep historical work, slice into date windows with since: and until: rather than grinding one cursor chain, which is where the upstream cursor-stability bug produces duplicates past 50 pages. ### Can a single query hold more than 10 operators? Yes, but there is a hard ceiling. Twitter caps a query around 22 to 23 operators; go further and it fails silently or returns empty with no error. In practice most useful queries land between 5 and 10. When you need something complex, group related conditions in parentheses to keep the count down, for example (bitcoin OR eth OR crypto) reads as one expression, not three operators. ## Where these numbers come from Each row is a figure in this post and the artefact it was read from. Prices and limits on this platform move, so check the date on the source before you plan against it. [igorbrigadir twitter-advanced-search repository](https://github.com/igorbrigadir/twitter-advanced-search) The community-maintained operator catalog the post says every operator definition on the page was cross-checked against, alongside live calls. [X API v2 build-a-query operator reference](https://docs.x.com/x-api/posts/search/integrate/build-a-query) The official v2 operator list the post uses to show the trimmed subset, which lacks min_faves, min_retweets, within_time, and filter blue_verified. [X API pricing page](https://docs.x.com/x-api/getting-started/pricing) The source for the $0.005 per post read figure in the route comparison table, and for the point that the official search route carries a spend floor. [X API rate-limit reference](https://docs.x.com/x-api/fundamentals/rate-limits) The published X ceilings the post sets against the TwitterAPIs Advanced Search endpoint, which carries one flat 600 req/min ceiling of its own rather than a per-endpoint window. [X API search pagination documentation](https://docs.x.com/x-api/posts/search/integrate/paginate) Documents the cursor token mechanics the post relies on, and is the contrast for its finding that deep cursor chains past roughly ten pages return repeats or stop early. [Snowflake ID scheme](https://en.wikipedia.org/wiki/Snowflake_ID) Explains why tweet ids are monotonically increasing and time-sortable, which is what makes the since_id and max_id bounds in the post behave as time windows.