# 0% of Greenhouse jobs were over 6 months old. That was my bug, not their data

> Source: <https://dev.to/sdijjxcfs/0-of-greenhouse-jobs-were-over-6-months-old-that-was-my-bug-not-their-data-40ee>
> Published: 2026-10-23 20:55:58+00:00

I spent a week assuming my scraper was fine. Then a sanity check told me 0% of Greenhouse postings were older than six months, which is not a thing that happens on a real job board.

The culprit was one field. And it is not exotic — if you have ever scraped a job feed, you have probably made the same mistake.

`updated_at`
Our Greenhouse connector read:

```
postedAt = row.get("updated_at") or row.get("created_at")
```

That reads fine. It is wrong twice over. First, the Greenhouse board API does not return `created_at` for a job, so the fallback never fires. Second — and this is the part that matters — `updated_at` is *when a recruiter last touched the requisition*, not when the job went live.

Here is what that does to real data. `Account Executive, AI Native` came back with a recent `updated_at`. Its actual `first_published` is **2024-12-20**. A req opened in 2024 and edited yesterday looks one day old.

The fix is one line:

```
postedAt = (row.get("first_published") or row.get("created_at")
            or row.get("updated_at"))
```

`first_published` exists. It was just not the field we reached for.

Same 259 companies, same day, only the date field changed:

|  | with `updated_at` | with `first_published` | 
|---|---|---|
| median age | 22 days | **51 days** | 
| older than 180 days | 5.5% | **14.2%** | 
| Greenhouse older than 180 days | **0.0%** | **14.5%** | 

The Greenhouse number is the tell. No board has zero stale reqs. 0.0% was not a finding, it was a bug report: the freshness signal had been pinned to "last edited", so every edit reset the clock.

If you are filtering scraped jobs by age, or building an "only new since last run" delta on top of `posted_at`, this bug is silent and total. Every stale req comes back as new, forever.

Measured 2026-10-24 from each board's own first-published field:

So the median "open" role has been open for close to two months. Nearly a third are past a quarter, and one in seven is past half a year.

| Board | Postings | Median age | p90 | Oldest | >180d | Date field | 
|---|---|---|---|---|---|---|
| Greenhouse | 4,063 | 52d | 236d | 2,628d | 14.5% | `first_published` | 
| Ashby | 2,615 | 46d | 215d | 1,325d | 12.7% | `createdAt` | 
| Lever | 155 | 77d | 527d | 2,230d | **30.3%** | `publishedAt` | 

Lever postings are older on median and much older in the tail. That is either a different hiring style or a board that keeps reqs visible after the hire — I cannot tell which from the data, so I am not going to dress it up as a conclusion.

Companies with the longest-running open reqs (minimum five dated postings): Secureframe median **442 days**, Zilliz 430, Character 310, Delfina 299. A stale listing is not proof of a fake job either. Plenty of teams keep a rolling req open for a role they hire into continuously. But if you are a candidate sorting by "newest", a rolling req sorted by `updated_at` will follow you around forever.

Before you trust any `posted_at`, answer two questions about it:

`first_published` / `createdAt` / `publishedAt` are posting dates. `modified_at` are edit dates. Mixing them is the bug above.
A cheap sanity test that would have caught this in minutes: compute the share of postings older than six months, per board. If any real board comes back at 0%, you are not measuring what you think you are measuring.

The age numbers and the per-company breakdown live at [radarlabs.surge.sh/freshness.html](https://radarlabs.surge.sh/freshness.html), regenerated weekly from the companies' own public feeds. The underlying postings, with dates and salary ranges, are [a free CC BY 4.0 CSV](https://radarlabs.surge.sh/data/salary-ranges-full.csv) — 6,833 rows across 259 companies. If you would rather not run the scraping yourself, the same data is available as a paid Apify Actor, and I would rather you read the free CSV first and decide whether it is worth $0.0015 a row.
