# Ashby Jobs API - Search 615 Boards, No Company List (`deadwood_data_solutions/ashby-jobs-api`) Actor

Query an index of 615 verified Ashby boards — 14,884 open roles — with no company list of your own. Ashby publishes structured pay, so salary ranges come through on most postings. Filter by title, location, remote and posting date. Deduped for scheduled runs.

- **URL**: https://apify.com/deadwood\_data\_solutions/ashby-jobs-api.md
- **Developed by:** [K O](https://apify.com/deadwood_data_solutions) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 job records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Ashby Jobs Scraper — The Only One With Published Salary

**No company list needed.** Pick a verified company bundle and hit Start - every board name in it was checked against the live Ashby API, so you get real postings, salary included, without researching a single one.

**The only one of our ATS board scrapers — including the multi-ATS aggregator — that surfaces published compensation.** `jobs-aggregator` requests Ashby's compensation data too, but drops the field to keep its schema identical across all six ATS platforms it covers. If you want the salary line, this actor is the one that keeps it.

### Pain points

- Ashby's job-board API is public but per-company — you need each company's board name and have to hit boards one at a time.
- Compensation, when an employer publishes it, is buried in a nested object (`compensation.compensationTierSummary` / `scrapeableCompensationSalarySummary`) — most scrapers don't dig it out, so it's invisible even when it's there.
- A posting can carry more than one location; keeping only the "primary" one drops the rest.
- Watching a list of companies for genuinely new roles means storing what you already pulled and diffing every run yourself.

### What we solve

- **Published salary, surfaced.** `compensationSummary` pulls Ashby's compensation object out to a flat, human-readable string whenever the employer publishes one; `null` when they don't.
- **Every location tag.** `secondaryLocations` returns the full array of additional locations Ashby attaches to a posting, alongside the primary `location`.
- **Multi-board in one run.** Pass a list of `companies`; a board name that fails or was renamed is skipped with a logged warning, not a fatal error — the rest of your list still comes back.
- **Dedup built in.** `onlyNewSinceLastRun` (default on) persists the last 20,000 posting ids seen, so a recurring schedule only bills for postings that are genuinely new.

### Who uses it

Recruiters and sourcers, comp-data and salary-benchmark products, job-board and ATS aggregators, sales teams tracking a target account's hiring, and anyone building a "new roles at companies I follow" alert.

### Company bundles - no company list needed

You do not need to know a single board slug to run this. Pick a bundle in `presetLists`, hit Start, and you get live postings straight away.

| Bundle | Ashby boards | What it covers |
|---|---:|---|
| `top-tech` | 19 | Large, well-known technology employers. |
| `ai-ml` | 41 | AI labs, ML infrastructure and applied-AI companies. |
| `devtools` | 33 | Developer tools, infrastructure, observability and security. |
| `yc-backed` | 32 | Y Combinator alumni, from recent batches to public companies. |
| `fintech` | 23 | Payments, banking, lending, crypto and financial infrastructure. |
| `remote-first` | 20 | Companies that hire remote-first or distributed by default. |

Every slug in a bundle was checked against its live board on 2026-08-24 - a 200 response with at least one open role. Each bundle file carries its own `verifiedAt` date, and any board that stops answering is reported in the run's `FAILED_TARGETS` record instead of silently shrinking your results.

Bundles merge with anything you list yourself and are deduped, so you can start from a bundle and add your own companies to it.

### Search every board we know - no company list at all

Turn on **Search every board we know** and you do not supply companies, slugs or bundles. The run queries our index of **615 verified ashby boards** - **14,884 open roles** at the last check on 2026-08-24 - and applies your title, location and remote filters across all of them.

Ask it things a company list cannot answer:

- every remote senior backend role posted this week
- everything matching "machine learning" across every board in the index
- new postings only, across the whole index, on a daily schedule

| ATS | Boards |
|---|---:|
| ashby | 615 |

Every board in the index answered its ATS with at least one open role when the index was built. Boards are fetched most-active first and capped by **Maximum boards to search** (default 200), because each board is one request. The index is rebuilt on a refresh sweep; a board that stops answering three sweeps running is retired from it, and anything that fails during your run is reported in `FAILED_TARGETS` rather than silently shrinking your results.

### Input

| Field | What it does |
|---|---|
| `searchIndex` | **No company list at all.** Query every board in the shipped index and apply your filters across all of them. See the section above. |
| `maxBoards` | Only used with `searchIndex`. Each board is one request, so this caps run length. Boards are searched most-active first. |
| `presetLists` | **Start here if you have no company list.** One or more verified company bundles; every board in them was checked against the live API. Merges with anything you list yourself, deduped. |
| `companies` | One or more Ashby job-board names — the slug in `jobs.ashbyhq.com/<name>` (e.g. `Ramp`). |
| `maxPerCompany` | Stop after this many jobs from each board, so one large employer cannot fill the whole result. Recommended with bundles. Empty = no cap. |
| `titleIncludes` | Keep only jobs whose title contains one of these keywords (case-insensitive). Empty = all. Applied **before** billing. |
| `locationIncludes` | Keep only jobs whose location contains one of these keywords. Empty = all. Applied **before** billing. |
| `remoteOnly` | Keep only remote-flagged roles (or locations mentioning remote). Applied **before** billing. |
| `postedWithinDays` | Keep only postings published in the last N days — `7` gives you this week's roles. Postings on boards that publish no date are left out, because they cannot be shown to be recent. Applied **before** billing. |
| `includeDescription` | Include each posting's plain-text description. Off by default for lean records. |
| `includeSalary` | Read Ashby's structured pay into the salary fields, falling back to the posting body. On by default; costs no extra requests. |
| `onlyNewSinceLastRun` | Recommended for schedules — skips jobs already returned by a previous run, so you're only charged for genuinely new postings. Default `true`. |
| `maxItems` | Stop after this many normalized records. |
| `syncEnabled` | Optional, off by default. Also send each new job straight to a connected app — see [Sync](#sync-to-your-crm-notion-hubspot-airtable-or-supabase-optional) below. |
| `syncDestination`, `syncWriteTool`, `syncFieldMap`, `syncExtraArgs` | Only used when `syncEnabled` is on. |

### Output

| Field | Description |
|---|---|
| `jobId` | Ashby's posting id (UUID). |
| `company` | The board name you queried. |
| `title` | Job title. |
| `location` | Primary posting location. |
| `secondaryLocations` | **Array** — every additional location tag on this posting. |
| `department` | Department, when Ashby provides one. |
| `team` | Team, a finer-grained tag than department. |
| `employmentType` | e.g. `FullTime`. |
| `remote` | Boolean, from Ashby's `isRemote` flag. |
| `postedAt` | Publish timestamp. |
| `compensationSummary` | **Published salary/comp range as a string**, when the employer discloses one; `null` otherwise. |
| `url` | Public posting URL. |
| `applyUrl` | Direct apply-form URL. |
| `description` | Plain-text description, only when `includeDescription` is on; otherwise `null`. |
| `source` | Always `"Ashby"`. |
| `workplaceType` | Ashby's own label — `Remote`, `Hybrid` or `OnSite`. |
| `equityOffered` | `true` when the compensation block includes an equity component. |
| `salaryMin` / `salaryMax` | Pay range as plain numbers, so you can sort and filter on it. `null` when the posting does not state pay — never a guess. |
| `salaryCurrency` | ISO code (`USD`, `EUR`, `GBP`, …) where the posting makes it clear. |
| `salaryInterval` | `YEAR`, `MONTH`, `WEEK`, `DAY` or `HOUR`. |
| `salaryText` | The pay exactly as the posting worded it, kept so you can audit the parse. |
| `salarySource` | `ats` when the board published structured pay, `ats-summary` or `description` when it was read out of the posting text. `null` when no pay was found. |
| `cityDerived` / `regionDerived` / `countryDerived` / `countryCodeDerived` | The location string split into parts, so "Austin, TX" is filterable by state and country instead of by substring. |
| `locationType` | `REMOTE`, `HYBRID` or `ONSITE`. |
| `seniorityLevel` | `INTERN`, `JUNIOR`, `SENIOR`, `STAFF`, `PRINCIPAL`, `MANAGER`, `DIRECTOR`, `VP` or `EXECUTIVE`, read from the title. `null` when the title carries no signal. |
| `employmentTypeNormalized` | `FULL_TIME`, `PART_TIME`, `CONTRACT`, `INTERN`, `APPRENTICESHIP` or `VOLUNTEER` — one enum across every ATS, so records are comparable. |
| `daysSincePosted` | Whole days since the posting went live, precomputed. |
| `organizationUrl` | The public careers board this posting came from. |

**Pay coverage.** Ashby publishes structured pay on most postings, so this is the highest-coverage source in the family — measured at 129 of 135 postings on a live board in 08/2026.

This is a real record, captured live from the `Ramp` board on 08/21/2026 — not a placeholder:

```json
{
  "presetLists": ["ai-ml"],
  "jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "company": "Ramp",
  "title": " Security Engineer, Cloud",
  "location": "New York, NY (HQ)",
  "secondaryLocations": [
    "Remote (Canada)",
    "Remote (US)",
    "Miami, FL"
  ],
  "department": "Engineering",
  "team": "Backend",
  "employmentType": "FullTime",
  "remote": true,
  "postedAt": "2026-04-07T17:12:35.753+00:00",
  "compensationSummary": "$211.4K – $290.6K • Offers Equity",
  "url": "https://jobs.ashbyhq.com/Ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "applyUrl": "https://jobs.ashbyhq.com/Ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
  "description": null,
  "source": "Ashby",
  "salaryMin": 211400,
  "salaryMax": 290600,
  "salaryCurrency": "USD",
  "salaryInterval": "YEAR",
  "salaryText": "$211.4K – $290.6K • Offers Equity",
  "salarySource": "ats",
  "cityDerived": "New York City",
  "regionDerived": "NY",
  "countryDerived": "United States",
  "countryCodeDerived": "US",
  "locationType": "HYBRID",
  "seniorityLevel": null,
  "employmentTypeNormalized": "FULL_TIME",
  "daysSincePosted": 136,
  "organizationUrl": "https://jobs.ashbyhq.com/Ramp",
  "workplaceType": "Hybrid",
  "equityOffered": true
}
```

### Which actor do I want?

- **Tracking Ashby boards specifically, and want published salary or every location tag?** You're in the right place.
- **Tracking companies across Ashby *and* Greenhouse, Lever, SmartRecruiters, Recruitee or Workable in one feed?** Use [Job Postings Aggregator](https://apify.com/deadwood_data_solutions/jobs-aggregator) — one schema, one dedup layer, across all six ATS platforms. It fetches Ashby's compensation data too but drops `compensationSummary` and `secondaryLocations` to keep its schema consistent across ATSs — pull from here directly if you need salary.
- **Need a different single board?** [Greenhouse Jobs Scraper](https://apify.com/deadwood_data_solutions/greenhouse-jobs-scraper) (multi-department/office tagging) · [Lever Jobs Scraper](https://apify.com/deadwood_data_solutions/lever-jobs-scraper) (remote/hybrid/onsite detail) · [Workable Jobs Scraper](https://apify.com/deadwood_data_solutions/workable-jobs-scraper) (description, requirements and benefits, split out)

### Sync to your CRM, Notion, HubSpot, Airtable or Supabase (optional)

Turn on `syncEnabled` to also send each new job straight to a connected
app — free, with no extra charge. Connect the app under **Integrations** in
Apify Console, pick it as `syncDestination`, and set `syncWriteTool` to the
name of the tool that creates one record there (run the
[dataset-sync-connector](https://apify.com/deadwood_data_solutions/dataset-sync-connector)
Actor in `list-tools` mode against the same connector if you don't know the
name). A sync failure is logged as a warning and never blocks the dataset —
your jobs always land here first regardless of what the destination does.

### Pricing (Pay-Per-Event)

- **`query`** — charged once per run for the board poll, regardless of how many companies you list.
- **`job-record`** — charged per normalized job pushed (after dedup). This is the primary event.
- `apify-actor-start` (Apify-managed) — covers baseline compute per run.
- **Sync to a connected app (optional)** — free. No event is charged for records sent to a destination; it's an added convenience on top of the dataset you already paid for.

A daily monitor of a handful of companies returns a few new roles for pennies; a full-board pull of a large employer is a larger one-time run you control with `maxItems`.

### Source & reliability

Data comes from Ashby's public `api.ashbyhq.com/posting-api/job-board` service. No API key, no proxy needed. A board name that 404s or fails is skipped with a logged warning — it doesn't fail the rest of your run. Run `npm test` for the offline self-test covering the normalizer and its edge cases.

### FAQ

**How am I charged?**

A flat `query` event once per run, plus one `job-record` event per normalized job actually pushed to the dataset. With `onlyNewSinceLastRun` on (the default), a posting already returned in a previous run (tracked by id, for the most recent 20,000 seen) is skipped and not re-charged.

**Where does the data come from?**

Ashby's public Job Board API (`api.ashbyhq.com/posting-api/job-board`) — the same data Ashby's own hosted careers pages are built from. No API key or proxy required.

**How fresh is it?**

Live at request time — there's no bundled or cached dataset behind this actor, it calls Ashby directly on every run. Schedule it (daily or weekly) to keep catching new postings; `onlyNewSinceLastRun` means a recurring schedule only bills for roles you haven't seen yet.

**Do all jobs include salary?**

Only where the employer publishes compensation on the posting. When present it's surfaced as `compensationSummary`; otherwise the field is `null`. This is the only actor in this family that keeps that field at all — see [Which actor do I want?](#which-actor-do-i-want) above.

**Which actor do I want?**

See [Which actor do I want?](#which-actor-do-i-want) above — short version: single Ashby boards with salary detail, stay here; multiple ATS platforms in one feed, use [jobs-aggregator](https://apify.com/deadwood_data_solutions/jobs-aggregator) (no salary field).

***

*SEO keywords: Ashby jobs scraper, Ashby job board API, job posting scraper, salary data scraper, compensation data API, ATS scraper, new jobs feed, hiring signals, recruiting data*

# Actor input Schema

## `presetLists` (type: `array`):

Curated, verified company bundles. Pick one and run: every slug was checked against the live board on 2026-08-24, so you do not have to research any yourself. Combine with your own entries below if you want both.

## `searchIndex` (type: `boolean`):

Ignore company lists entirely and query our index of 615 verified ashby boards (14,884 open roles at last check, 2026-08-24). Combine with the title, location and remote filters below to pull, say, every remote senior backend role posted this week. Boards are fetched most-active first, up to "Maximum boards to search".

## `maxBoards` (type: `integer`):

Only used when the index search above is on. Each board is one request, so a higher number means a longer run. 200 covers the most active boards in the index.

## `companies` (type: `array`):

One or more Ashby job-board names — the slug in jobs.ashbyhq.com/<name> (e.g. Ramp).

## `maxPerCompany` (type: `integer`):

Stop after this many jobs from each board, so one large employer cannot fill the whole result. Recommended with bundles. Leave empty for no per-company cap.

## `titleIncludes` (type: `array`):

Keep only jobs whose title contains one of these (case-insensitive). Leave empty for all titles. E.g. engineer, product, sales.

## `locationIncludes` (type: `array`):

Keep only jobs whose location contains one of these (case-insensitive). Leave empty for all locations. E.g. new york, london, remote.

## `remoteOnly` (type: `boolean`):

Keep only jobs flagged remote (or whose location mentions remote).

## `postedWithinDays` (type: `integer`):

Keep only postings published in the last N days. 7 gives you this week’s roles. Postings on boards that publish no date are left out, because they cannot be shown to be recent. Leave empty for no date limit.

## `includeDescription` (type: `boolean`):

Include each posting's plain-text job description. Larger records.

## `includeSalary` (type: `boolean`):

Ashby publishes structured pay on most postings. This reads it into salaryMin / salaryMax / salaryCurrency / salaryInterval, falling back to the posting body when a board does not. No extra requests either way.

## `maxItems` (type: `integer`):

Stop after this many normalized records have been pushed. Leave blank for no limit.

## `onlyNewSinceLastRun` (type: `boolean`):

Recommended for scheduled runs. Uses persisted state to skip postings already returned earlier, so a recurring schedule only charges for genuinely new jobs.

## `syncEnabled` (type: `boolean`):

Send each new record to a connected destination (Notion, HubSpot, Airtable, Supabase, or any Apify MCP connector) in addition to the dataset. Free — this never triggers a billable event. A sync failure never blocks or fails the run.

## `syncDestination` (type: `string`):

Connect the app under Integrations in Apify Console first, then pick it here. Required only if 'Sync new records' is on.

## `syncWriteTool` (type: `string`):

Name of the destination's MCP tool that creates one record, e.g. 'create\_page' for Notion, 'insert' for Supabase. Run the dataset-sync-connector Actor in 'list-tools' mode against the same destination if you don't know it.

## `syncFieldMap` (type: `object`):

Maps destination argument names to this Actor's output field names, e.g. {"title": "legalName", "phone": "phone"}. Leave empty to pass each record through unchanged.

## `syncExtraArgs` (type: `object`):

Fixed arguments merged into every sync write call, e.g. {"database\_id": "abc123"} for Notion.

## Actor input object example

```json
{
  "presetLists": [
    "ai-ml"
  ],
  "searchIndex": false,
  "maxBoards": 200,
  "companies": [
    "Ramp"
  ],
  "maxPerCompany": 10,
  "remoteOnly": false,
  "includeDescription": false,
  "includeSalary": true,
  "maxItems": 500,
  "onlyNewSinceLastRun": true,
  "syncEnabled": false,
  "syncFieldMap": {},
  "syncExtraArgs": {}
}
```

# Actor output Schema

## `results` (type: `string`):

All normalized records from this run as JSON.

## `resultsCsv` (type: `string`):

All normalized records from this run as CSV.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "presetLists": [
        "ai-ml"
    ],
    "maxBoards": 200,
    "companies": [
        "Ramp"
    ],
    "maxPerCompany": 10,
    "maxItems": 500,
    "syncFieldMap": {},
    "syncExtraArgs": {}
};

// Run the Actor and wait for it to finish
const run = await client.actor("deadwood_data_solutions/ashby-jobs-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "presetLists": ["ai-ml"],
    "maxBoards": 200,
    "companies": ["Ramp"],
    "maxPerCompany": 10,
    "maxItems": 500,
    "syncFieldMap": {},
    "syncExtraArgs": {},
}

# Run the Actor and wait for it to finish
run = client.actor("deadwood_data_solutions/ashby-jobs-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "presetLists": [
    "ai-ml"
  ],
  "maxBoards": 200,
  "companies": [
    "Ramp"
  ],
  "maxPerCompany": 10,
  "maxItems": 500,
  "syncFieldMap": {},
  "syncExtraArgs": {}
}' |
apify call deadwood_data_solutions/ashby-jobs-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,deadwood_data_solutions/ashby-jobs-api"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/DF7tQjHYQAvmL0lwp/builds/s946PYALdi186Qq1J/openapi.json
