In development
Kortex
A job-scraping and application platform that discovers relevant roles across multiple job boards, deduplicates and scores them by relevance, and surfaces matches through a clean dashboard — built to cut through the noise of manual job hunting.
Overview
Kortex is a job-scraping and application platform: it discovers relevant roles across multiple job boards, deduplicates and scores them by relevance, and surfaces matches through a dashboard. It exists to cut through the noise of manual job hunting.
It is a multi-phase build in active development — the scraping engine, relevance scoring, and dashboard have shipped; application tracking and automation are in progress.
Problem
Job hunting is a high-noise, repetitive workflow. The same search runs across five boards, returns the same postings under different URLs, and buries the handful of genuinely relevant roles under hundreds of near-misses. Doing this manually every day is demoralizing and error-prone — exactly the kind of work software should absorb.
Solution
Kortex splits the pipeline into three phases. A scraping engine collects postings from multiple boards on a schedule and normalizes them into a common schema. A scoring layer deduplicates cross-board reposts and ranks each role against the user's profile, so the feed is ordered by relevance rather than recency. A dashboard surfaces the ranked matches with enough context to decide quickly.
Architecture
The backend is FastAPI, with scraping and scoring jobs running as ARQ workers on Redis — scrapes are scheduled, retried, and isolated from the request path. Supabase (Postgres) stores normalized listings and scoring state. The dashboard is a Next.js/TypeScript app talking to the API.
Deduplication happens at ingest: postings are canonicalized (company, title, location, description fingerprint) before scoring, so the same role syndicated across boards collapses into one entry.
job boards ──► per-source adapters ──► normalize (common schema)
│ │
ARQ workers on Redis ▼
scheduled · retried · dedup at ingest
isolated per source (company · title · location ·
│ description fingerprint)
▼ │
request path ▼
stays clean relevance scoring vs
user profile
│
▼
Supabase (Postgres) — listings
+ scoring state
│
▼
FastAPI ──► Next.js dashboard
(ranked, not
chronological)Tech stack
Challenges
- Job boards differ wildly in structure and change without notice — the scraper layer needs per-source adapters and has to fail per-source, not globally.
- Cross-board deduplication is fuzzier than it looks: the same role appears with different titles, salary framing, and truncated descriptions depending on the board.
- Relevance scoring has to stay explainable — a ranked feed the user doesn't trust is worse than a chronological one.
Lessons learned
- Queue-first design pays for itself immediately: putting every scrape behind ARQ made retries, scheduling, and rate-limiting boring problems instead of architectural ones.
- Normalizing to a strict schema at ingest — before storage, before scoring — kept every downstream phase simple.
- Shipping in phases (scrape → score → dashboard) meant each layer was validated against real data before the next was built on top of it.
Future improvements
- Application tracking — moving from “found a match” to managing the full application lifecycle.
- Notification channels for high-score matches.
- Feedback-driven scoring: letting accept/reject decisions tune the relevance model.
- More board adapters.