Traversal labs — index and evidence (WING-333)¶
Mapping every place a job link can spit us out, and how to walk from a listing to the employer's real ATS. Nine seats mapped this on 2026-08-05; every claim below is tagged with how it is known.
MEASURED = we fetched it and observed it. INFERRED = reasoned, not yet observed. Nothing here is a guess presented as fact. Where a lab could not confirm something it says UNKNOWN, because a wrong posting-id regex silently corrupts the dedupe key and is worse than no key.
The labs¶
| Lab | Seat(s) | File |
|---|---|---|
| Adzuna path (hop 0 → ATS) | Sonnet, Gronktayvius, Cedric, Jenny | adzuna-path--*.md |
| ZipRecruiter | Sonnet | ziprecruiter.md |
| Indeed | Sonnet | indeed.md |
| Employer vanity (Radancy/TalentBrew, Phenom) | Gronktayvius | employer-vanity--gronktayvius.md |
| USAJOBS | Gronktayvius | usajobs--gronktayvius.md |
| (rerunning) | — |
Four independent takes on the Adzuna path deliberately, so the design could be compared rather than trusted from one source.
Headline: the aggregators rank in the opposite order to their size¶
| Source | Hops to a real ATS | Account needed? | Stable posting id | Status |
|---|---|---|---|---|
| Indeed | 1 — applystart?jk= → jobs.lever.co/..., careers.vituity.com/... |
no (observed) | data-jk, high confidence |
MEASURED |
| ZipRecruiter | 2 — job-redirect?match_token= → jobs.spectrum.com |
no (observed) | UNKNOWN — opaque token, stability unconfirmed | MEASURED |
| Adzuna | never | YES — registration wall | n/a | MEASURED |
Why Adzuna cannot be traversed (three independent walls)¶
- WAF.
adzuna.com/land/ad/*returns 403 to a plain HttpClient (180/180) and to a headless browser (36/40). A headful browser passes (0/15 blocked). The block targets automation, not IP or User-Agent. Evenrobots.txtis 403. (MEASURED) - No employer link on the page. The
/details/{id}page has 99 anchors, 0 external, and zero ATS hosts referenced anywhere in the page source — not in anchors, not in scripts, not in JSON-LD. The employer destination simply is not present pre-auth. (MEASURED) - Registration wall.
/details/{id}/apply?aztt=<JWT>does not hand off to the employer. It lands on/authenticate/register, whose only exits are Continue-with-Google, Continue-with-Facebook, Login, and "Return to job advert". (MEASURED)
Wall 3 is the decisive one: it means solving the WAF would buy nothing. Reaching the employer through Adzuna requires an Adzuna account, and the product never auto-creates accounts.
A "No thanks, take me to the job" string exists in the page source but is a localization key
(JOBS:AD_DETAILS:ALERTS:SKIP) belonging to the email-alerts modal — NOT an escape from
registration. An earlier draft of this research read it as a live control; it is not.
The aztt token (useful even though the path is closed)¶
{"tt":"api","iat":1785984582,"exp":1786589382,"ci":"IiBgeUGR8RGnetrqMq1kUQ","ts":"4efc5ab9"}
ts is our own app id — so attribution to Work Wingman
rides through the apply. Relevant to any partnership conversation: we demonstrably send them
attributed traffic.
Adzuna as a discovery index only¶
The API returns id, redirect_url, adref, title, company, location, description, salary, category,
created, contract_time, lat/long. No field names the downstream board. Matching a listing to a
traversable source would therefore mean matching on company+title+location ourselves — technically
easy, but it runs into an Adzuna API ToS clause (reported by Gronktayvius, UNVERIFIED because
their terms page is a JS shell) forbidding API users from contacting third-party content providers.
CORRECTION. An earlier draft of this file argued the meta-layer was redundant because "if Indeed and ZipRecruiter are traversable, they are also directly searchable." That is FALSE, and the error matters because it would have led us to drop the one licensable source we have.
- Indeed's Publisher API is closed. Keys stopped working in the 2023 shutdown; the program shut in 2024 and has minted none since. The surviving Indeed Apply API is partner-only and sales-led, aimed at ATS and staffing platforms.
- ZipRecruiter's Partner API is the EMPLOYER side — post jobs, receive applications, report hiring signals — gated behind an ATS-integration sales contact. It is not candidate-side search.
- Adzuna is one of the few aggregators still running a publisher/partner program, alongside Jooble and Talent.com.
So traversing a link someone handed us is NOT the same capability as querying a corpus. Adzuna's value is not breadth and not traversal — it is that it is one of the last legitimately licensable job-search APIs. That is scarce, and it is why Adzuna stays in the architecture even though its apply path is closed to us.
This also promotes the ToS clause from a footnote to the pivotal unknown: if Adzuna forbids resolving their listings elsewhere, their inventory is search-plus-hand-off-to-the-user only.
Unevaluated and worth evaluating: Jooble and Talent.com — the other two named as still running publisher programs, and the realistic path to non-tech occupational breadth.
The trap that generalises: page text lies about the ATS¶
Two independent instances, different vendors, both MEASURED:
jobs.spectrum.com— HTML contains markers foricims,lever,radancy,talentbrew. The truth is iCIMS (careers-charter.icims.com, posting id 77665), findable ONLY via an anchor href. A first-marker-wins scan resolves it to Lever — wrong.careers.vituity.com(reached from Indeed) — HTML says "taleo", with zero outbound anchors to any Taleo host.
Rule: the vendor of an anchor's host beats any marker found in page text or scripts. Two different ATS vendors in anchors → return Unknown (ambiguous), never pick one.
Second trap, same page: /application-process is an apply-shaped link to an information page. An
apply candidate must leave the host or match a known ATS host.
Third trap: Adzuna's ApplyIQ => /apply-iq anchor leads into their own competing apply product, not
the employer.
Posting-id extraction (the dedupe key)¶
VERIFIED against the vendors' own APIs — extracted id equals the id the API reports:
| Vendor | Example | Extracted | Status |
|---|---|---|---|
| Greenhouse | careers.airbnb.com/positions/7995199?gh_jid=7995199 |
7995199 |
VERIFIED (note: vanity host, id survives in query) |
| Ashby | jobs.ashbyhq.com/ramp/34413f8d-… |
uuid | VERIFIED |
| iCIMS | careers-charter.icims.com/jobs/77665/… |
77665 |
VERIFIED |
| Lever | — | — | UNTESTED (public API errored during verification) |
| Indeed | data-jk |
jk | MEASURED, high confidence |
| ZipRecruiter | opaque match_token |
— | UNKNOWN — do not use for dedupe |
| Phenom | highly variable per tenant | — | UNKNOWN by design |
Fixture corpus¶
tests/fixtures/traversal/ — 696 KB, 13 verbatim captures, each with url, final url, status, bytes,
sha256 prefix, timestamp, and method in PROVENANCE.jsonl. Includes the artifacts the negative
tests need: the 403 WAF body, the registration wall, and the multi-marker vanity page.
Captured with a real browser as itself — no stealth plugins, no webdriver patching, no UA/TLS spoofing, no CAPTCHA solving, no proxy rotation. Headful captures ran with the window positioned off-screen so no window is ever shown to the user.
Open¶
- Adzuna ToS clause — needs a human to load their JS-rendered terms page.
- LinkedIn lab — rerunning.
- Lever posting-id regex — untested.
- ZipRecruiter posting-id stability — unknown; do not key dedupe on it yet.
- None of this is implemented. These are specs plus evidence, not code.