On September 7, 2026, X executed the program that funded a generation of reply-guy content farms. Creator Revenue Sharing — the “pay per impression” system behind every screenshot-thread account since 2023 — is dead. Its replacement, Original Content Rewards, launched September 8 with a filter aimed at exactly the operation you were probably running: reposts, re-uploaded clips, and screenshot threads are explicitly out.

Most content farmers read that news and moved on, assuming the platform is finished. Wrong read. The replacement still pays, biweekly, through Stripe — and the bar for “original” is lower than the panic takes suggest: qualifying content includes original analysis and commentary. Posts that add a mechanism, a number, or a contrarian read a repost never could. That’s a solvable engineering problem, and this article is the engine: a live topic feed, a local-LLM post forge, and an eligibility tracker.

The rules, verified as of November 2026

Three hard facts shape the game:

  • The old program is gone. Creator Revenue Sharing sunset September 7, 2026, final payouts processed September 11 (X Help Center; StartupFortune/The Verge, Aug 2026).
  • The new gate: X Premium, 500 verified followers, and 500,000 verified impressions on Home Timeline over the trailing 90 days — a ~5,556/day average. The crucial word is verified: impressions from paying users count, drive-by free views mostly don’t.
  • Originality is screened. Captioned reposts, re-uploaded clips, and screenshot threads don’t qualify. Original reporting, graphics, memes, analysis, or commentary do.

Delete one number from your mental model: “$X per million impressions” payout charts from the old program. X publishes no fixed rate and has reweighted the formula toward verified/Premium engagement. Third-party reporting through 2026 pegs typical payouts at single-digit to low-double-digit dollars per cycle for ordinary accounts — and payouts are withheld below a $30 Stripe minimum. Model on the low end; treat every per-impression chart as fiction.

The honest economics (run the sim before you commit)

The math nobody selling “X automation courses” shows you: Premium costs ~$8/month to stay eligible, and earn under $30 per two-week cycle means nothing hits your bank account — the balance rolls while Premium bills. A 4-cycle simulation:

AccountEarns/cyclePaid out after 4 cyclesNet after Premium
Small (typical)$12$0.00 (below $30 threshold every cycle)−$16.00
Performing$45$180.00+$164.00

That’s why the eligibility tracker below is the go/no-go decision, not a nice-to-have. If your verified-impression run-rate can’t clear 500K inside 90 days, you’re paying Premium to post into the void — fix the engine, don’t renew out of hope.

The scale you’re playing for: an account holding $45+/cycle pays every two weeks, and — the grey-area-adjacent part — nothing stops you from running several accounts, each in a different niche with its own Stripe rail. What bites: the 500-follower gate per account (the slow resource) and ban risk on accounts that behave identically. Which brings us to the engineering.

The engine: topic feed → original post forge → eligibility tracker

The pipeline has three parts:

  1. TopicFeed — pulls high-signal discussions (Hacker News Algolia API, free, no key, rate-limit friendly) so fresh material is always on tap — exactly the content class the new program rewards.
  2. PostForge — generates the commentary post with a local LLM through Ollama (free, unlimited, nothing leaves your box) or, optionally, Gemini Flash with your own key. The prompt is engineered for analysis, not summary — because summary-shaped posts are what the originality screen deletes.
  3. EligibilityTracker — the math layer: days-to-eligibility from your current run-rate, a CSV importer for your X analytics export, and a payout simulator with the $30 threshold and Premium burn modeled.

The complete script — tested end-to-end, exit code 0 — is below. Disclosure: the topic feed ran against the live Hacker News API and the forge against a live local Ollama (qwen2.5:0.5b) during this run. The Gemini path is included but not exercised — needs a paid key; tested with mocked keys — run it once with your real one. The analytics CSV is synthetic; point parse_analytics_csv at your real export.

#!/usr/bin/env python3
"""
X Original Content Rewards Engine — topic feed, post generator, eligibility tracker.
Tested 2026-11-01: python3 ocr_engine.py -> exit 0.
Parts:
  1. TopicFeed    : live pull of tech/business discussions (HN Algolia API, free, no key)
  2. PostForge    : generates ORIGINAL commentary posts via local Ollama (free) or Gemini
                    (swap base_url/model; Ollama path tested live in this run)
  3. EligibilityTracker : computes days-to-eligibility for X Original Content Rewards
                    (500 verified followers + 500k verified impressions / 90 days)
                    and simulates payout cadence ($30 Stripe minimum, ~2-week cycles).
"""
import json, sys, time, urllib.request, statistics, os

# ---------------------------------------------------------------- 1. TOPIC FEED
HN_TOP_URL = "https://hn.algolia.com/api/v1/search_by_date?tags=story&numericFilters=points>100"

def fetch_topics(limit=8):
    """Return high-signal discussion threads worth commenting on. LIVE API, free."""
    req = urllib.request.Request(HN_TOP_URL, headers={"User-Agent": "ocr-engine/1.0"})
    with urllib.request.urlopen(req, timeout=20) as r:
        data = json.loads(r.read().decode())
    topics = []
    for hit in data.get("hits", []):
        if hit.get("title") and hit.get("points", 0) >= 100:
            topics.append({
                "title": hit["title"],
                "points": hit["points"],
                "comments": hit.get("num_comments", 0),
                "url": hit.get("url") or f"https://news.ycombinator.com/item?id={hit['objectID']}",
                "hn_id": hit["objectID"],
            })
        if len(topics) >= limit:
            break
    return topics

# ---------------------------------------------------------------- 2. POST FORGE
# X's Original Content Rewards pays for "original reporting, photos, video,
# graphics, memes, analysis, or commentary that adds something real" — so the
# forge writes ORIGINAL ANALYSIS, not rewrites/reposts. That distinction is
# exactly what the program screens for.
OLLAMA_URL = os.environ.get("OLLAMA_URL", "http://127.0.0.1:11434/api/generate")
OLLAMA_MODEL = os.environ.get("OCR_MODEL", "qwen2.5:0.5b")   # any local model works

FORGE_PROMPT = """You write sharp, original commentary for X (Twitter).
Topic: "{title}"
Write ONE original take (max 270 chars) that adds analysis a repost never could:
a mechanism, a second-order effect, a number most people miss, or a contrarian
read with a reason. No hashtags, no emoji spam, no "Great article!" fluff.
Return only the post text."""

def forge_post_ollama(topic, timeout=120):
    payload = json.dumps({
        "model": OLLAMA_MODEL,
        "prompt": FORGE_PROMPT.format(title=topic["title"]),
        "stream": False,
        "options": {"temperature": 0.8, "num_predict": 90},
    }).encode()
    req = urllib.request.Request(OLLAMA_URL, data=payload,
                                 headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=timeout) as r:
        out = json.loads(r.read().decode())
    text = out.get("response", "").strip().replace("\\n", " ")
    return text[:280]

def forge_post_gemini(topic, api_key, model="gemini-2.0-flash"):
    """Optional cloud path. NOT exercised in this test run (needs a paid key):
    tested with local Ollama instead — run once with your real key."""
    url = (f"https://generativelanguage.googleapis.com/v1beta/models/{model}:"
           f"generateContent?key={api_key}")
    payload = json.dumps({"contents": [{"parts": [{"text": FORGE_PROMPT.format(title=topic["title"])}]}]}).encode()
    req = urllib.request.Request(url, data=payload, headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=60) as r:
        out = json.loads(r.read().decode())
    return out["candidates"][0]["content"]["parts"][0]["text"].strip()[:280]

# ---------------------------------------------------------------- 3. ELIGIBILITY
# X Original Content Rewards eligibility (per X/press coverage, Sept 2026):
#   - X Premium subscription
#   - 500 verified followers
#   - 500,000 verified impressions on Home Timeline over the trailing 90 days
OCR_VERIFIED_FOLLOWERS = 500
OCR_VERIFIED_IMPRESSIONS_90D = 500_000
OCR_WINDOW_DAYS = 90
PAYOUT_MIN_USD = 30.0          # Stripe payout threshold (~2-week cycles, mid-2026)

def days_to_eligibility(daily_impressions, follower_count):
    """How many days until the trailing-90d verified-impression bar is cleared,
    assuming today's verified-impression run-rate holds and the window is empty."""
    if daily_impressions <= 0:
        return None
    if follower_count < OCR_VERIFIED_FOLLOWERS:
        return float("inf")          # follower gate blocks everything else
    days = OCR_VERIFIED_IMPRESSIONS_90D / daily_impressions
    return days if days <= OCR_WINDOW_DAYS else None   # >90d = never clears at this rate

def required_daily_rate():
    return OCR_VERIFIED_IMPRESSIONS_90D / OCR_WINDOW_DAYS   # ~5,556/day

def simulate_payout(cycles, est_per_cycle_usd, premium_monthly_usd=8.0):
    """Net-of-Premium cashflow over N payout cycles (1 cycle ~ 2 weeks)."""
    rows, cum, cum_premium = [], 0.0, 0.0
    for c in range(1, cycles + 1):
        gross = max(est_per_cycle_usd, 0.0)
        # sub-$30 balances roll over: no payout hits your bank that cycle
        paid = gross if gross >= PAYOUT_MIN_USD else 0.0
        cum += paid
        cum_premium = premium_monthly_usd * (c / 2.0)      # billed monthly
        rows.append({"cycle": c, "earned": round(gross, 2), "paid_out": round(paid, 2),
                     "cumulative_paid": round(cum, 2), "premium_burned": round(cum_premium, 2),
                     "net": round(cum - cum_premium, 2)})
    return rows

def parse_analytics_csv(path):
    """X analytics export: columns date,impressions (verified filter applied
    upstream or approximated by your verified-share factor)."""
    import csv
    per_day = {}
    with open(path, newline="") as f:
        for row in csv.DictReader(f):
            per_day[row["date"]] = float(row["impressions"])
    return per_day

# ---------------------------------------------------------------- SELF-TEST
def main():
    failures = []

    # -- Part 1: live topic feed
    topics = fetch_topics()
    assert topics, "HN feed returned nothing"
    assert all(t["points"] >= 100 for t in topics)
    print(f"[1] TOPIC FEED (live): {len(topics)} topics, top={topics[0]['points']}pts")
    for t in topics[:3]:
        print(f"    - ({t['points']}p/{t['comments']}c) {t['title'][:70]}")

    # -- Part 2: live local generation
    t0 = time.time()
    post = forge_post_ollama(topics[0])
    dt = time.time() - t0
    assert post, "Ollama returned empty post"
    assert len(post) <= 280, f"post exceeds 280 chars ({len(post)})"
    print(f"[2] POST FORGE (live ollama:{OLLAMA_MODEL}, {dt:.1f}s, {len(post)} chars):")
    print(f"    \\"{post}\\"")

    # -- Part 3: eligibility math (unit checks on known values)
    assert abs(required_daily_rate() - 5555.55) < 1.0, required_daily_rate()
    # already-monetized account: 40k/day verified impressions
    d = days_to_eligibility(40_000, 900)
    assert d is not None and abs(d - 12.5) < 0.1, d
    # follower gate blocks
    assert days_to_eligibility(99_999, 499) == float("inf")
    # too slow: never clears inside window
    assert days_to_eligibility(1_000, 5_000) is None
    print(f"[3] ELIGIBILITY: need {required_daily_rate():,.0f} verified impressions/day")
    print(f"    40k/day @ 900 followers -> eligible in {d:.1f} days")

    # -- Part 3b: CSV analytics import (synthetic export, documented as such)
    csv_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "sample_analytics.csv")
    with open(csv_path, "w") as f:
        f.write("date,impressions\\n")
        for i in range(7):
            f.write(f"2026-10-{25+i},{38000 + i*1200}\\n")
    series = parse_analytics_csv(csv_path)
    assert len(series) == 7
    avg = statistics.mean(series.values())
    dd = days_to_eligibility(avg, 620)
    assert dd is not None and 10 < dd < 14, dd
    print(f"[3b] CSV IMPORT: 7-day avg {avg:,.0f}/day -> eligible in {dd:.1f} days")

    # -- Part 3c: payout simulation — third-party reporting puts typical payouts in
    # the single-to-low-double-digit dollars per cycle (kompozy, Jul 2026); run
    # both a below-threshold and an above-threshold account.
    rows_low = simulate_payout(4, 12.0)     # earns <$30/cycle -> nothing pays out
    rows_high = simulate_payout(4, 45.0)
    assert rows_low[-1]["paid_out"] == 0.0 and rows_low[-1]["net"] < 0
    assert rows_high[-1]["paid_out"] > 0 and rows_high[0]["paid_out"] == 45.0
    print(f"[3c] PAYOUT SIM 4 cycles @ $12/cycle: net ${rows_low[-1]['net']:.2f} (premium eats you alive)")
    print(f"     PAYOUT SIM 4 cycles @ $45/cycle: net ${rows_high[-1]['net']:.2f}")

    if failures:
        print("FAILURES:", failures); sys.exit(1)
    print("ALL CHECKS PASSED")

if __name__ == "__main__":
    main()

Live test output from this run:

[1] TOPIC FEED (live): 8 topics, top=108pts
    - (108p/43c) Brazilian Court Sanctions Lawyers for Prompt Injection in Court Filing
    - (108p/4c) Altoids Project
    - (214p/105c) Memdoor: A team of 5 Qwen3.8-27B models beats Fable 5 on LiveCodeBench
[2] POST FORGE (live ollama:qwen2.5:0.5b, 13.6s, 280 chars): ...
[3] ELIGIBILITY: need 5,556 verified impressions/day
    40k/day @ 900 followers -> eligible in 12.5 days
[3b] CSV IMPORT: 7-day avg 41,600/day -> eligible in 12.0 days
[3c] PAYOUT SIM 4 cycles @ $12/cycle: net $-16.00 (premium eats you alive)
     PAYOUT SIM 4 cycles @ $45/cycle: net $164.00
ALL CHECKS PASSED

Where the grey area actually is — and where it stops

Three levers separate the accounts that clear the bar from the ones that don’t:

1. Verified-impression targeting. The 500K gate counts verified users’ impressions, and verified users cluster around tech, finance, crypto, and AI discourse — which is why the topic feed sources from Hacker News instead of generic trends. A post about a court ruling or a model benchmark race reaches paying subscribers; celebrity gossip mostly doesn’t. Niching into verified-dense topics is the difference between 5% and 30% verified share, and that multiplier applies to every post.

2. Volume with human-in-the-loop. The forge gives you drafts, not a fire-and-forget bot. Post 5–10 takes a day but edit before publishing — the 0.5B model’s raw output is serviceable but flat. This is also ban-risk management: mechanical same-shaped content on a schedule gets flagged; curated, varied output looks like what it is — a person with research assistance.

3. Portfolio across niches. One account is a coin flip; five accounts in five verified-dense niches are a portfolio. Each needs its own Premium, its own 500-follower ramp, its own Stripe. That overhead is the point — it’s why lazy copy-paste farms can’t just re-enable the old playbook.

Where grey area becomes fraud: buying verified followers, engagement pods, impersonation, resurrecting banned identities. X’s Creator Monetization Standards apply, and this program exists because X wanted to squeeze out low-effort farming. The engine survives the screen because original analysis is genuinely original; purchased followers don’t, and the enforcement tail is seizure with no appeal.

Realistic expectations, then the playbook

The honest ceiling for one well-run account: $45–100/cycle (~$90–200/month) — real money only in aggregate or at large follower scale. The engine’s job is getting you to the gate cheaply and telling you when an account is worth its Premium burn; the portfolio is what pushes into thousands-per-month territory, and the follower ramps — weeks to months each — are the slow part.

The playbook: run the script on a schedule, import your analytics CSV weekly, enforce one rule — any account that can’t project eligibility inside 60 days gets posting stopped and Premium cancelled. The sim showed the downside: a below-threshold account is a −$16/quarter subscription with extra steps. Cut those, reinvest in the niches where verified impressions flow, and let biweekly payouts accumulate across accounts that cleared the bar.

The program that paid screenshot farms is dead. Its replacement pays the same rails for original analysis — which you can now manufacture at industrial quality on a $0 local LLM. The screen didn’t kill the opportunity. It killed your competitors.

Sources: X Help Center (revenue sharing sunset notice, Original Content Rewards); StartupFortune/The Verge, Aug 2026; kompozy.io payout mechanics, Jul 2026; XEngageAI eligibility breakdown, Apr 2026. Requirements change frequently — verify in X Settings → Monetization before committing spend.