What are the Twitter advanced search operators?
Last updated August 24, 2026
Operators are prefixes you stack inside one query string. from:nasa pins an author, min_faves:500 sets an engagement floor, since:2026-01-01 opens a date window, lang:es restricts language, double quotes lock an exact phrase and a leading minus negates. TwitterAPIs forwards the whole string to tweet/advanced_search, where a call reads roughly 20 posts for $0.0008.
Every rate here is the pricing TwitterAPIs publishes. The billed rate is $0.0008 per call; $0.04 per 1,000 tweets is derived from it at a full 20-tweet page, which is the default page size rather than a guaranteed yield (source: twitterapis.com/pricing).
The grammar underneath every query
Whitespace between two terms means AND, so mars rover requires both words and the parenthesised form (mars rover) resolves to the identical set. There is no AND keyword to type and no NOT keyword either. OR widens a match but has to be capitalised, because a lowercase or is read as an ordinary search word rather than a boolean, which is the single most common reason a query parses cleanly and returns the wrong set. Everything else in the vocabulary is a prefix operator written as name:value with no space after the colon, and each clause you add narrows the result rather than widening it unless you have explicitly introduced an alternation. Hold those two facts and the rest of this reference is vocabulary.
Quoting, wildcards and pinned spellings
Double quotes lock wording in order and switch spell correction off, so "machine learning" will not quietly match a corrected variant. An asterisk inside a quoted phrase stands in for exactly one word, which makes "best * of 2026" match a whole family of phrases in a single clause. A leading plus pins a literal spelling against autocorrect, which matters for product names the classifier keeps helpfully rewriting, as in +kubernetes. Hashtags and cashtags are matched by their own sigils, #opensource and $NVDA, and neither needs quoting. url:host.com matches a tokenised domain anywhere in the body, and a hyphenated host has to be written with an underscore instead, so url:t_mobile.com rather than the literal spelling of the domain.
Grouping, precedence and the trap in an unwrapped OR
Operators stack with spaces and every clause applies at once, which is why from:elonmusk min_faves:500 since:2026-01-01 behaves exactly as written. The trap is OR, which binds loosely rather than the way most people read it. In bitcoin OR eth min_faves:1000 the engagement floor attaches to the eth side alone and leaves every bitcoin post ungated, which presents as the operator being ignored. Wrap the alternation, as in (bitcoin OR eth) min_faves:1000, and the floor covers both. Exclusions behave identically, so apple -fruit OR juice needs explicit grouping too. Parenthesise every OR cluster by habit, build long strings from the inside out, and treat each parenthesised group as one unit rather than as each of its terms.
Negation, and the two operators that refuse it
A leading minus negates, and it takes more shapes than most references show. It works on a bare word, so apple -fruit drops the wrong sense of an ambiguous term. It works on a quoted phrase, as in -"hot take". It works on a filter, so -filter:replies strips replies and -filter:retweets removes the old RT text style and native reposts together in one clause. It also works on the engagement operators, where it does something different in kind rather than degree: -min_faves:25 does not loosely exclude popular posts, it inverts the floor into a ceiling. Two operators cannot be negated at all. There is no -list: form and no way to invert filter:follows, so plan both as inclusions only.
Scoping by author, mention and list
from:handle keeps only posts that account wrote. to:handle keeps replies aimed at it, while a bare @handle matches anything naming it, so @nasa -from:nasa isolates mentions and drops the account's own posts. Those three get confused constantly and they return very different sets, so pick by the job: from: for timeline work, @ for mention tracking, both joined by OR when you want the full picture around an account. list:owner/slug and list:84839422 restrict results to a named list's members, addressed either by path or by numeric id. filter:verified catches the pre-2023 legacy badge while filter:blue_verified catches paid accounts, and stacking filter:verified -filter:blue_verified isolates the legacy pool. filter:follows narrows to accounts the searching session already follows.
Engagement floors, and the ceilings nobody mentions
min_faves:N, min_retweets:N and min_replies:N each set a lower bound, so solana min_faves:1000 drops everything quieter than that. Negating the same operator inverts it into a ceiling, which is the half almost nobody uses: -min_faves:25 returns posts holding fewer than twenty five likes, and -min_replies:1 isolates posts nobody answered at all. filter:has_engagement keeps anything that drew a like, repost, reply or quote, and its negated form finds the posts that landed completely flat. These gates are the reason to pre-filter rather than post-filter, since a floor applied upstream means you never pay to pull the noise in the first place. On a broad topic term a five-thousand-like floor strips the overwhelming majority of the volume before a single row is billed.
Bounding time by date, epoch or post id
since: is inclusive and until: excludes its own day, both taking YYYY-MM-DD. Append _HH:MM:SS_UTC to either and you can slice below a day boundary, which matters when a topic moves hour to hour. since_time: and until_time: take Unix epoch seconds instead, convenient when your upstream system already stores timestamps that way. since_id: and max_id: bound by post id, which works because those ids increase monotonically and are therefore time-sortable, so a higher id is always the later post. within_time:2d, 3h, 5m or 30s gives a rolling window measured back from the moment you run it, which makes it the wrong choice for anything you need to reproduce later. One rule catches everyone: a time bound alone returns nothing and must ride alongside a keyword, a handle or a hashtag.
Post kind and conversation scoping
filter:replies keeps replies, and its negation strips them for a clean original-content set. filter:self_threads narrows to an author's own reply chains. filter:quote keeps posts carrying a quoted original. filter:retweets covers both the old RT text style and quote posts, while filter:nativeretweets targets retweet-button reposts and reaches back only about seven to ten days; include:nativeretweets adds those back to a query that would otherwise drop them by default. conversation_id: followed by a post id returns every post in that one conversation. quoted_tweet_id: returns the posts quoting a given post, and quoted_user_id: widens the same idea across a whole account. Those last three are how you pull a discussion through search rather than through the dedicated post routes.
Attachment filters, and how they combine
filter:media catches any attachment at all, and the narrower forms split by kind: filter:images for a still from any source, filter:twimg for natively hosted images only, filter:videos for any video including embeds, filter:native_video for platform-hosted video, plus filter:consumer_video and filter:pro_video for the standard and amplify variants. filter:spaces keeps live audio posts. Three more key off what the body contains rather than what is attached to it, namely filter:links, filter:hashtags and filter:mentions. filter:news restricts to whitelisted news domains and filter:safe drops sensitive content. Combining is where the family earns its keep, because subtraction is available too: from:nasa filter:media -filter:images returns that account's video and GIF posts with every still image removed, which no single positive filter expresses on its own.
Language codes, and the pseudo-codes nobody documents
lang: takes ISO 639-1 two-letter codes, lang:en, lang:es, lang:ja, lang:hi and the rest, and runs against the platform's own classifier rather than yours. On short or mixed text that classifier guesses wrong often enough to matter, which is why a second family exists alongside it. The pseudo-codes sort by the shape of the content instead of its language: und for text it could not classify, qam for posts carrying only mentions, qct for cashtags only, qht for hashtags only, qme for media links only, qst for very short text, and zxx for content with no text at all. That last one is the genuinely useful one, since from:user lang:zxx filter:images returns image-only posts with every captioned one excluded.
Posting client, place, and the operators that are dead
source: filters by the app that published a post, with spaces and hyphens becoming underscores, so twitter_for_iphone, twitter_for_android, twitter_web_app, tweetdeck and twitter_ads all work as written. The location family, near:"City Name", near:me, within:10km, geocode:37.7764,-122.4172,10km and place:, still parses but matches a shrinking pool because exact geotagging was retired for most posts, so use it to narrow a keyword query and never as the backbone of one. filter:vine and filter:periscope parse without error and return nothing, the worst failure mode available, because a pipeline can sit empty for days. card_name: retains roughly seven to eight days. A query caps out around 22 to 23 operators, past which the whole string fails silently rather than partially matching.
Where the string is evaluated, and what it costs
The finished string goes into the query parameter of GET /twitter/tweet/advanced_search and is evaluated server-side exactly as it would be on the twitter.com advanced search screen, with an Authorization Bearer header carrying your key. product chooses the ranking, defaulting to Latest, with Top, People, Photos and Videos also accepted; it changes the ordering and the result flavour, never which operators fire. Results arrive roughly twenty at a time and you read past that by feeding next_cursor back in as cursor rather than by asking for a larger page. One call is a standard read at $0.0008, which puts a thousand posts near four cents, and the whole key runs under one ceiling of 600 requests a minute with 20 concurrent. That figure is ours: $0.0008 a call, per our pricing page.
Operator families at a glance
| Family | Example | What it narrows |
|---|---|---|
| Author and audience | @nasa -from:nasa | Mentions of an account, minus its own posts |
| Engagement floor | solana min_faves:1000 | Posts holding a thousand likes or more |
| Engagement ceiling | -min_faves:25 | Posts under twenty five likes |
| Date window | nvda since:2026-01-01 until:2026-03-01 | A calendar range, end bound exclusive |
| Rolling window | bitcoin within_time:3h | The last three hours, measured at run time |
| Conversation | conversation_id:1888888888 | Every post in one exchange |
| Attachment | from:nasa filter:media -filter:images | Video and GIF posts, stills removed |
| Language | mundial lang:es | One auto-detected language |
This dictionary documents every available field for each object type.
Questions and answers
- Do operators have to be capitalised?
- Only OR does. The prefixes themselves are lowercase, as in from:, lang: and filter:, but a lowercase or is treated as an ordinary search word rather than a boolean, so the query silently means something other than what you wrote. That single rule accounts for a large share of strings that parse cleanly and return the wrong set, because nothing anywhere in the response warns you that it happened.
- How do I exclude something from results?
- Put a minus directly in front of it, with no space. It works on a bare word, on a quoted phrase, on a filter such as -filter:replies, and on the engagement operators, where -min_faves:25 flips a floor into a ceiling rather than merely excluding. Two things resist it entirely: list: and filter:follows cannot be negated at all, so there is no -list: form to reach for and no way to invert a follows restriction.
- Why does my OR query ignore the engagement floor?
- Because an unparenthesised OR binds loosely. In bitcoin OR eth min_faves:1000 the floor attaches only to the eth side, leaving every bitcoin post ungated, which reads exactly like the operator being ignored. Wrapping the alternation fixes it, so (bitcoin OR eth) min_faves:1000 applies the floor across both sides. Group every OR cluster as a matter of habit and the ambiguity disappears, along with most queries that look right and return wrong.
- Can I search a date range with no keyword?
- No. A time operator on its own returns nothing, so since:2026-01-01 has to ride alongside a keyword, a handle, a hashtag or a filter. A bare date bound is read as an empty query rather than as a broad one. Pair it with something concrete first, then narrow using the _HH:MM:SS_UTC form when a single calendar day still returns more volume than you want to page through.
- How do I search inside one conversation?
- conversation_id: followed by a post id returns every post in that conversation, which is what you want when the whole exchange matters rather than one level of it. quoted_tweet_id: does the neighbouring job, returning the posts that quote a given one, and quoted_user_id: widens that across an entire account. All three take numeric ids and page on the ordinary cursor, so nothing about the calling pattern changes.
- Does the ranking mode change which operators work?
- No. product only changes the ordering and the result flavour, with Latest as the default and Top, People, Photos and Videos as the alternatives. The same operator string is evaluated either way, so a query tested on Latest behaves identically when you switch it. Choose by the job instead: Latest when monitoring a live query where recency is the point, Top when the most engaged matches beat the freshest ones.
- Which operators are dead or unreliable in 2026?
- filter:vine and filter:periscope still parse and simply return nothing, which is worse than an error because a pipeline can sit empty for days without anyone noticing. The geo family, near:, within: and geocode:, matches a shrinking pool since exact geotagging was retired for most posts. filter:nativeretweets reaches back only seven to ten days, card_name: about seven to eight, and filter:verified behaves inconsistently since the badge rebrand.
- Is there a limit on how many operators one query can hold?
- Roughly 22 to 23, and going past it fails silently with no error rather than returning a partial match, which is the expensive kind of failure. Most useful queries land between five and ten anyway. When a query genuinely needs more, group related conditions in parentheses, since a parenthesised alternation counts as one expression toward the ceiling rather than as each of the terms inside it.
- How do I filter to posts that contain only an image?
- Combine a media filter with a pseudo-language code. lang:zxx matches content carrying no text at all, so from:user lang:zxx filter:images returns image-only posts with none of the captioned ones mixed in. The other pseudo-codes work the same way: qht for hashtag-only posts, qam for mention-only, qct for cashtag-only, qme for bare media links, and und for text the classifier could not place anywhere.
- What does running these queries cost?
- One search request is a standard read at $0.0008 and returns roughly twenty posts, which works out near four cents per thousand. Adding operators does not change that price at all. Because a tighter string returns fewer irrelevant rows per page, precise operators usually lower total spend rather than raising it, which is the practical argument for learning the engagement gates before you start paging anything.
Keep reading
Start with $0.50 in free credits
No credit card. Roughly 12,500 tweets to test every endpoint.