# Spoor — job-count statistics from ATS job boards

> Open-role counts scraped once a day straight from the job boards of 8598 companies — Greenhouse, Lever and Ashby — and counted into a set of statistics. Every "open now" figure describes 2026-09-24 (UTC), and pay is USD per year. The site is aggregates, with one exception: `/` is the board itself — every open posting across every tracked company, narrowed by any of eight filters, one of which is a search on the title.

The figures behind every page are at `/llms-full.txt` as Markdown tables — the numbers each page draws as charts, in the form a reader that cannot see an SVG can quote.

## Read this before quoting a number

- **This is a tracked list, not a market.** 8598 companies across 8817 job boards, chosen by hand. "Engineering hiring fell 4%" is a statement about these boards. It is not a statement about the economy, an industry, or a country.
- **A posting is a posting, not a job.** A repost is a new row with its own date, because a company that reposts the same role monthly genuinely is posting monthly. Open roles are counted as live postings, never as distinct titles.
- **`other` is not a category.** It is what the title classifier could not place, and it is the largest single thing on this site — larger than Engineering. It is listed last everywhere for that reason. Reporting it as the biggest category is the most common way to be wrong here.
- **Pay describes the postings that quote pay.** Ranges arrive structured from two of the three providers; the largest provider returns none. Every pay figure is the median over the postings that published a range, in one currency and one interval, and every page carries the count so the denominator is never guessed.
- **Daily series start at the first successful scrape**, 2026-08-23. Before it, only postings that were still open are visible, so a chart across those dates climbs towards the present whatever hiring did. Age of the current openings is reported instead, and it describes what is open rather than what was hired.
- **Places do not add up, deliberately.** A role open in two cities is two cities and one country. Country totals count distinct countries per posting rather than summing the cities inside them.
- **A listing is what the last scrape stored, not what is open right now.** The boards are read once a day. A role filled or withdrawn since that read is still listed until the next one, and the counts on every other page describe the same moment — so a listing and a count never disagree, and both can be a day behind the board.
- **Everything is UTC**, and every count is a whole number of postings.

## The data

- [/](https://spoor.fyi/): the board, and the site's front door — every open posting, newest first, 50 to a page, each with its title, date, locations, workplace and employment type. Every row links at this site's own URL for the posting, and that is the one to link or quote: it is a page here, carrying the description the board published, and it keeps working after the role closes. `robots.txt` disallows the path, so it is a URL to quote rather than one to crawl. The board's own URL is not published — a posting quoted as `boards.greenhouse.io/...` says nothing about where it was found. Eight filters, all optional and all combinable: `?q=<words>` (up to 4 words, each of which the title must contain), `?company=<domain>`, `?group=<key>`, `?level=<key>`, `?place=<key>`, `?workplace=remote|hybrid|onsite`, `?employment=full_time|part_time|contract|internship|temporary|fixed_term|apprenticeship` and `?pay=1` for the postings that publish a range — plus `?page=N` up to 200. The keys are the ones on `/insights/roles` and `/insights/places`; a company's key is its domain. Every filter is a whitelist: a value this site does not count is a 404 rather than an empty page. A posting scraped since the last classification run has no group, level or place yet, so those filters do not sum to the board's total. Above 1,000 matches the count is a floor rather than a total — narrowing the board makes it exact. **The one route here that returns postings rather than counts**, and the one whose `pay_min`/`pay_max` are the posting's own currency and interval rather than USD per year — so those columns must not be summed or averaged across rows.
- [/insights](https://spoor.fyi/insights): the overview — open roles now, the daily series, every job group, the pay distribution in 5,000 steps, and how old the open postings are. Takes `?range=30d|90d|1y|all`.
- [/insights/companies](https://spoor.fyi/insights/companies): the shape of the population — how many companies are hiring, how the open roles are spread across them, what grew and what shrank.
- [/insights/roles](https://spoor.fyi/insights/roles): the whole role taxonomy — group, title and level, with counts and median pay at all three depths. Takes `?sort=open|pay`. The page fetches the third depth a title at a time; `/llms-full.txt` carries all three whole.
- [/insights/places](https://spoor.fyi/insights/places): every region, country and city the boards name, with the coordinates the map draws them at, plus the remote share and what could not be placed.
- [/company/<domain>](https://spoor.fyi/company/stripe.com): one company — its standing, what it hires for, its daily series and the month each still-open posting was posted in. The key is the domain: `/company/stripe.com`.
- [/insights/roles/<group>](https://spoor.fyi/insights/roles/engineering): the titles inside one job group.
- [/insights/roles/<group>/<title>](https://spoor.fyi/insights/roles/engineering/software_engineer): the levels inside one title.

Group, title and level keys are the path segments — `engineering`, `software_engineer`, `staff` — and every table in `/llms-full.txt` that names a category carries its key beside its label, so a URL can be built from a row.

## Everything at once

- [/llms-full.txt](https://spoor.fyi/llms-full.txt): this file, the headline figures, the whole taxonomy, and every place — one document, no paging.
- [/sitemap.xml](https://spoor.fyi/sitemap.xml): every HTML page.
- [/privacy](https://spoor.fyi/privacy): what the site keeps about a reader, and how to have it removed.
- [/terms](https://spoor.fyi/terms): the terms for using the site, including reading it by machine.

## How the numbers are made

The job boards of tracked companies are read once a day, one request per board, and the whole API payload is stored. Nothing after that touches the network: a second pass reads the stored payloads into normalised fields, classifies each posting's title into group, title and level, resolves its location strings against a gazetteer, and recomputes every statistic on this site whole. Pages read those stored statistics and aggregate nothing themselves. The listing and a posting's own page are the only two that read postings at request time, because neither a list of roles nor a description has a statistic to precompute — the first is bound to one company and one screen, the second to one posting.

A posting is closed when a board that answered successfully stops listing it. A board that errors, times out or returns unparseable JSON closes nothing, and a board that loses more than a fifth of its live postings at once closes nothing and raises a flag instead — so a provider outage cannot read as a wave of layoffs.

Two things this cannot see. A company moving between ATS providers reposts its jobs under new ids, which looks like a wave of closures and a wave of postings on the same day; it is left visible rather than papered over with fuzzy title matching. And a posting removed for any reason is "closed" — filled, cancelled and expired are indistinguishable from outside.

## Optional

- [/mcp](https://spoor.fyi/mcp): the same data as six read-only tools, for an agent that speaks the Model Context Protocol over streamable HTTP. No sign-in.
- [/health](https://spoor.fyi/health): the latest day the site can read, as JSON.
- [/robots.txt](https://spoor.fyi/robots.txt): crawling rules. Every page here is public and welcome to be read.
