Illustration for "How to Search Reddit with TypeScript"

How to Search Reddit with TypeScript

Every example here runs as-is against the live API in Node or on the edge: no SDK install, your first search, a pagination loop that doesn't drop results, and a scheduled poll for ongoing monitoring.

Setup

Sign up — no card required — for 1,000 free Reddit searches, then create a key from /developer. Nothing to install — fetch is global in Node 18+ and every modern runtime. Set the key as an environment variable rather than pasting it into your source:

terminal
export THREADSNOOP_API_KEY=your_key_here

Your first search

Every call sends your key as x-api-key and returns Reddit's own field names untouched, wrapped in a small envelope:

search.ts
const API_KEY = process.env.THREADSNOOP_API_KEY!;
const BASE = "https://api.threadsnoop.com/v1";

type ThreadSnoopEnvelope<T> = {
  data: T[];
  _threadsnoop: { cursor: string | null; has_more: boolean; credits_used: number; credits_remaining: number };
};

async function searchPosts(subreddit: string, q: string) {
  const params = new URLSearchParams({ subreddit, q, limit: "25" });
  const res = await fetch(`${BASE}/posts?${params}`, {
    headers: { "x-api-key": API_KEY },
  });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  const body: ThreadSnoopEnvelope<{ title: string; permalink: string }> = await res.json();
  return body;
}

const { data } = await searchPosts("SaaS", "burnout");
for (const post of data) console.log(post.title, post.permalink);

q= is an exact-phrase filter (with basic plural flexing) applied server-side, so data only contains matches — no client-side filtering needed for a simple keyword search. Reads are $1.50 per 1,000 (as low as $1.00/1,000 in bulk), 1 credit per request regardless of how many results come back.

Paginating through every result

_threadsnoop carries a cursor and has_more. Pass the cursor back as after= when sorting oldest-first, or before= for the default newest-first order, and stop at has_more: false:

search.ts
async function searchAll(subreddit: string, q: string) {
  const results: { title: string; permalink: string }[] = [];
  let cursor: string | null = null;

  while (true) {
    const params = new URLSearchParams({ subreddit, q, limit: "100", sort: "desc" });
    if (cursor) params.set("before", cursor);

    const res = await fetch(`${BASE}/posts?${params}`, {
      headers: { "x-api-key": API_KEY },
    });
    if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
    const body: ThreadSnoopEnvelope<{ title: string; permalink: string }> = await res.json();

    results.push(...body.data);
    if (!body._threadsnoop.has_more) break;
    cursor = body._threadsnoop.cursor;
  }
  return results;
}

Ask for limit=100 — fewer results per page just means more pages, and every page costs 1 credit regardless of size. A page with zero matches for your q= still costs 1 credit, since it's one upstream read either way; scope a rare phrase with after=/before= first rather than paginating a year of history blind.

Searching comments too

Comments outnumber posts roughly 10 to 1 on most subreddits, and the same pattern works on /v1/comments:

search.ts
const params = new URLSearchParams({ subreddit: "freelance", q: "invoice", limit: "100" });
const res = await fetch(`${BASE}/comments?${params}`, { headers: { "x-api-key": API_KEY } });
const { data: comments } = await res.json();

Handling rate limits

Every key gets the same limit — 2 requests per second sustained, burst of 10 — regardless of plan. A 429 response includes a Retry-After header; respect it rather than guessing:

search.ts
async function fetchWithRetry(url: string, maxRetries = 3): Promise<Response> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const res = await fetch(url, { headers: { "x-api-key": API_KEY } });
    if (res.status === 429) {
      const wait = Number(res.headers.get("Retry-After") ?? "2");
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
    return res;
  }
  throw new Error("gave up after retries");
}

Check your balance and limit any time with GET /v1/account — it's free and doesn't cost a credit.

Monitoring a subreddit on a schedule

There's no webhook — this is a pull API — so ongoing monitoring means calling on a schedule (a cron job, a scheduled serverless function, whatever your stack already runs) and tracking the newest post you've already seen:

poll.ts
import { readFileSync, writeFileSync, existsSync } from "node:fs";

const STATE_FILE = "last_seen.json";

async function pollNew(subreddit: string) {
  const lastSeen = existsSync(STATE_FILE)
    ? (JSON.parse(readFileSync(STATE_FILE, "utf8")).lastId as string)
    : null;

  const params = new URLSearchParams({ subreddit, sort: "desc", limit: "100" });
  const res = await fetch(`${BASE}/posts?${params}`, { headers: { "x-api-key": API_KEY } });
  const { data: posts }: ThreadSnoopEnvelope<{ id: string }> = await res.json();

  const newPosts = [];
  for (const post of posts) {
    if (post.id === lastSeen) break;
    newPosts.push(post);
  }

  if (posts.length) writeFileSync(STATE_FILE, JSON.stringify({ lastId: posts[0].id }));
  return newPosts;
}

Fresh posts (under roughly 36 hours old) carry a placeholder score and comment count — score: 1, num_comments: 0 — until Reddit finalizes them. For a real comment count on something that fresh, call /v1/comments/tree with the post's id instead.

Full endpoint reference, response shapes, and the billing model in more depth: the practical API guide. Building the same thing in Python instead? search Reddit with Python. Want an LLM in the loop instead of writing the filtering logic yourself? using AI agents to search Reddit.

Frequently asked questions

Do I need to install a package to search Reddit with TypeScript?

No — fetch is global in Node 18+ and every modern runtime, and every endpoint is plain REST returning Reddit's own field names. No client library to install or keep updated.

How do I avoid dropping results across pages?

Follow the cursor: each response's _threadsnoop.cursor and has_more tell you exactly where to resume. Pass the cursor back as before= (default newest-first sort) or after= (oldest-first) and stop once has_more is false, rather than guessing an offset.

How much does searching Reddit with TypeScript cost?

1 credit per request regardless of endpoint or how many results come back — $1.50 per 1,000 reads, as low as $1.00 per 1,000 in bulk. New keys start with 1,000 free reads.

How do I monitor a subreddit continuously in TypeScript?

There's no webhook — call on a schedule (a cron job or a scheduled serverless function is enough) with sort=desc, and stop once you hit the newest post ID you saw last time. Store that ID between runs so you never reprocess the same posts twice.

Start querying in the next five minutes

Sign up, grab your key, and run the first search example above. Your first 1,000 Reddit searches are already on your account.

1,000 free Reddit searches on signup, no card required.