HiHiredBot Sniper: Engineering Notes

HiHiredBot Sniper: Engineering Notes

How we built a real-time job-market monitor that beats the crowd to every posting. Written for engineers, hiring managers, and the curious.

40+boards watched
5 minpolling cycle
178,000+postings indexed
10+currencies parsed

From alert to application, three screens

Live captures from a real phone on a real morning: a Databricks Senior Forward Deployed Engineer role pushed two minutes after posting with parsed pay of $182,000 to $250,208, the one-tap apply screen, and the tailored resume building itself.

Sniper push alerts on the phone
One-tap apply screen
Tailored resume building

The problem

Two facts shape modern job hunting. First, speed wins: applications in the first hours of a posting get read, and everything after the first few hundred mostly does not. Second, a meaningful share of public listings are not real openings at all: they are evergreen ads, resume collectors, or compliance postings for roles already promised to someone. A job seeker fighting both problems by hand loses on both fronts.

Sniper attacks both: it finds real postings within minutes of going live, filters out the fakes, and turns the alert into a tailored application before the crowd arrives.

Architecture

Job boards (40+) ATS + aggregator APIs Fetch adapters 5-min loop, API budgets Normalize + dedupe id match + 72h content hash Filters fresh < 8h · geo · honest pay AI pipeline match score · ghost check Alerts push · SMS · 2h digest Tailor on demand resume + cover, cost-tracked

Ingestion: an adapter registry over the ATS universe

  • Pluggable source adapters for Greenhouse, Lever, Ashby, Workday, Adzuna, RemoteOK, and Hacker News hiring threads, all normalizing into one jobs schema.
  • Sources carry per-user criteria as JSON. Priority sources run on a tight 5-minute loop; metered APIs (Adzuna’s 250-call daily cap) run on their own budget-aware schedule with call accounting, so a config change can never silently burn the quota.
  • Workday tenants are probed and validated before activation, because guessing tenant IDs produces silent empty feeds.

Dedupe: the hard part nobody sees

  • Primary identity is (source, external job id), but boards repost the same role under new external ids, so a second content-hash guard keyed on company, title, and location remembers what was alerted for 72 hours.
  • Inserts backfill missing columns (salary, remote status) on re-fetch instead of duplicating rows, which keeps 178,000+ postings clean across months of polling.

The repeat guard, verbatim from production. The design constraint in the comment is the interesting part:

/** Content-level repeat guard: boards (Adzuna especially) repost the same
 *  job under a new external id, which passes the source_job_id newness
 *  check. Keyed on company|title|location, remembered for 72h. NOTE: this
 *  MARKS the job as seen, so only call it once per job, as the final gate. */
public static function recently_alerted($job) {
    $map = get_option('hhb_sniper_seen', []);
    if (!is_array($map)) { $map = []; }
    $now = time();
    foreach ($map as $k => $ts) {
        if (($now - (int) $ts) > 72 * HOUR_IN_SECONDS) { unset($map[$k]); }
    }
    $key = sha1(strtolower(trim(($job['company'] ?? '') . '|' .
           ($job['title'] ?? '') . '|' . ($job['location'] ?? ''))));
    $seen = isset($map[$key]);
    $map[$key] = $now;
    update_option('hhb_sniper_seen', $map, false);
    return $seen;
}

Filtering: freshness, geography, and honest pay

  • Alerts fire only for postings younger than 8 hours: every phone buzz means “worth sprinting on right now.”
  • The remote filter handles the messy reality of location strings, including region codes like USCA that imply distributed work while matching no keyword, and falls back to home-market geography for hybrid roles.
  • Pay renders currency-aware: boards quote in ten-plus currencies stored in one numeric column, and a 22,000,000 JPY salary must never display as $22,000,000.

The AI pipeline: cheap reads, premium writes

  • A fast, inexpensive model handles the reading jobs: scoring each posting against the candidate profile, auditing resumes for ATS compatibility, and running ghost-job detection that flags listings with the fingerprints of a fake.
  • A stronger writing model handles generation: per-posting resume tailoring from a structured master resume, cover letters, follow-ups, and thank-you notes in the candidate’s own voice.
  • Every model call is cost-tracked in cents, per user per day, with budget gates. HTTP timeouts scale with generation size, because a timed-out generation is still billed by the provider and delivering nothing is the most expensive possible outcome.

That timeout lesson cost real money before it became one line of code:

/* A full resume tailor takes ~64s of generation; the old fixed 60s timeout
   failed at the finish line, and the provider bills the request anyway,
   so the short timeout paid for work it then threw away. Allow roughly one
   second per 25 output tokens plus overhead, floored at 60, capped at 300. */
$http_timeout = max(60, min(300, 30 + (int) ($max_tokens / 25)));

Alerting: from feed to phone

  • Instant push notifications via ntfy, optional SMS, and a rolling digest email every two hours that drains and dedupes the alert log.
  • A per-company flood cap ensures no single employer’s posting spree can bury everything else.
Lesson we keep relearning: in a system that polls the same sources forever, correctness lives in the dedupe and the filters, not the fetch. Most of Sniper’s engineering time went into what NOT to show.

Stack

PHP on WordPress infrastructure with MySQL, the Anthropic API for scoring and generation, ntfy for push, and cron-driven schedulers. Deliberately boring technology, chosen so the interesting parts could be the data quality and the AI pipeline.

Try it

Sniper ships as part of HiHiredBot, the AI agent that runs the whole job hunt: find, tailor, send, follow up, prep, negotiate. Start free, or read about the tiers.