Build Rankhog

KINDAreplaces $99/mosaves $1,188/yrback to the verdict

0%0 of 18 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A local opportunity finder whose hard problems are being a well-behaved API client without hard-coding a rate you cannot verify, spending a paid search budget only on threads worth checking, and drafting replies that answer a question rather than advertising.

estimated effort weekendthe files for this build are in the project pack

RuntimeA Node 22 command line sweep plus a review page on localhostReddit accessRegistered API credentials, a descriptive User-Agent, pacing taken from the response headersStorageSQLite where a dismissed thread is dismissed permanentlyRanking filterA search API behind a per-sweep query budget and a cacheDraftingOne model call per candidate, fed the subreddit's own rules, output stored and never sent

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Runs the sweep and the local review page.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. free for personal, non-commercial use; commercial use needs separate approval

    Why The API expects an identified client, and the terms you agree to when registering are the ones that govern what you may do with the data.

    Get it Create an application in your Reddit preferences, note the client identifier and secret, and read the current data API terms and developer guidance before writing the client.

    Verify An authenticated search returns results and the response carries rate limit headers you can read

  3. API keya few pounds per thousand queries on most providers

    Why The ranking filter is the thing that separates a thread worth answering from one nobody will ever see, and it is the only per-query cost in the build.

    Get it Any search API with published per-query pricing and a spend cap you can set.

    Verify One keyword lookup returns results and the account shows the queries used

  4. API keypennies per sweep at a few dozen drafts

    Why Drafting is the other external cost, and it should have its own limit rather than sharing one.

    Get it Create a key with its own spend limit.

    Verify A draft returns and the token usage is recorded

  5. have readyfree, and expensive in time

    Why This is the part the tool cannot give you, and the reason the paid product has a business. Without it the best draft in the world gets removed.

    Get it Comment usefully, without mentioning anything you sell, for several weeks. There is no shortcut and buying one is worse than nothing.

    Verify You have posted ordinary comments in each target subreddit over multiple weeks

  6. decidefree

    Why Each is enforced by code and asserted by a test, and choosing them after a bad week means choosing whatever already happened.

    Get it Write down the paid queries per sweep, the promotional replies per subreddit per week, and the ordinary to promotional ratio below which the tool stops suggesting promotion.

    Verify All three are in the configuration and named in tests

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- keywords(id, text, subreddits_json, enabled, added_at)
- threads(id, remote_id, subreddit, title, permalink, author, created_at, score, num_comments, fetched_at)
- matches(id, thread_id, keyword_id, matched_at, state) · state is new, ranked, drafted, dismissed or posted_by_me
- serp_cache(keyword_id, checked_at, results_json, queries_used) · one lookup per keyword per window, reused by every match
- sub_rules(subreddit, rules_json, fetched_at, self_promo_summary) · pulled from the subreddit's own rules endpoint, refreshed on a schedule
- drafts(match_id, model, prompt_version, text, created_at, edited_text, copied_at)
- participation(subreddit, week, ordinary_comments, promotional_comments) · you fill this in, and the tool refuses to suggest more promotion when the ratio is wrong
- Dismissal is permanent. A thread you rejected never appears again, because a finder that repeats itself is noise.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
DATABASE_PATHrequired./data/rankhog.dbThreads, matches, drafts and dismissals. The dismissals are the part you cannot rebuild.
REDDIT_CLIENT_IDsecretrequiredredactedFrom your registered application. Never in the repository.
REDDIT_CLIENT_SECRETsecretrequiredredactedSame application. Rotate it if it ever reaches a log.
REDDIT_USER_AGENTrequiredrankhog/0.1 by u-yourname (contact: you@example.com)Names the tool, a version and a contact, as the platform's guidance asks.
SERP_API_KEYsecretrequiredredactedThe ranking filter. Set a spend cap at the provider as well as here.
SERP_QUERIES_PER_SWEEPrequired25A hard stop. The sweep reports what it did not check.
LLM_API_KEYsecretrequiredsk-...redactedDrafting only. Its own key with its own limit.
PROMO_PER_SUB_PER_WEEKrequired1Above this, promotional drafts are hidden and the reason is shown.

The build, in order

  1. A client the platform can identify and slow down

    Every request is authenticated, identifiable, and paced by what the server says rather than by a number you typed.

    1. Read the current terms before you write the client, not after it is blocked.

      Files src/reddit/client.js.env.example

    2. The server already tells you the answer on every reply.

      Files src/reddit/pacing.jstests/pacing.test.js

    3. A retry loop through a 429 is the behaviour that earns a block.

      Files src/reddit/client.js

    done when · tick each as it passes
    watch out
    • Copying a requests-per-minute figure out of a blog post is wrong twice: the figure is stale, and it ignores that the server reports your remaining allowance and reset time on every single response. Pace from those headers, and keep no rate constant in the code at all so there is nothing to go out of date.
  2. The sweep, and the pile you already rejected

    A sweep turns keywords into candidate threads without ever resurfacing one you dismissed.

    1. Everything free happens before anything billed.

      Files src/filters.jstests/filters.test.js

    2. The dismissed pile is what makes the list readable on day ten.

      Files src/matches.js

    3. A keyword that only produces noise should be deleted, not endured.

      Files src/sweep.js

    done when · tick each as it passes
    watch out
    • Storing matches without a dismissal state means every sweep hands you the same forty threads you looked at yesterday, plus two new ones, and you stop reading the list by the fourth day. Make dismissal permanent, show new matches first, and count how many candidates each keyword produced so a keyword that only generates noise is obvious.
  3. Only the threads people actually land on

    A paid search lookup happens once per keyword per window and the budget is a hard stop.

    1. The ranking question belongs to the keyword.

      Files src/serp.jstests/serp-cache.test.js

    2. A soft budget is a bill with an apology attached.

      Files src/budget.js

    3. A cost you can see is a cost you can decide about.

      Files src/views/sweep.ejs

    done when · tick each as it passes
    watch out
    • Checking every matched thread against the search API spends the budget on threads nobody ever sees, because the ranking question is about the keyword, not the thread: one lookup per keyword answers it for every match at once. Cache per keyword per window, and make the budget a hard stop that reports the remainder instead of a soft target that gets exceeded.
  4. Drafts that answer the question

    Every draft opens with a real answer and respects the subreddit's own rules about promotion.

    1. And show them beside the draft, with the fetch time.

      Files src/rules.jssrc/draft.js

    2. That property is the difference between help and spam.

      Files src/draft.jstests/answer-first.test.js

    3. There is no send. That is the point.

      Files src/views/match.ejs

    done when · tick each as it passes
    watch out
    • A model told to write a reply promoting your product writes an advertisement with a question mark on the end, and that is precisely the comment that gets removed, reported, and remembered. Put the subreddit's own rules in the prompt, require the answer to stand on its own if the last sentence were deleted, and check that property before showing the draft.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more seo & marketing.