xmaster 1.6.3

Enterprise-grade X/Twitter CLI — post, reply, like, retweet, DM, search, and more
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
// ──────────────────────────────────────────────────────────
//  How the X Algorithm Works and How to Grow
//  Based on xai-org/x-algorithm (January 2026)
// ──────────────────────────────────────────────────────────

#set page(
  paper: "a4",
  margin: (top: 2.8cm, bottom: 2.8cm, left: 2.5cm, right: 2.5cm),
  numbering: "1",
  number-align: center,
)

#set text(font: "New Computer Modern", size: 10pt, lang: "en")
#set par(leading: 0.65em, first-line-indent: 1.2em, justify: true)
#set heading(numbering: "1.1")

#show heading.where(level: 1): it => {
  v(1em)
  set text(12pt, weight: "bold")
  set par(first-line-indent: 0em)
  block[#counter(heading).display() #it.body]
  v(0.5em)
}

#show heading.where(level: 2): it => {
  v(0.8em)
  set text(10.5pt, weight: "bold")
  set par(first-line-indent: 0em)
  block[#counter(heading).display() #it.body]
  v(0.3em)
}

#show heading.where(level: 3): it => {
  v(0.5em)
  set text(10pt, weight: "bold", style: "italic")
  set par(first-line-indent: 0em)
  block[#it.body]
  v(0.2em)
}

#show raw.where(block: true): it => {
  set text(8.5pt, font: "Menlo")
  block(width: 100%, fill: luma(248), stroke: 0.5pt + luma(200), radius: 2pt, inset: 10pt, it)
}

#show raw.where(block: false): it => {
  set text(9pt, font: "Menlo")
  box(fill: luma(245), outset: (x: 2pt, y: 2pt), radius: 2pt, it)
}

#show figure.caption: it => { set text(9pt); it }

#let code-tag = box(fill: luma(230), stroke: 0.5pt + luma(180), outset: (x: 2pt, y: 1.5pt), radius: 2pt)[#text(7.5pt, weight: "bold")[CODE]]
#let emp-tag = box(fill: luma(230), stroke: 0.5pt + luma(180), outset: (x: 2pt, y: 1.5pt), radius: 2pt)[#text(7.5pt, weight: "bold")[EMP.]]
#let inf-tag = box(fill: luma(230), stroke: 0.5pt + luma(180), outset: (x: 2pt, y: 1.5pt), radius: 2pt)[#text(7.5pt, weight: "bold")[INF.]]

// ════════════════════════════════════════════
//  TITLE
// ════════════════════════════════════════════

#set par(first-line-indent: 0em)

#align(center)[
  #v(0.5cm)
  #text(16pt, weight: "bold")[
    How the X Algorithm Works and How to Grow:\
    A Source Code Analysis
  ]
  #v(0.6cm)
  #text(10.5pt)[Boris Djordjevic]
  #v(0.15cm)
  #text(9pt, fill: luma(100))[199 Biotechnologies · March 2026]
  #v(0.8cm)
]

#block(width: 100%, inset: (x: 2em))[
  #set text(9.5pt)
  #set par(first-line-indent: 0em)
  #text(weight: "bold")[Abstract.] #h(0.5em)
  X open-sourced its recommendation algorithm in January 2026. This paper explains how it works and what it means for anyone trying to grow an account, particularly from a small or dormant starting point. The system uses a Grok-based transformer that scores every post on 19 engagement signals---15 positive and 4 negative---learned entirely from user behaviour, with no hand-engineered features. We explain the scoring pipeline, deduce the relative importance of each signal, and derive a concrete growth strategy grounded in the source code. Every claim is tagged by evidence source: source code (#code-tag), published empirical research (#emp-tag), or first-principles inference (#inf-tag).
]

#v(0.5cm)
#line(length: 100%, stroke: 0.5pt + luma(180))
#v(0.3cm)

#set par(first-line-indent: 1.2em)

// ════════════════════════════════════════════
//  1. HOW THE ALGORITHM WORKS
// ════════════════════════════════════════════

= How the Algorithm Works

When you open your "For You" feed, the system executes a pipeline that narrows millions of posts down to a ranked list of roughly 50. The entire process takes about 200 milliseconds.

== Where posts come from

Posts are sourced from two places, in parallel #code-tag:

- *In-network (Thunder).* Posts from accounts you follow, served from an in-memory store with sub-millisecond lookups.
- *Out-of-network (Phoenix Retrieval).* Posts discovered from a global corpus using a machine learning model that matches your engagement history to candidate posts via dot-product similarity.

Out-of-network is the discovery mechanism. It is how posts reach people who do not follow you. It is also penalised by a multiplicative discount factor (`OON_WEIGHT_FACTOR` < 1.0), meaning in-network content has a structural advantage #code-tag.

== How posts are scored

Every candidate post is scored by a Grok-based transformer model that predicts the probability of 19 different user actions---like, reply, repost, share, block, report, and so on. These predicted probabilities are combined into a single score using a weighted sum #code-tag:

$ "score" = sum_(i=1)^19 w_i dot P(a_i) $

The weight constants ($w_i$) determine how much each action matters. They are not published. We estimate them in Section 2.

== What the model sees about you

The transformer takes your last *128 engagements* as input---every like, reply, repost, and share you made, along with the posts and authors involved #code-tag. This engagement history is how the model understands your interests. It is also how it predicts what you would engage with next.

A dormant account has no engagement history. The system literally cannot score posts for you or about you until you generate data (see Section 4).

== Filtering

Before scoring, 10 filters remove ineligible content---duplicates, old posts, content from blocked or muted accounts, muted keywords, and previously seen posts. After scoring, additional safety filters remove spam, violence, and policy violations #code-tag.

Out-of-network content faces a stricter safety threshold than in-network content #code-tag.

== Author diversity

If the same author appears multiple times in a feed, each successive appearance is penalised by an exponential decay function #code-tag:

$ "multiplier"(n) = (1 - "floor") dot "decay"^n + "floor" $

This means posting 5 times in an hour is counterproductive---your 5th post receives a fraction of its natural score.

// ════════════════════════════════════════════
//  2. WHAT THE ALGORITHM REWARDS
// ════════════════════════════════════════════

= What the Algorithm Rewards (and Punishes)

The scoring formula uses exactly 19 signals. The weight constants are hidden, but we can estimate their relative importance from code structure, platform economics, and empirical research.

== The 19 signals, ranked by estimated impact

#figure(
  table(
    columns: (auto, 1fr, auto, auto),
    stroke: 0.5pt + luma(180),
    inset: 6pt,
    table.header(
      [],
      text(weight: "bold")[Signal],
      text(weight: "bold")[Est. weight],
      text(weight: "bold")[Source],
    ),
    table.hline(),
    text(weight: "bold")[1], [*Follow author* --- user follows you from this post], [$tilde$30$times$], inf-tag,
    text(weight: "bold")[2], [*Share via DM* --- user sends your post in a direct message], [$tilde$25$times$], inf-tag,
    text(weight: "bold")[3], [*Reply* --- user replies to your post], [$tilde$20$times$], emp-tag,
    text(weight: "bold")[4], [*Share via copy link* --- user copies the URL to share elsewhere], [$tilde$20$times$], inf-tag,
    text(weight: "bold")[5], [*Quote tweet* --- user quotes your post with commentary], [$tilde$18$times$], emp-tag,
    text(weight: "bold")[6], [*Profile click* --- user clicks your name or avatar], [$tilde$12$times$], emp-tag,
    text(weight: "bold")[7], [*Click* --- user clicks into the full conversation], [$tilde$10$times$], emp-tag,
    text(weight: "bold")[8], [*Share (generic)* --- user opens the share menu], [$tilde$10$times$], inf-tag,
    text(weight: "bold")[9], [*Dwell* --- user pauses on your post (binary)], [$tilde$8$times$], emp-tag,
    text(weight: "bold")[10], [*Video quality view* --- user watches your video past a threshold], [$tilde$3$times$], code-tag,
    text(weight: "bold")[11], [*Retweet* --- user reposts without commentary], [$tilde$3$times$], emp-tag,
    text(weight: "bold")[12], [*Photo expand* --- user taps to see full image], [$tilde$2$times$], inf-tag,
    text(weight: "bold")[13], [*Favourite (like)* --- baseline], [1$times$], emp-tag,
    text(weight: "bold")[14], [*Dwell time* --- how long the user pauses (continuous, in seconds)], [$tilde$0.1/s], code-tag,
    text(weight: "bold")[15], [*Quoted click* --- user clicks into the original from a quote], [$tilde$4$times$], inf-tag,
    table.hline(),
    text(weight: "bold")[16], [*Not interested* --- user taps "show less"], [$tilde minus$20$times$], inf-tag,
    text(weight: "bold")[17], [*Mute author* --- user mutes you], [$tilde minus$40$times$], inf-tag,
    text(weight: "bold")[18], [*Block author* --- user blocks you], [$tilde minus$74$times$], inf-tag,
    text(weight: "bold")[19], [*Report* --- user reports your post], [$tilde minus$369$times$], inf-tag,
  ),
  caption: [All 19 scoring signals from `weighted_scorer.rs`, ranked by estimated relative weight. Favourite (like) = 1#sym.times baseline. Signals 1--15 are positive; 16--19 are negative. True weight values are in the unpublished `params.rs` module.],
) <tab:signals>

== Key observations

*Likes are the weakest positive signal.* Most growth advice focuses on likes. The algorithm barely values them. A like is the lowest-effort action a user can take, and its weight reflects that.

*Shares are probably the most underrated signals.* DM shares, copy-link shares, and generic shares are three separate dedicated signals---new in 2026. The `share_via_dm` signal fires when *other users* send your post via DM to someone---it is the highest-conviction sharing action (personally vouching). Create content people want to privately share: insider data, useful tools, niche insights #inf-tag.

*Follows-from-post are the ultimate signal.* If your content causes someone to follow you, that post receives the highest positive weighting. This rewards genuinely novel or valuable content from accounts people have not seen before #inf-tag.

*Negative signals are predictive, not reactive.* The Grok transformer predicts the _probability_ that a user would block, mute, or report your content---and penalises your post _before anyone acts_. Content that the model expects to provoke negative reactions is suppressed pre-emptively #code-tag.

*Negative compression is asymmetric.* Positive scores scale linearly. Negative scores are compressed into a bounded band near zero. This means even moderate negative predictions can kill a post, while positive signals stack without limit #code-tag.

*Bookmarks are not a signal.* They are not among the 19 signals in the scorer. The widely circulated claim that bookmarks carry high weight is incorrect #code-tag.

// ════════════════════════════════════════════
//  3. CONTENT STRATEGY
// ════════════════════════════════════════════

= What to Post (and How)

== Text

Text posts have the highest average engagement rate on X at 0.48%, compared to 0.41% for images and video #emp-tag. They require no production overhead, enabling higher posting frequency with quality. The scoring formula has no format-specific bonus for text---it simply tends to generate more replies and dwell time #inf-tag.

*Structure for maximum impact:*
- Open with a strong first line (visible without expanding).
- Use line breaks for scannability---this increases dwell time.
- End with a question or provocative claim to drive replies.

== Images

The `photo_expand_score` signal fires when a user taps to see the full image #code-tag. Design images that demand expansion:
- Infographics with text too small to read in the feed.
- Charts and data visualisations from papers or research.
- Screenshots that are partially cropped to force a tap.

Native image uploads see up to 40% more engagement than linked images #emp-tag.

== Threads

Threads maximise the continuous `dwell_time` signal---the only non-probability input to the scorer, measured in seconds #code-tag. A 5-tweet thread where someone reads all 5 generates substantially more dwell signal than a single tweet. Threads average 3#sym.times more engagement than single tweets #emp-tag.

== Video

Videos must exceed a minimum duration threshold (`MIN_VIDEO_DURATION_MS`, value not published) to qualify for the `vqv_score` (video quality view) signal. Videos shorter than this threshold receive zero contribution from VQV #code-tag. Aim for 15--60 seconds minimum.

== Posting frequency and spacing

The author diversity decay means each successive post from you in the same feed session gets `decay`#super[_n_] of its natural score. *Space posts at least 2 hours apart* to avoid cannibalisation #code-tag. A rhythm of 3--5 posts per day, well spaced, outperforms 10 posts dumped in quick succession.

== What not to post

#figure(
  table(
    columns: (1fr, 1fr, auto),
    stroke: 0.5pt + luma(180),
    inset: 6pt,
    table.header(
      text(weight: "bold")[Behaviour],
      text(weight: "bold")[Why it hurts],
      text(weight: "bold")[Source],
    ),
    [Off-topic content], [Elevates P(not interested) prediction], inf-tag,
    [Engagement bait ("Like if you agree!")], [Trained users ignore or mute; elevates P(mute)], inf-tag,
    [Combative or aggressive tone], [Grok predicts higher P(block), P(mute) even with high engagement], emp-tag,
    [Spam-like patterns (copy-pasted replies)], [Triggers P(report); may activate safety filters], emp-tag,
    [5+ hashtags], [Associated with spam; 40% engagement reduction observed], emp-tag,
    [Posting 5+ times in 1 hour], [Author diversity decay: 5th post gets decay#super[4] of score], code-tag,
  ),
  caption: [Behaviours that trigger negative signals or scoring penalties.],
)

// ════════════════════════════════════════════
//  4. GROWING FROM A SMALL OR DORMANT ACCOUNT
// ════════════════════════════════════════════

= Growing from a Small or Dormant Account

This section addresses a specific scenario: an account with fewer than 150 followers, dormant or low-activity, now trying to grow.

== The cold start problem

The Grok transformer requires engagement history as input. The model takes your last 128 interactions and uses them to understand your interests and predict what you would engage with. If your engagement history is empty, the query hydrator returns an error and the scoring pipeline short-circuits #code-tag:

```rust
if thrift_user_actions.is_empty() {
    return Err(format!("No user actions found for user {}", user_id));
}
```

*This means step zero is using X actively*---liking, replying, reposting---for at least 1--2 weeks before expecting any organic reach from original content.

== Phase 1: Build your engagement history (Days 1--14)

*Daily time: 45--60 minutes.*

#set par(first-line-indent: 0em)

*Morning (20 min):*
- Like 20--30 posts in your niche. Each like enters `history_actions` and teaches the retrieval model your topics #code-tag.
- Reply to 5--10 posts from accounts with 1K--50K followers, targeting posts under 30 minutes old #emp-tag.
- Quote 2--3 posts with added context. Quote tweets are a separate signal from retweets #code-tag.

*Midday (15 min):*
- Post 1--2 original tweets (text-only at this stage).
- Reply to every response you receive within 30 minutes.

*Evening (10 min):*
- Create content people want to privately share. The `share_via_dm` signal fires when *others* DM-share *your* post---not when you DM someone else's. Insider data, useful tools, niche insights drive this #code-tag.
- Follow 5--10 relevant accounts. Your following list determines what Thunder serves as in-network candidates, shaping your own engagement history #code-tag.

#set par(first-line-indent: 1.2em)

== Phase 2: Establish a rhythm (Days 15--60)

With engagement history populated, the transformer can score your content.

- Increase to 3--5 original posts per day, spaced $gt.eq$ 2 hours apart #code-tag.
- Maintain a 70/30 split: 70% engaging with others, 30% original content #emp-tag.
- Add 1 thread per week (5--7 tweets) for dwell time #code-tag.
- Add 1 image post per day designed for tap-to-expand #code-tag.

== Phase 3: Compound (Days 60--180)

- 5--7 posts per day if quality is maintained.
- 1--2 video posts per week exceeding the minimum duration for VQV scoring #code-tag.
- Actively seek quote-post opportunities on larger accounts.
- Share valuable posts via DM consistently---the most underrated lever in the algorithm.

== The reply strategy in detail

Replies are estimated at $tilde$20#sym.times the weight of a like. They are the single highest-impact action available to a small account for three reasons:

+ *Algorithmic weight.* A reply fires `ServerTweetReply`, one of the highest-weighted positive signals.
+ *Visibility.* Your reply appears in the thread below the original post, exposing you to the original author's audience.
+ *Profile clicks.* Readers who find your reply valuable click your profile, firing `profile_click_score` ($tilde$12#sym.times) for your other content.

*What makes a good reply:* Add information, cite data, offer a contrarian perspective, or share relevant experience. Never write "great post" or emoji-only responses---these generate zero engagement and teach the model that your content is low-value #inf-tag.

*Target selection:* Accounts with 1K--50K followers in your niche. Large enough to have active threads, small enough to notice you. Avoid accounts with 500K+ followers---your reply will be buried #emp-tag.

// ════════════════════════════════════════════
//  5. PREMIUM
// ════════════════════════════════════════════

= Premium Subscription

The Premium boost is not present in the 2026 recommendation source code #code-tag. It likely operates at a different layer of the stack. However, empirical data consistently reports substantial reach advantages #emp-tag:

- Premium accounts average $tilde$600 impressions per post vs. significantly fewer for free accounts ($tilde$10#sym.times advantage).
- Premium+ accounts average $tilde$1,550 impressions per post.

*When to subscribe:* Not on Day 1. The boost is multiplicative---it amplifies your existing score. If your engagement history is empty and your content generates no engagement, multiplying zero is still zero. Subscribe at Day 21--30, once you are posting consistently and receiving measurable engagement #inf-tag.

Choose Premium (\$8/month) initially, not Premium+ (\$16/month). The incremental benefit matters more at higher follower counts where the reach differential compounds.

// ════════════════════════════════════════════
//  6. NEGATIVE SIGNALS
// ════════════════════════════════════════════

= How the Algorithm Punishes Content

== Predictive, not reactive

The four negative signals---not interested, mute, block, report---are _predictions_. The Grok transformer estimates the probability that a user would take these actions and penalises your post before anyone actually does #code-tag.

Your historical blocks and mutes become training data. The model generalises: if users who engage with your niche topic tend to mute your posts, the model will suppress your content for the entire niche-interested audience segment #inf-tag.

== Asymmetric compression

Positive scores scale linearly. Negative scores are compressed into a bounded band near zero by the `offset_score()` function #code-tag. This means:

- A post with strong positive signals and a few negative signals survives---the positive dominates.
- A post with even moderate negative signals and weak positive signals enters compression and is effectively killed.
- There is a floor---mass-reporting cannot drive a score to negative infinity. But the floor is near zero, which is functionally invisible.

== The penalty hierarchy

#figure(
  table(
    columns: (auto, 1fr, auto),
    stroke: 0.5pt + luma(180),
    inset: 6pt,
    table.header(
      text(weight: "bold")[Rank],
      text(weight: "bold")[Penalty],
      text(weight: "bold")[Recovery],
    ),
    [1], [Safety filter drop (spam, violence, policy)], [Irreversible for that post],
    [2], [Blocked/muted by viewer (hard filter---removed before scoring)], [Unblock/unmute by viewer],
    [3], [High P(report) prediction], [Model must relearn from positive signals],
    [4], [High P(block) prediction], [Same],
    [5], [High P(mute) prediction], [Same],
    [6], [High P(not interested) prediction], [Same],
    [7], [Out-of-network discount factor], [Viewer follows you],
    [8], [Author diversity decay], [Resets each feed session],
    [9], [Rate-limit shadowban (>100 likes/hr, follow cycling)], [48 hours to 3 months],
  ),
  caption: [Penalty mechanisms ranked by severity. Predictions (ranks 3--6) are persistent and require sustained positive engagement to reverse.],
)

// ════════════════════════════════════════════
//  7. MILESTONES
// ════════════════════════════════════════════

= Expected Growth Timeline

#figure(
  table(
    columns: (1fr, auto, 1fr),
    stroke: 0.5pt + luma(180),
    inset: 6pt,
    table.header(
      text(weight: "bold")[Milestone],
      text(weight: "bold")[Timeline],
      text(weight: "bold")[What it means],
    ),
    [Engagement history populated], [Day 7--14], [The algorithm can now score your content],
    [For You feed shows your niche], [Day 7--10], [Retrieval model has learned your interests],
    [First meaningful reply exchange], [Day 2--3], [Engagement edges forming],
    [Subscribe to Premium], [Day 21--30], [$tilde$10#sym.times reach amplification],
    [250 followers], [Week 3--4], [Meaningful in-network audience],
    [Post exceeding 1,000 impressions], [Week 3--4], [Out-of-network retrieval working],
    [500 followers], [Month 2--3], [Compounding growth begins],
    [First viral post (10,000+ impressions)], [Month 2--3], [You are established in the retrieval model's embedding space],
    [1,000 followers], [Month 4--6], [Self-sustaining growth],
  ),
  caption: [Expected milestones for a dormant account ($tilde$100 followers) following the described strategy.],
)

// ════════════════════════════════════════════
//  8. DAILY CHECKLIST
// ════════════════════════════════════════════

= Daily Checklist

#block(width: 100%, stroke: 0.5pt + luma(180), radius: 3pt, inset: 14pt)[
  #set par(first-line-indent: 0em)
  #set text(9.5pt)

  #text(weight: "bold")[Morning (20 min)]
  - Like 20--30 niche posts (builds engagement history)
  - Reply to 5--10 posts from larger accounts (target posts < 30 min old)
  - Quote 2--3 valuable posts with added context

  #v(6pt)
  #text(weight: "bold")[Midday (20 min)]
  - Post 1--2 original posts (spaced $gt.eq$ 2 hours from each other)
  - Reply to all replies on your content within 30 minutes
  - Create content worth DM-sharing (the signal fires when others share YOUR post)

  #v(6pt)
  #text(weight: "bold")[Evening (10 min)]
  - Post 1 more original post or start a thread segment
  - Check metrics: which posts generated profile clicks? Double down on that format.
  - Follow 3--5 new relevant accounts

  #v(6pt)
  #text(weight: "bold")[Weekly]
  - 1 thread (5--7 tweets)
  - 1 image post designed for tap-to-expand
  - Review: which content types drove the most engagement?
  - Unfollow accounts that pollute your engagement history with off-topic content
]

// ════════════════════════════════════════════
//  REFERENCES
// ════════════════════════════════════════════

#heading(numbering: none)[References]

#set par(first-line-indent: 0em)
#set text(9pt)

#block(inset: (left: 1.5em))[
  + xAI Corp. _x-algorithm_. GitHub, Apache 2.0, January 20, 2026. `github.com/xai-org/x-algorithm`

  + Buffer Research. "Does X Premium Really Boost Your Reach? We Analyzed 18.8 Million Posts." 2025.

  + PostEverywhere. "How the X/Twitter Algorithm Works in 2026 (From the Source Code)." 2026.

  + Tweet Archivist. "Complete Technical Breakdown: How the X Algorithm Works." 2025--2026.

  + Social Media Today. "X Reveals Key Signals for Post Reach." 2025.

  + Pixelscan. "Twitter Shadowban: Causes, Detection & Fixes (2026 Guide)." 2026.

  + Circleboom. "The Hidden X Algorithm: TweepCred, Shadow Hierarchy, and Dwell Time." 2025.

  + Tomorrow's Publisher. "X Softens Stance on External Links." October 2025.

  + TechCrunch. "X open sources its algorithm while facing a transparency fine." January 2026.
]