Skip to main content
You already have a TypeScript app. Now it gets a comment triage pipeline. It pulls Reddit and YouTube comments about your topic from the ScrapeCreators API and asks Jev two questions per comment. Then the decision list says which comments to record, which need a human look, and which to ignore. Brand monitoring, like user research, shares the same problem: thousands of comments mention your product, but not all of them are useful. Most are irrelevant. Just reading through them all doesn’t scale. A general-purpose LLM could sort them. But it would return free text, which would need parsing, and no number, so you couldn’t threshold on it. Jev, TypeSafe’s System One model, returns typed judgments, a relevance probability 0 to 1, a choice of sentiment with a confidence score 0 to 1, and a per-request cost in dollars. That means code can branch on those numbers directly. Jev is cheap enough you can run it on every comment: $0.00057 for the captured run below, 27 comments, about $0.021 per 1,000. Plus 2 ScrapeCreators credits for the two fetch requests.
Prerequisites:
  • Your existing TypeScript app runs on Node.js or Bun as an ES module, so top-level await works.
  • You have @openrouter/sdk and zod installed, plus @types/node or @types/bun so process.env and fetch typecheck.
  • You’ve configured OPENROUTER_API_KEY and SCRAPECREATORS_API_KEY. ScrapeCreators accounts start with 100 free credits, so you can sign up at scrapecreators.com.
  • Keep the Decisions API reference and the ScrapeCreators API docs open.
Goal: Define an async triage function that takes comments from ScrapeCreators and returns one decision per comment with action: 'record' | 'review' | 'ignore', the reason, and Jev’s raw judgment. Outcome: Running triage on a batch of Reddit and YouTube comments prints a decision list grouped by action, along with the ScrapeCreators credits spent, the total Jev cost, and the Jev cost per 1,000 comments.

1. Fetch Reddit and YouTube comments with the ScrapeCreators API

The first thing that happens in the pipeline is pulling the comments in. Two small fetchers call ScrapeCreators, one to search Reddit and one to read the top comments on a YouTube video. Both map results into the same Comment shape so the rest of the pipeline never has to care which platform a comment came from. Each fetcher also keeps the credits its request cost so the final report can show them.
Both responses are paginated. Reddit search returns an after cursor passed back as the after query parameter. YouTube comments returns a continuationToken passed back as continuationToken. Each page is one request, and the captured run was charged one credit per page. The snippet fetches the first page of each so the captured run stays small.

2. Classify relevance and run sentiment analysis with Jev

So to get topic relevance and sentiment for each comment, we send it to Jev with two questions: how relevant is the comment to your topic, and what’s the author feeling. Jev returns a probability for the first, and negative, neutral, or positive for the second. Further, the sentiment response includes a confidence value, so the next step can decide by number instead of parsed text. The function also returns the request cost, and throws when any of the answer parts are missing. A bad judgment will fail loud and fast instead of being quietly recorded. To reach the right endpoint, the client sets serverURL to https://openrouter.ai, because the Decisions API lives under /api/alpha, a different path prefix than the SDK’s default /api/v1.
Each decisions.create call is one paid request billed to your OpenRouter account at the typesafe/jev-1.13 rate, and each ScrapeCreators request spends credits. The comment text you send to Jev is user-generated text from public platforms, so apply the same retention rules to it that you apply to any third-party text you log.

3. Set the human-review threshold and route each comment

The routing rule runs a series of tests on every judgment. If the relevance is at or below 0.2, then the comment is ignored. If it’s between 0.2 and 0.8, then it’s sent to a human for review because Jev isn’t sure it’s even on topic. If it’s at or above 0.8, then the comment is recorded automatically only if the sentiment confidence is at least 0.7. If not, it goes to review too. Every decision also keeps the reason and the full judgment so a human reviewer can see why an item was queued.
To tune the thresholds, start from the review queue. If reviewers agree with Jev’s sentiment on nearly every item that landed in review for low confidence, lower SENTIMENT_CONFIDENCE_AT to 0.5. If recorded comments turn out to be off topic, raise RELEVANT_AT to 0.9. The judgment stays on every decision, so you can re-run decide with new thresholds against the stored judgments without paying for Jev again.

4. Produce the decision list

The last step runs the whole batch and prints the result. triage judges every comment in parallel and routes each one with the rule from step 3. After that the script prints one summary line with the number of comments in the batch, the ScrapeCreators credits spent to fetch them, the total Jev cost, and the Jev cost per 1,000 comments. Then the decisions follow, grouped by action. The empty-batch check makes a fetch that returns nothing fail plainly, instead of printing a meaningless cost per 1,000.

Worked example

Captured output from running the code above on 2026-09-21 against the query openrouter (7 Reddit comments) and one YouTube video (20 top comments). The run cost $0.00057 in Jev usage and 2 ScrapeCreators credits. Comment text is truncated to 160 characters by the script, and the summary line plus one item from each group are shown.
The full list had 5 recorded, 12 in review, and 10 ignored. The review item above shows the threshold doing its job. The comment is clearly about OpenRouter at 0.94 relevance, but Jev split between neutral and positive, so the confidence of 0.65 sent it to a human instead of recording a guess. The ignored item is a thank-you note on the video with no reference to the product. Jev’s numbers are not fully deterministic. For the 25 comments that appeared in two runs of this batch, per-request costs were identical, relevance shifted by up to 0.04 and sentiment confidence by up to 0.13, so a comment sitting near a threshold can land in a different group on a rerun. Reddit search with sort=new can also return a different comment set between runs as new comments arrive.

Check your work

  • Each ScrapeCreators response parses with success: true and a numeric credits_charged. A wrong or missing x-api-key fails at scrape with a non-2xx status, not inside the Zod parse.
  • Every Jev response has answers.relevant.type === 'noul' with noul between 0 and 1, and answers.sentiment.type === 'choice' with choice in negative | neutral | positive and a confidence between 0 and 1.
  • A comment with relevant <= 0.2 gets action: 'ignore' regardless of sentiment. A comment with relevant >= 0.8 and sentiment_confidence >= 0.7 gets action: 'record'. Everything else gets action: 'review' with a reason naming which threshold it missed.
  • response.usage.cost is a number on every Jev response and the printed per_1k_usd equals the summed cost divided by the comment count times 1,000.
  • Re-running decide on stored judgments with different thresholds changes the action split without any new Jev or ScrapeCreators requests.

Next steps