DEV Community

Daniel Meshulam
Daniel Meshulam

Posted on Fully Autonomous

SpaceX, Anduril and Databricks jobs API: the Greenhouse board API and the pay ranges it hides

SpaceX (spacex), Anduril (andurilindustries) and Databricks (databricks) post their jobs on Greenhouse, and Greenhouse publishes every board through a public JSON API, with no key:

curl -s "https://boards-api.greenhouse.io/v1/boards/spacex/jobs" | jq '.meta.total, (.jobs | length)'
Enter fullscreen mode Exit fullscreen mode

That printed 2832 and 2832: the whole board in one response, no paging. Five things to know, measured on 2026-10-27 with a few seconds between requests.

1. The token is not the company's name

for t in anduril andurilindustries xai; do
  printf "%s " $t
  curl -s -o /dev/null -w "%{http_code}\n" "https://boards-api.greenhouse.io/v1/boards/$t/jobs"
  sleep 2
done
Enter fullscreen mode Exit fullscreen mode

That printed 404, 200 and 200. Anduril's board is andurilindustries; anduril does not exist. A 404 here means no such board, and a 200 with an empty list means a board with nothing open, so the two can be told apart.

2. Pay ranges need a flag

The list has no pay field unless you ask with ?pay_transparency=true. Then every job carries pay_input_ranges, in the same response:

curl -s "https://boards-api.greenhouse.io/v1/boards/andurilindustries/jobs?pay_transparency=true" \
  | jq '[.jobs[] | select(.pay_input_ranges | length > 0)] | length, .[0].pay_input_ranges[0]'
Enter fullscreen mode Exit fullscreen mode

2,342 of Anduril's 2,520 jobs had a range, the first {"min_cents": 9000000, "max_cents": 11000000, "currency_type": "USD", "title": "US Salary Range"}. The amounts are in cents, a job can carry several ranges ("Zone 2 Pay Range"), and the title does not reliably say the period: 12 ranges titled "HOURLY POSITION" topped out at 60,000. Across the 60 largest Greenhouse boards, 32 publish ranges, on 14,208 of 53,750 jobs. SpaceX publishes none there; 1,518 of its 2,832 jobs write "Pay Range" into the description instead.

3. content=true is twelve times the bytes, escaped twice

The description, departments and offices come only with ?content=true. SpaceX's board is 2.7 MB without it and 32 MB with it, and the HTML arrives entity-encoded:

curl -s "https://boards-api.greenhouse.io/v1/boards/databricks/jobs?content=true" \
  | jq -r '.jobs[0].content[0:60]'
Enter fullscreen mode Exit fullscreen mode

That printed <p>Req ID: .... Unescape before stripping tags, or the tag stripper finds no tags and the reader gets <p>.

4. A posting is not a job

Each posting has its own id, and internal_job_id names the job behind it:

curl -s "https://boards-api.greenhouse.io/v1/boards/spacex/jobs" \
  | jq '(.jobs | length), ([.jobs[].internal_job_id] | unique | length)'
Enter fullscreen mode Exit fullscreen mode

That printed 2832 and 2125. One SpaceX job is advertised under 13 different titles, all in Hawthorne. Anduril's 2,520 postings are 1,527 jobs, one of them posted 14 times across 7 titles and 5 places. Count openings by internal_job_id, list postings by id.

5. location is one string, offices is a list

Anduril writes up to nine places into one location.name, joined by "; " (196 of its 2,520 postings), while offices lists them separately with its own codes, such as "Costa Mesa, CA (OC-00)". metadata holds each company's own fields: "Employment Type" is "Full-time" at Anduril and "Regular" at SpaceX, next to SpaceX's "Discipline".

Hosted

Greenhouse Jobs Scraper on Apify takes a board token or a company domain, always asks for pay_input_ranges and turns the cents into salaryMin, salaryMax and a period, and unescapes the description. Company Career Page Jobs Scraper takes SpaceX or Anduril by name next to employers on Workday, Oracle and the rest. Every other system's public endpoint, with a tested curl, is in job-board-apis.

Top comments (0)