Twitter Filter Rule Reference: Fields and Operators
Twitter Filter uses a simple expression language to match tweets in your timeline. Each rule has a condition (expression) and an action (mark, hide, or block). This page is the complete reference for writing rules.
Actions#
| Action | Effect |
|---|---|
mark | Yellow border + reduced opacity, with a label showing the matched rule |
hide | Completely hides the tweet from the timeline |
block | Hides the tweet immediately and blocks the account after a configurable delay (default 3s). A toast notification appears with an Undo button to cancel before the block executes |
Warning: The
blockaction will block real accounts on your behalf. Use with caution. The delay + undo mechanism helps prevent accidental blocking.
Available Fields#
User fields#
| Field | Type | Description |
|---|---|---|
user.id | string / null | Numeric user ID |
user.name | string | Display name |
user.screen_name | string | Username / handle (without @) |
user.description | string / null | Bio |
user.location | string / null | User-defined location (free text, not GPS — users can write anything here) |
user.url | string / null | Profile URL |
user.avatar | string / null | Avatar image URL |
user.default_profile | boolean | Has default profile (never customized banner) |
user.default_profile_image | boolean | Has default profile image (egg avatar) |
user.blue_verified | boolean | Paid blue checkmark |
user.verified | boolean | Gray/gold verification badge |
user.verified_type | string / null | e.g. “Business”, “Government” |
user.followers | number / null | Follower count |
user.following | number / null | Following count |
user.statuses_count | number / null | Total tweets posted |
user.created_at | string / null | Account creation date (ISO format) |
Relationship & privacy fields#
| Field | Type | Description |
|---|---|---|
user.protected | boolean | Account is protected (locked) |
user.is_following | boolean | You follow this user |
user.followed_by | boolean | This user follows you |
user.blocking | boolean | You are blocking this user |
user.blocked_by | boolean | This user is blocking you |
user.muting | boolean | You are muting this user |
Tweet fields#
| Field | Type | Description |
|---|---|---|
tweet.text | string | Tweet text content |
tweet.is_reply | boolean | Is a reply |
tweet.has_links | boolean | Contains links |
tweet.has_media | boolean | Contains images/videos |
tweet.lang | string / null | BCP 47 language code (e.g. "en", "ja", "zh"). Special values: "und" (undetermined), "zxx" (no linguistic content) |
tweet.is_promoted | boolean | Whether this is a promoted (ad) tweet |
Community signal fields#
Resolved asynchronously from X’s own “About this account” data and shared across users via a community cache — unlike every field above, these aren’t available synchronously from the timeline response. A rule using one of these fields simply doesn’t match a given tweet until resolution completes (which can take a moment the first time you encounter an account), and block cannot be used with a condition that references them — this data can be wrong or spoofed (e.g. via VPN), so pair it with hide and combine it with another structural condition rather than using it standalone. The first time you save, edit, or subscribe to a rule using one of these fields, a one-time dialog explains what gets shared.
| Field | Type | Description |
|---|---|---|
user.based_in | string / null | X’s inferred current location for the account — a country or region/continent name |
user.based_in_granularity | "country" / "region" / null | Whether based_in resolved to a specific country or only a broader region |
user.likely_proxy | boolean / null | X’s own suspicion that the account is using a proxy/VPN — weak, noisy signal; never use standalone |
user.connected_region | string / null | App Store/Play payment region — more stable than based_in, harder to spoof |
user.connected_platform | string / null | Which store/platform connected_region came from, e.g. "App Store", "Android App", "Web" |
null is a normal, legitimate value here (not an error) — it means the account resolved successfully but genuinely has no value for that field, which is common for political/diplomatic/major-org accounts (based_in) or accounts connected via the web rather than an app (connected_region/connected_platform). It works with ==/!= like any other value.
Operators#
| Operator | Description |
|---|---|
==, != | Equality / inequality |
<, >, <=, >= | Numeric comparison |
&&, || | Logical AND / OR |
! | Logical NOT |
+, -, *, / | Arithmetic |
contains | Case-insensitive substring match |
matches | Regex match (Unicode mode — \p{...} property escapes supported, e.g. \p{Extended_Pictographic}) |
Built-in Functions#
| Function | Description |
|---|---|
is_null(val) | Check if value is null/undefined |
len(val) | String length (returns 0 for non-strings) |
days_since(date) | Days elapsed since an ISO date string (returns Infinity for invalid dates) |
lower(val) | Convert string to lowercase |
Null Handling#
Some fields can be null (e.g. user.followers, user.description). Here’s how null behaves:
- Arithmetic (
+,-,*,/): if either side is null, result is null - Comparison (
<,>,<=,>=): if either side is null, result is false - Equality (
==,!=): works normally —user.description == nullreturns true when bio is empty - String ops (
contains,matches): return false if either side is not a string - Use
is_null(val)to explicitly check for null values
Community signal fields are different: an unresolved community signal field isn’t null — the whole rule is skipped for that tweet until resolution completes (no match, no error, just tried again once the data arrives). null only shows up for these fields after resolution succeeds, meaning the account genuinely has no value there — at that point it behaves like any other nullable field above.
Examples#
Keyword filtering#
Filter tweets or bios containing specific keywords:
tweet.text contains 'crypto' || user.description contains 'crypto'
Suspicious accounts#
Low follower ratio combined with a new account:
user.followers / user.following < 0.01 && days_since(user.created_at) < 30
Bot-like username pattern#
Usernames that are letters followed by 8 or more digits (e.g. sarah83927482):
user.screen_name matches '^[a-zA-Z]+\d{8,}$'
Default or missing avatar#
Catch accounts that never set a profile picture:
user.default_profile_image == true
Hide non-English tweets#
tweet.lang != 'en'
Hide blocked users that still appear in timeline#
user.blocking == true
Hide promoted (ad) tweets#
tweet.is_promoted == true
Hide tweets from users you’re muting#
In case muted tweets leak through:
user.muting == true
Low-follower accounts flagged as likely proxy/VPN users#
Combine a weak community signal with a structural one rather than using it standalone (action hide, never block):
user.likely_proxy == true && user.followers < 20
Mark protected accounts that don’t follow you#
user.protected == true && user.followed_by == false
Only show tweets from accounts you follow#
Hide everything from accounts you don’t follow:
user.is_following == false
Emoji-only replies#
Catch replies that contain only emoji and whitespace (commonly used by spam bots):
tweet.is_reply && tweet.text matches '^(@\w+\s+)*[\p{Emoji}\p{Emoji_Component}\s]+$' && len(tweet.text) > 7 && user.followers < 50
Complex: new unverified account with spam keywords#
Combine multiple conditions:
days_since(user.created_at) < 7
&& user.blue_verified == false
&& (tweet.text contains 'free' && tweet.text contains 'mint')
Tips#
- Whitelist: Your own tweets and tweets from users you follow are always skipped — they will never be matched by any rule.
- Rule priority: When multiple rules match the same tweet, the most severe action wins (
block>hide>mark). - Screen name lists: Each rule can also have a list of usernames that it matches against directly, without needing a condition expression.
- Testing rules: Set the action to
markfirst — matched tweets get a yellow border so you can see exactly what’s being caught. Once you’re happy with the results, switch tohideorblock.