05 / BACKEND SYSTEM

Building a multi-source hiring intelligence engine with canonical normalization and change tracking

A Python acquisition layer that converts heterogeneous applicant-tracking systems into dependable, comparable job-market signals.

Primary visual evidence
10
ADAPTER IMPLEMENTATIONS
24
TEST MODULES
68
TESTS PASSED IN CAPTURED RUN
1.24 s
CAPTURED SELECTED-SUITE RUNTIME

Executive Summary

Every applicant-tracking system represents the same idea — a job posting — differently: different APIs, different HTML, different pagination, different field names for the same concept. This engine puts ten of them behind a shared adapter interface, converts everything into one canonical job model, and computes the delta between snapshots rather than re-ingesting the same postings as new data every run.

Context

Structured as a proper Python package — Python 3.11+, database schemas, orchestration scripts, observability hooks — with dedicated modules for rate limiting, proxy health, normalization, revision history and hiring-velocity scoring, aimed at recurring monitoring rather than a one-off export. It is the acquisition core that also powers the job-market side of the larger Elevya Signal Intelligence platform elsewhere in this portfolio, built first here as a standalone, reusable package.

My role and ownership

  • Adapter and registry architecture
  • Normalization and delta processing
  • Rate limiting, retries and proxy-health controls
  • PostgreSQL schemas and revision history
  • Automated tests for core logic and sources

Personal contribution

Adapters, registry, canonical model, state comparison, revisions, network controls and tests.

Problem

A title with a requisition code baked in, a location string that mixes city and remote-work signals in no consistent order, a salary range written as free text, a contract type spelled four different ways across four platforms — none of that is usable for comparison until it's normalized. And without snapshot-to-snapshot comparison, "new data" is really just the same postings re-ingested on every run.

BEFORE

  • One-off source-specific scripts
  • Different field names and types per ATS
  • Repeated processing when boards have not changed
  • No durable record of added, changed or removed jobs
  • No company-level hiring momentum signal

AFTER

  • Adapter registry with source-specific clients
  • Canonical title, location, contract, salary and function fields
  • Conditional fetch and rate-limit controls where supported
  • Delta and revision layers for lifecycle tracking
  • High-Velocity Indicator for acceleration classification

Meaningful result: Ten ATS integrations converge on one canonical job and change model.

Solution

Ten source adapters — Greenhouse, Lever, Ashby, Workable, Personio, BambooHR, Recruitee, SmartRecruiters, Teamtailor and France Travail — sit behind one registry and shared orchestration, so adding an eleventh platform means writing one adapter, not touching the pipeline that runs all ten. Every record, regardless of source, lands in the same canonical schema.

01
ATS board
API / GraphQL / HTML
02
Adapter
Source acquisition
03
Canonical model
Normalized job
04
Delta
New / changed / closed
05
Signal
Hiring momentum

A delta module compares each new snapshot against the last known state per source, so only genuine changes generate a revision record instead of re-writing everything every run. A hiring-velocity module turns the resulting change history into a company-level acceleration signal — the piece that makes ten normalized feeds worth more than ten spreadsheets, because it answers "who's hiring faster than last month," not just "who's hiring." The pipeline in sequence:

Engineering

The package targets Python 3.11+ and uses asyncio, httpx, psycopg and Selectolax. The adapter interface is strict enough that the orchestrator never needs to know which platform it's talking to, at the cost of upfront design time — a cost that pays back every time a new ATS gets added, since the tenth adapter took a fraction of the effort the first one did.

DECISION 01

Thin adapters, shared policy

Platform-specific parsing stays isolated while retries, rate limits, metrics and database behavior improve once for the whole engine.

DECISION 02

Normalize before downstream use

A canonical job record converts inconsistent titles, locations, contracts and salaries into fields that support analytics.

DECISION 03

Track revisions, not only current rows

Delta processing distinguishes new, changed and closed jobs and preserves evidence for hiring momentum.

DECISION 04

Test contracts and edge cases

The repository includes tests for infrastructure and adapters; the supplied capture shows 68 passing tests with 19 warnings.

Python 3.11+ asyncio httpx PostgreSQL Adapters Delta / revisions

Visual Proof

Outcome

Ten adapter source files, 24 test modules covering infrastructure and platform clients, and PostgreSQL schemas for core records, revisions and cache with optimized indexes — this is the acquisition layer that made the larger Signal Intelligence platform possible to build in the time it took, because the hardest normalization problem was already solved and tested here first.

Verified evidence

  • 10 adapter source files identified
  • 24 test modules covering infrastructure and platform clients
  • Captured selected run: 68 passed, 19 warnings in 1.24 seconds
  • PostgreSQL schemas for core records, revisions, cache and optimized indexes
  • CLI scripts for sweeps, exports, HVI backfill and classifier training

Current limitations

  • No live multi-platform sweep was captured for this portfolio package.
  • The screenshot proves selected tests, not every production integration or credential path.
  • An optimization percentage in source documentation lacks a benchmark artifact and is not repeated here.
  • Warnings remain in the captured suite and should be removed or explicitly accepted.
  • A public demo should use fixtures or cached responses.

WHY THIS PROJECT MATTERS

Ten ATS integrations converge on one canonical job and change model.

This case study is designed to be inspectable: strong claims are connected to screenshots, source evidence or explicitly stated limitations.