All platforms

Ashby Jobs API

Query jobs from Ashby job boards through one API — and how Ashby's posting endpoint, compensation tiers and isRemote flag really behave.

?source=ashbysource reference

Ashby is an all-in-one recruiting platform that's become the default at a lot of newer, engineering-heavy startups. Boards live at jobs.ashbyhq.com/<name>, and the public posting API returns an entire board — descriptions and structured compensation included — in a single unauthenticated GET. Alongside Greenhouse it's the nicest ATS in this pool to read, and it publishes better salary data than any of them.

Try it#

BASH
curl -G https://api.hyperjobs.io/v1/jobs \
  -H "Authorization: Bearer $HYPERJOBS_KEY" \
  --data-urlencode "source=ashby" \
  --data-urlencode "has_salary=true" \
  --data-urlencode "limit=2"
Illustrative — the shape, not a live response
{
  "data": [
    {
      "id": "ashby:7c1f2a94-3e17-4d8b-9c05-1f6ab2e40d33",
      "title": "Staff Backend Engineer",
      "company": {
        "slug": "meridian",
        "name": "Meridian",
        "domain": "meridian.dev",
        "website": "https://meridian.dev",
        "logo": "https://media.licdn.com/dms/image/…",
        "industry": "Software Development",
        "employee_count": 95
      },
      "location": {
        "remote": true,
        "countries": ["United States"],
        "cities": ["New York"],
        "regions": ["NY"],
        "raw": ["New York, NY, USA"]
      },
      "employment_type": ["FULL_TIME"],
      "work_arrangement": "Remote",
      "salary": {
        "min": 191000,
        "max": 291000,
        "currency": "USD",
        "unit": "YEAR",
        "annual_min": 191000,
        "annual_max": 291000,
        "summary": "$191K - $291K"
      },
      "skills": ["typescript", "postgresql", "kubernetes"],
      "taxonomies": ["Software"],
      "apply_url": "https://jobs.ashbyhq.com/meridian/7c1f2a94-3e17-4d8b-9c05-1f6ab2e40d33/application",
      "url": "https://jobs.ashbyhq.com/meridian/7c1f2a94-3e17-4d8b-9c05-1f6ab2e40d33",
      "source": "ashby",
      "posted_at": "2026-07-11T13:45:00.000Z",
      "expires_at": null,
      "updated_at": "2026-07-14T02:15:40.000Z"
    }
  ],
  "total": 2,
  "limit": 2,
  "offset": 0
}

How Ashby boards actually work#

One GET, no auth, no pagination, whole board with bodies:

BASH
curl "https://api.ashbyhq.com/posting-api/job-board/<name>?includeCompensation=true"

<name> is the board name from jobs.ashbyhq.com/<name>. Response is { "jobs": [...] }. Each posting carries id (a UUID), title, descriptionHtml (real HTML, not entity-escaped), publishedAt as ISO, jobUrl, applyUrl, department, team, employmentType, location, address, secondaryLocations[], isListed, isRemote, workplaceType, and — if you asked for it — compensation.

includeCompensation=true is opt-in and free. Leave it off and you silently get no pay data at all.

Compensation is genuinely structured#

Ashby is the best salary source in this pool, and the reason is that it publishes typed components rather than a string:

JSON
"compensation": {
  "compensationTiers": [
    { "components": [
      { "compensationType": "Salary", "interval": "1 YEAR",
        "currencyCode": "USD", "minValue": 191000, "maxValue": 240000 },
      { "compensationType": "EquityCashValue", "interval": "1 YEAR", "minValue": 40000 }
    ] }
  ],
  "scrapeableCompensationSalarySummary": "$191K - $291K"
}

Two things follow. Filter on compensationType === "Salary" — an EquityCashValue or Commission component is not base pay, and summing them gives you a number the employer never offered. And tiers are geographic: one posting can carry a band per market, so the real posted range is the outer band across every tier — the minimum of the mins, the maximum of the maxes.

interval is "1 YEAR" or "1 HOUR" — a count and a unit in one string, so it needs normalising before it means anything.

Ashby also ships scrapeableCompensationSalarySummary, which is a courtesy: a pre-rendered $191K - $291K explicitly meant to be read by scrapers. It's the fallback when the typed numbers are absent, and parsing it means anchoring each number to a currency symbol — otherwise Multiple Ranges and stray tier counts get read as salaries.

The address is better than the location string#

location is a display string like New York, NY (HQ). Alongside it, address.postalAddress carries addressLocality, addressRegion and addressCountry as separate fields. The string often drops the country the structured address keeps, so preferring the structured form is what makes a bare-city posting resolve. secondaryLocations[] has the same shape for multi-site roles.

What bites you#

`isRemote` is hybrid-inclusive

Ashby's isRemote boolean reads true for hybrid roles too. On some boards it's true for roughly 87% of postings. Treat it as "is this remote" and you will tag most of a company's on-site-two-days-a-week jobs as fully remote.

workplaceType is the field you want — Remote, Hybrid, OnSite, the real three-way. Use isRemote only as a fallback when workplaceType is absent, and never when it's present.

Some board tokens aren't companies. Ashby hosts curated multi-company aggregator boards — "jobs pages" — whose token is jobs-page-<uuid> or a bare UUID. They look exactly like a company board and return real postings, from many different employers. Scrape one and you attribute dozens of companies' jobs to a single garbage employer named Jobs-Page-<uuid>. We detect and skip those tokens. If you're building your own board list, this is the trap that quietly poisons your company table.

isListed: false is in the payload. Unlisted postings come back with everything else. Filter them out or you'll publish roles the employer chose not to show.

A posting may have applyUrl but no jobUrl. Take whichever is present.

Or use ours#

If you have ten known Ashby boards, the endpoint above is the whole job and you should just use it. This page exists partly to make that easy.

What we add is the surrounding work. We track 4,227 Ashby boards, which is the hard part — Ashby publishes no customer index, and the aggregator "jobs pages" mean a naive board list actively harms you. Beyond that: the outer band computed across compensation tiers, workplaceType preferred over the hybrid-inclusive boolean, expiry by board diffing, dedup against the same role on LinkedIn or Lever, and the company join turning a board slug into a profile.

And normalisation. Ashby's { compensationType, interval: "1 YEAR", minValue }, Greenhouse's cents, and Recruitee's string-typed bounds all arrive as one salary object with annual_min and annual_max you can compare. Salary data is honest about where that breaks down.

Ashby is in the fast refresh tier, attempted every cycle — a new posting usually lands within the hour. Coverage has the tiers.

Quickstart · Pricing

Every source above normalises onto the same schema, with the hiring company already joined onto each posting.

Get an API key