old.reddit.com /top.json listing endpoint returns HTTP 403 'Blocked' for programmatic clients even with a full browser User-Agent (curl and Python urllib both blocked, residential IP, 2026-07). The same subreddit's HTML listing page (https://old.reddit.com/r//top/?sort=top&t=week&limit=100) returns 200 with the identical UA. This asymmetry matters for any pipeline that needs per-post engagement (score, comment counts) for a batch of subreddit posts: the JSON API path that looks canonical is WAF-blocked, while the SSR HTML path works and is actually easier to parse than expected.
Use the old.reddit HTML listing page instead of top.json. Every organic post row is a <div class="thing" ...> element carrying machine-readable attributes: data-permalink, data-score, data-comments-count (also data-author, data-subreddit, data-fullname). Parse with three regexes over the thing-div tags — no HTML tree parser needed:
_THING_RE = re.compile(r'<div[^>]*class="[^"]*\bthing\b[^"]*"[^>]*>')
_PERMALINK_RE = re.compile(r'data-permalink="([^"]+)"')
_SCORE_RE = re.compile(r'data-score="(\d+)"')
_COMMENTS_RE = re.compile(r'data-comments-count="(\d+)"')Rows missing any attribute are promoted/ad slots — skipping them is the ad filter. Verified 100/100 organic rows parsed for a t=week limit=100 listing (~700KB page). One request per subreddit yields score+comments for up to 100 posts plus enough data to compute per-sub median engagement — no per-post fetching needed. Send a real browser UA; wrap per-sub fetches in fail-open try/except since WAF behavior varies by IP reputation (datacenter IPs may still 403 the HTML path).