Back to all articles
X's Algorithm Decoded: What Actually Gets Reach in 2026
AlgorithmGrowthTwitterTweepCred

X's Algorithm Decoded: What Actually Gets Reach in 2026

We went through X's open-source algorithm code line by line. Here's what we found about TweepCred, spam filters, and the hidden scoring system that determines if your tweets get seen or buried.

January 4, 202612 min read

In March 2023, X (formerly Twitter) open-sourced their recommendation algorithm on GitHub. Most people glanced at the headlines and moved on. We spent weeks reading the actual code.

What we found changes everything you think you know about growing on X.

Section 1: TweepCred - Your Hidden Reputation Score

Every account on X has a hidden reputation score called TweepCred. It ranges from 0 to 100, and it determines how much the algorithm trusts your content.

The code lives in Reputation.scala:

def scaledReputation(raw: Double): Byte = {
  // convert log(pagerank) to a number between 0 and 100
  val e: Double = 130d + 5.21 * scala.math.log(raw)
  val pos = scala.math.rint(e)
  val v = if (pos > 100) 100.0 else if (pos < 0) 0.0 else pos
  v.toByte
}

Your TweepCred is calculated using a PageRank-style algorithm. It factors in:

  • Who follows you (high TweepCred followers boost your score)
  • Who you interact with
  • Your account age and activity patterns
  • Engagement quality on your content
  • Here's the brutal part: if your TweepCred drops below 40, you're essentially invisible in Top Tweets.

    Section 2: The Following Ratio Penalty

    This is where most people unknowingly destroy their reach.

    The algorithm has specific thresholds hardcoded in Reputation.scala:

    private val threshAbsNumFriendsReps = 2500
    private val threshFriendsToFollowersRatioUMass = 0.6
    private val maxDivFactorReps = 50

    Translation:

  • If you follow more than 2,500 accounts AND
  • Your following/follower ratio exceeds 0.6
  • Your TweepCred gets divided by up to 50x.

    Let's say you have a TweepCred of 70. You go on a following spree and end up following 3,000 accounts with a 0.8 ratio. Your effective score could drop to 1.4.

    The function that does this:

    def adjustReputationsPostCalculation(mass: Double, numFollowers: Int, numFollowings: Int) = {
      if (numFollowings > threshAbsNumFriendsReps) {
        val friendsToFollowersRatio = (1.0 + numFollowings) / (1.0 + numFollowers)
        val divFactor = scala.math.exp(
          constantDivisionFactorGt_threshFriendsToFollowersRatioReps *
            (friendsToFollowersRatio - threshFriendsToFollowersRatioUMass) *
            scala.math.log(scala.math.log(numFollowings))
        )
        mass / ((divFactor min maxDivFactorReps) max 1.0)
      } else {
        mass
      }
    }

    This is why "follow for follow" strategies backfire. The algorithm literally penalizes accounts that look like they're gaming the system.

    Section 3: The Spam Filter That Kills Your Replies

    The SpamVectorScoringFunction.java file reveals how X filters spam:

    private static final int MIN_TWEEPCRED_WITH_LINK =
        EarlybirdConfig.getInt("min_tweepcred_with_non_whitelisted_link", 25);

    If your TweepCred is below 25 and you post a link (that isn't an image, video, or news), you get flagged as spam.

    The scoring logic:

    if (documentFeatures.isFlagSet(EarlybirdFieldConstant.HAS_LINK_FLAG)
        && !documentFeatures.isFlagSet(EarlybirdFieldConstant.HAS_IMAGE_URL_FLAG)
        && !documentFeatures.isFlagSet(EarlybirdFieldConstant.HAS_VIDEO_URL_FLAG)
        && !documentFeatures.isFlagSet(EarlybirdFieldConstant.HAS_NEWS_URL_FLAG)) {
      // Contains a non-media non-news link, definite spam vector.
      tweepCredThreshold = MIN_TWEEPCRED_WITH_LINK;
    }

    This means:

  • Links without images = spam signal
  • Links without videos = spam signal
  • Random URLs in replies = spam signal
  • Always pair links with media. The algorithm treats naked links as a red flag.

    Section 4: The Reply-Back Signal (This One Is Huge)

    Deep in RecapFeatures.scala, there's a feature that most people don't know about:

    val IS_REPLIED_REPLY_ENGAGED_BY_AUTHOR = new Binary(
      name("recap.engagement.is_replied_reply_engaged_by_author"),
      Set(EngagementsPrivate, EngagementsPublic).asJava)

    This tracks whether the original author engaged with your reply. If they:

  • Reply to your reply
  • Like your reply
  • Retweet your reply
  • Your content gets a significant boost. The algorithm is literally looking for this signal.

    The related features in the code:

    val IS_REPLIED_REPLY_FAVORITED_BY_AUTHOR = new Binary(...)
    val IS_REPLIED_REPLY_QUOTED_BY_AUTHOR = new Binary(...)
    val IS_REPLIED_REPLY_REPLIED_BY_AUTHOR = new Binary(...)
    val IS_REPLIED_REPLY_RETWEETED_BY_AUTHOR = new Binary(...)

    This is why generic replies like "Great post!" never perform. They don't trigger a response from the author. Write replies that start conversations.

    Section 5: Real Graph - Your Relationship Score

    The algorithm doesn't treat all engagement equally. It uses something called "Real Graph" to weight interactions based on relationship strength.

    From RecapFeatures.scala:

    val REAL_GRAPH_WEIGHT =
      new Continuous("recap.tweetfeature.real_graph_weight", Set(UsersRealGraphScore).asJava)

    This means:

  • 10 likes from strangers < 2 likes from your close connections
  • Engagement from accounts you regularly interact with counts MORE
  • Building genuine relationships > farming random engagement
  • The algorithm tracks:

  • Who you reply to consistently
  • Who replies to you
  • Mutual interactions over time
  • This is why engagement pods eventually stop working. The algorithm detects when engagement comes from outside your natural graph.

    Section 6: The Heavy Ranker Model Weights

    X uses a machine learning model called the "Heavy Ranker" to score every tweet. The model predicts multiple engagement types and weights them.

    From HomeGlobalParams.scala:

    object ModelWeights {
      object FavParam extends FSBoundedParam[Double](
        name = "home_mixer_model_weight_fav", ...)
      object RetweetParam extends FSBoundedParam[Double](
        name = "home_mixer_model_weight_retweet", ...)
      object ReplyParam extends FSBoundedParam[Double](
        name = "home_mixer_model_weight_reply", ...)
      object ReplyEngagedByAuthorParam extends FSBoundedParam[Double](
        name = "home_mixer_model_weight_reply_engaged_by_author", ...)
      object BookmarkParam extends FSBoundedParam[Double](
        name = "home_mixer_model_weight_bookmark", ...)
      object ShareParam extends FSBoundedParam[Double](
        name = "home_mixer_model_weight_share", ...)
    }

    The algorithm predicts the probability of:

  • Favorites (likes)
  • Retweets
  • Replies
  • Reply-backs from authors
  • Bookmarks
  • Shares
  • Each prediction is multiplied by its weight, and the sum becomes your tweet's score.

    Negative signals also have weights:

    object NegativeFeedbackV2Param extends FSBoundedParam[Double](
      name = "home_mixer_model_weight_negative_feedback_v2", ...)
    object ReportParam extends FSBoundedParam[Double](
      name = "home_mixer_model_weight_report",
      min = -20000.0, max = 0.0)

    Reports carry massive negative weight (up to -20,000). A few reports can tank your visibility.

    Section 7: Dwell Time - The Silent Ranking Factor

    The algorithm tracks how long people look at your content:

    val IS_DWELLED_1S = new Binary(name("recap.engagement.is_dwelled_1s"), ...)
    val IS_DWELLED_2S = new Binary(name("recap.engagement.is_dwelled_2s"), ...)
    // ... up to 10 seconds
    val IS_TWEET_DETAIL_DWELLED_8_SEC = new Binary(...)
    val IS_TWEET_DETAIL_DWELLED_15_SEC = new Binary(...)
    val IS_TWEET_DETAIL_DWELLED_25_SEC = new Binary(...)
    val IS_TWEET_DETAIL_DWELLED_30_SEC = new Binary(...)

    If people scroll past your tweet in under 1 second, that's a negative signal. If they stop and read for 8+ seconds, that's a strong positive signal.

    This is why:

  • Hooks matter (stop the scroll)
  • Formatting matters (line breaks = easier to read = longer dwell)
  • Substance matters (empty content gets skipped)
  • Section 8: What This Means For Your Strategy

    Based on the actual code, here's what works:

  • Keep your following ratio under 0.6 - Don't follow more than 60% of your follower count, especially if you're over 2,500 following
  • Never post naked links - Always include an image or video when sharing URLs
  • Write replies that get responses - The IS_REPLIED_REPLY_ENGAGED_BY_AUTHOR signal is real. Ask questions, add takes, spark conversation
  • Build genuine connections - Real Graph weight means consistent interactions with the same accounts matter more than spreading yourself thin
  • Optimize for dwell time - Use hooks, formatting, and substance to keep people reading
  • Avoid negative signals - Spam patterns, reports, and "don't like" clicks carry heavy negative weights
  • The algorithm isn't random. It's a machine learning model with specific features and weights. Now you know what those features are.

    How Quickly Uses This

    We built Quickly to analyze your replies against these exact signals before you post. Our A-F grading system checks:

  • Spam trigger patterns
  • Reply-back probability
  • Content quality signals
  • Algorithm optimization opportunities
  • Instead of guessing, you'll know if your reply will get seen or buried.

    The algorithm is public. The playbook is in the code. Now it's just about execution.

    Share this article

    Ready to Sound Like Yourself?

    Join the waitlist for early access to Quickly.