<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Alan Wisper — writing</title>
  <subtitle>Notes on job-posting data, phone forensics, deniable storage on Android, and proving Rust code correct.</subtitle>
  <link href="https://alanwisper.com/feed.xml" rel="self" type="application/atom+xml" />
  <link href="https://alanwisper.com/" rel="alternate" type="text/html" />
  <id>https://alanwisper.com/</id>
  <updated>2026-09-22T12:00:00Z</updated>
  <author><name>Alan Wisper</name><uri>https://alanwisper.com/</uri></author>
  <icon>https://alanwisper.com/icon-192.png</icon>
  <entry>
    <title>How “posted 2 days ago” hides a 14-month-old job</title>
    <link href="https://alanwisper.com/posts/real-posting-age" rel="alternate" type="text/html" />
    <id>https://alanwisper.com/posts/real-posting-age</id>
    <published>2026-09-22T12:00:00Z</published>
    <updated>2026-09-22T12:00:00Z</updated>
    <summary>Job boards reset a posting's date on every re-list. The employer's hiring system does not, and three of the big ones publish the real date openly.</summary>
    <content type="html"><![CDATA[
<p>If you have applied for jobs in the last two years you have met the ghost posting: a role that is always open, always “posted 2 days ago”, and never filled. Surveys say most recruiters admit to posting them. New York passed a bill against them this summer. None of that helps you decide whether the posting in front of you is one.</p>
<p>Here is the thing nobody tells you: the date you see on LinkedIn or Indeed is the board's date, not the employer's. Boards reset it on every re-list. The employer's hiring system, the software behind the “Apply” button, keeps the original date, and for the three systems most tech companies use, that date is public.</p>
<h2 id="where-the-real-date-lives">Where the real date lives</h2>
<p>Click “Apply” on a LinkedIn posting and you usually land on one of these hosts. Each one has a public, unauthenticated endpoint that returns every open posting on the company's board, including when it was first published.</p>
<div class="table-wrap">
<table>
<thead><tr><th>Hiring system</th><th>Apply link looks like</th><th>Public endpoint</th><th>Date field</th></tr></thead>
<tbody>
<tr><td>Greenhouse</td><td class="mono">boards.greenhouse.io/{board}/jobs/{id}</td><td class="mono">boards-api.greenhouse.io/v1/boards/{board}/jobs</td><td class="mono">first_published, updated_at</td></tr>
<tr><td>Ashby</td><td class="mono">jobs.ashbyhq.com/{board}/{id}</td><td class="mono">api.ashbyhq.com/posting-api/job-board/{board}</td><td class="mono">publishedAt</td></tr>
<tr><td>Lever</td><td class="mono">jobs.lever.co/{site}/{id}</td><td class="mono">api.lever.co/v0/postings/{site}?mode=json</td><td class="mono">createdAt</td></tr>
</tbody>
</table>
</div>
<p>These endpoints exist so companies can embed their own job list on their own website. Reading them is what they are for. What is new is reading them from the other side: as a candidate, to check the board's story against the employer's.</p>
<h2 id="one-board-one-afternoon">One board, one afternoon</h2>
<p>I pulled GitLab's Greenhouse board on 2026-09-22. It is a normal, healthy board, which is why it makes a fair example.</p>
<pre><code>$ curl -s https://boards-api.greenhouse.io/v1/boards/gitlab/jobs | jq '.jobs | length'
203</code></pre>
<figure>
<div class="table-wrap">
<table class="num">
<thead><tr><th>Measure</th><th>Value</th></tr></thead>
<tbody>
<tr><td>Open postings</td><td>203</td></tr>
<tr><td>Median age since first published</td><td>34 days</td></tr>
<tr><td>Older than 90 days</td><td>18</td></tr>
<tr><td>Older than 180 days</td><td>3</td></tr>
<tr><td>Oldest</td><td>200 days</td></tr>
</tbody>
</table>
</div>
<figcaption>GitLab, Greenhouse public board, 2026-09-22. Age is measured from <code>first_published</code>.</figcaption>
</figure>
<p>One posting on that board, “AI Engineer”, was first published on 2026-05-22 and last updated on 2026-09-14. A board that shows the update date would tell you it is eight days old. It is <mark>four months old</mark>. Nothing sinister about it; but you would want to know.</p>
<figure>
<div class="timeline" role="img" aria-label="Open 123 days since first published on 2026-05-22; a board that shows the last update would say 8 days.">
<div class="tl-labels"><span>first published 2026-05-22</span><span class="tl-board">a board shows 8 days</span></div>
<svg class="tl-bar" viewBox="0 0 1000 10" preserveAspectRatio="none" aria-hidden="true"><rect class="tl-open" x="0" y="0" width="1000" height="10" /><rect class="tl-claim" x="935" y="0" width="65" height="10" /></svg>
<div class="tl-labels"><span class="tl-age">123 days open</span><span>updated 2026-09-14 · checked 2026-09-22</span></div>
</div>
<figcaption>“AI Engineer”, GitLab's Greenhouse board. Brass: open since first published. Clay: what a board that shows the last update would call its age.</figcaption>
</figure>
<p>Other boards are not this tidy. An analysis by csvfirst in May 2026, across 200 companies on Greenhouse, found individual postings at Datadog, Jane Street and Canonical that had been open for more than 2,500 days. Seven years. The board said “posted recently” the whole time.</p>
<h2 id="do-it-yourself">Do it yourself</h2>
<p>You do not need a tool. Take the “Apply” link, find the board name in it, and ask the endpoint. For Greenhouse:</p>
<pre><code>curl -s https://boards-api.greenhouse.io/v1/boards/gitlab/jobs \
  | jq '.jobs[] | select(.id == 8556658002) | {title, first_published, updated_at}'</code></pre>
<p>For Lever, <code>createdAt</code> is a Unix timestamp in milliseconds. For Ashby, <code>publishedAt</code> is an ISO date. Both are in the list response, keyed by the id from the URL.</p>
<div class="callout">
<p>The extension does the same lookup automatically on LinkedIn, Indeed and Glassdoor, and adds the part a single lookup cannot show: how many times the employer has re-listed the same role.</p>
</div>
<h2 id="what-this-does-not-tell-you">What this does not tell you</h2>
<ul>
<li><strong>Intent.</strong> A 400-day posting can be an evergreen pipeline for a role the company always hires, or a fiction kept up for appearances. The age is a fact; the reason is not in the data.</li>
<li><strong>Everything else.</strong> Workday, iCIMS and Taleo, which most large non-tech employers use, do not publish a clean first-published date. For those you are back to heuristics: “Reposted” labels, applicant counts, missing salary.</li>
<li><strong>Closed postings.</strong> When a posting disappears from the board, the endpoint does not say whether it was filled or withdrawn. Only a daily crawl can tell you that a company opens and closes the same role every quarter, and that is what the crawl is for.</li>
</ul>
<h2 id="limitations-of-this-post">Limitations of this post</h2>
<ul>
<li>One board on one day. The numbers above describe GitLab in September 2026, not the industry.</li>
<li>The endpoints are documented as public, but nothing stops a vendor from adding rate limits or authentication later.</li>
<li>The csvfirst figures are theirs, not mine; I have not reproduced them yet.</li>
</ul>
          
]]></content>
  </entry>
</feed>
