Methodology
How Crux measures what works on your X account
Crux does not guess what works for you and does not average other people’s accounts. It measures your own posts, each against the posts you published around it, and reports a pattern only when chance is unlikely to explain it. This page is the whole method, with the numbers the code uses.
Updated
In short: each post’s engagement is divided by the median of the 20 posts nearest it in time, so follower growth does not decide the result. Posts are grouped by hook, format, topic, weekday and 3-hour slot; a group needs at least 5 posts. A group is reported only when its median falls outside the 5th to 95th percentile of 500 random draws of the same number of your posts: above it works for you, below it does worse than usual. Learnings put the posts you publish from Crux drafts through the same test.
The number each post is judged on
Every post needs one number to be judged on. Crux uses impressions where X provides them, which is for posts that come in through the read-only X import. X archive downloads carry likes and reposts for every post but no impressions, so an archive post is judged on likes plus 3 times its reposts, because a repost carries a post further than a like does.
engagement = impressions, when X provides them
engagement = likes + 3 × reposts, otherwiseThe two are different scales, so a post is only ever compared with posts measured the same way. Your reposts of other people’s posts are left out: they are someone else’s writing. Replies stay in, and are measured as a format of their own.
A baseline of its own neighbours
Reach grows with your follower count. Divide every post by your all-time average and you mostly measure how far into your growth a post landed: early posts all look like failures and recent ones like hits. So Crux divides each post by a local baseline instead: the median engagement of the 20 posts nearest it in time, on either side, measured the same way and not counting the post itself.
multiplier = this post’s engagement ÷ median(the 20 nearest posts)Follower count barely moves across 20 consecutive posts, which is what makes a multiplier comparable across years: 2× means twice your usual at the time, whether the post is from your first month or last week. A post with fewer than 5 comparable neighbours gets no baseline, and neither does one whose neighbours’ median is zero; both are left out rather than given an invented comparison.
A known weakness sits at the start of a history: the first posts’ neighbours mostly come later, when the account was bigger, so their multipliers read slightly low. At the edge of a history there is no unbiased choice.
Hooks, formats, topics and times
Posts are then grouped four ways, and each group gets a lift.
- Hooks: what the opening line does. Crux reads the first line and sorts it into one of 7 kinds: number or statistic, question, contrarian claim, announcement, list, first-person and plain statement. How each kind is recognised.
- Formats: reply or stand-alone post, with or without media, with or without a link, four length bands (under 100, 100 to 180, 180 to 260, and 260 or more characters), one block or several paragraphs, and whether the text contains a number written in digits.
- Topics: words and two-word phrases you keep coming back to. A term counts only when it appears in at least 5 posts and in at least 2% of all your posts, so one story told once is not a topic.
- Posting times: weekday, and 3-hour slot of the day. Each is divided by your overall median multiplier, so 1 means a typical post for you. The slots are read in the time zone you set in Settings.
lift = median(multipliers of the posts in the group)The median, never the mean: one post that went 40× would otherwise give a whole group a reputation its other posts never earned. A group with fewer than 5 posts is dropped, not shown with a warning. Each group that stays carries its size, and a confidence word that follows from it: low under 15 posts, moderate from 15, high from 40.
The test against chance
A lift on its own proves little. Draw a group of your posts at random and its median multiplier will rarely be exactly 1. So before a group is called a pattern, Crux asks how far from 1 a group of that size lands when there is no pattern at all. It takes every scored post’s multiplier, draws as many as the group holds at random, takes the median, and does that 500 times. The 5th and 95th percentiles of those 500 medians are the band chance produces.
band = 5th to 95th percentile of 500 medians of n random posts
above the band → works for you
below the band → does worse than usual
inside the band → not a findingThe direction matters: a group below the band is never read as something that works, whatever its lift. A group inside the band is kept with its numbers, but no part of Crux presents it as advice. The draws use a fixed seed, so two runs over the same archive give the same answer, and a band that could not separate anything (fewer than 10 scored posts, or fewer than 10 different medians across the draws) means no verdict at all.
When Crux says nothing
Some accounts cannot carry a multiplier. When 35% or more of your posts got no engagement, the baseline of many posts is itself zero, those posts drop out, and what is left speaks for a lucky minority while reading as if it spoke for the whole account. Crux then reports no patterns at all, and says why. The same goes for an account where every post scored the same, because there is no spread for a ratio to measure.
A worked example, on sample data
The figures below come from a made-up account run through the same code that reads real accounts, when this page was built. They are not anyone’s real posts.
The account: 180 posts from 3 November 2025 to 28 August 2026, measured on likes plus 3 × reposts, as an archive would be. 0% of its posts got no engagement, well under the 35% at which Crux reports nothing, so the account can carry a multiplier. Its reach grows over the period: the median of its first 20 posts is 50, of its last 20, 137. Compared on raw numbers, every early post would look weak.
One post, from 22 May 2026: “Why do onboarding emails get ignored?” got 181 likes and 17 reposts, so its engagement is 181 + 3 × 17 = 232. The 20 posts nearest it had a median of 111.5, so its multiplier is 2.08×: about 2.08 times what the account usually got at the time.
The account’s posts grouped by hook, each tested against chance:
| Hook | Posts | Lift | Chance band | Verdict |
|---|---|---|---|---|
| Question hook | 30 | 1.40× | 0.84× to 1.17× | Above the band: works for this account |
| Number / stat hook | 34 | 1.12× | 0.84× to 1.17× | Inside the band: not reported |
| First-person hook | 40 | 0.96× | 0.85× to 1.16× | Inside the band: not reported |
| List hook | 16 | 0.92× | 0.74× to 1.27× | Inside the band: not reported |
| Plain statement hook | 60 | 0.86× | 0.90× to 1.09× | Below the band: does worse than usual |
Question hooks cleared the band (1.40× against 1.17×), so Crux reports them as working for this account. Plain statement hooks fell below theirs (0.86× against 0.90×), so they are reported as doing worse than usual. Number / stat hooks sit at 1.12×, which looks like a win, but 34 posts drawn at random land there often enough that Crux does not report it.
How Learnings test what you ship
Crux writes drafts from the commits, merged pull requests and releases of the GitHub repos you watch, and from your coding agent’s notes. When it finds one of those drafts posted on your X timeline, it reads the post’s numbers at 1 hour, 24 hours and 7 days, and the post joins the rest of your history, measured exactly as above. Each of those posts also carries the kind of work it announced: launch, feature, fix, improvement, milestone or other. A note from your coding agent can name the kind itself; otherwise the model that writes the draft picks one.
Learnings then ask two questions of your posts:
- Which kinds of post work for you. Each kind with at least 5 posts gets a lift, the median multiplier of its posts, and is tested against the medians that random groups of the same size from all your scored posts produce. The kinds are tested together, so Crux corrects for testing several at once, as it does for topics: each kind gets an exact p-value for its median, and the Benjamini-Hochberg rule holds the expected share of false findings among the kinds to 10%.
- Whether posts from Crux drafts beat your usual post. One test: the median multiplier of your posts from Crux drafts that are at least 72 hours old, against the same 500-draw band of random groups from all your scored posts, posts from Crux drafts included. A younger post is still collecting numbers, and Crux’s posts are always your newest, so they wait.
On an account where most posts got no engagement there is no multiplier to test (see When Crux says nothing), so Learnings ask the same two questions of a simpler number: the share of posts that got at least one like or repost, compared with posts from the same month and of the same type, and corrected once for every comparison made.
lift(kind) = median(multipliers of your posts of that kind)
a kind is tested from 5 posts
kinds: Benjamini-Hochberg over all of them, false-discovery rate 10%
Crux drafts: posts at least 72 hours old, 5th to 95th percentile band of 500 drawsA kind, or Crux’s own drafts, that clears the test is reported like every other finding, as working for you or as doing worse than usual, and the next draft is written with it. Until then, Learnings show how many posts each kind has so far, and claim nothing.
How a draft is scored
A post draft you can still post carries a score in the composer, on the Drafts list and on an Inbox card once it is ready: a score out of 100 and at most three one-line reasons. Archived drafts and Articles are not scored. Code computes it, again on every keystroke; no AI model is asked. A draft starts at 75 and moves for two kinds of reason.
- General rules, the same checks the composer shows: over X’s length limit, or a [[placeholder]] left in, −40; a figure the AI wrote that you did not give, −20; a phrase on your banned list, −15; a link in the main post, −10, because X’s public ranking code weights opening a link at 0.2 and a reply at 5.0; a link or @handle the AI brought in, −10; a long post whose opening sentence runs past 280 characters, −10; a phrase from a hand-picked starter list of phrases often found in AI-written posts, −8; and a question, +5, because the same code weights a reply at 5.0 and a like at 0.5.
- Your own findings, only those that cleared the test against chance above, and only where the draft has the feature: its hook, a link, a number, its layout and length, a topic it mentions, the kind of work it came from, and posts from Crux drafts. A finding above the band adds 3 to 10 points, more for a larger multiple; one below takes as many away. A like-or-repost finding moves it 5. Together they move it at most 25; past that, each one’s points are scaled down in proportion, so the reasons still add up to the score. Each reason names the finding and how many posts it rests on. A finding inside the band moves nothing. The general link rule always applies, even when your own finding says posts with a link do well for you: the card then shows both, one taking points off and one adding them, and the score is their sum.
80 and over reads Strong, 55 to 79 OK, and under 55 Weak. When your account has no finding that has cleared the test yet, the score says so: “Scored on general rules only — your learnings aren't strong enough yet”. When it has some but none of them fits this draft, it says: “Scored on general rules only — none of your learnings apply to this draft”.
The points are Crux’s choice, not measured. The score ranks a draft against these rules and your own findings; it does not predict how many people will see or like the post.
What the method cannot tell you
- Why. A pattern says posts of one kind did better on your account, not that the kind caused it: a hook can look strong because of the topics you tend to use it for.
- The future. Everything here describes your history. What your audience rewards can change.
- Reach, for archive posts. Likes and reposts measure how people reacted, not how many saw the post; only imported posts carry impressions.
- A certainty. A band from the 5th to the 95th percentile means a group with no real effect still lands outside it about one time in ten. Across many groups, the odd finding will be luck; the larger the group, the less likely.
Related: how to download your X archive, the hook kinds, with examples, and how Crux compares with other X tools.