# Ashby Jobs Scraper with Salaries (`redomic/ashby-jobs-monitor`) Actor

Ashby jobs scraper for any startup careers board (OpenAI, Ramp, Notion and thousands more), with salary ranges as numbers: min, max, currency, period, plus equity and bonus flags. Flat rows with title, team, location, remote, apply link. Monitor mode returns only new, changed and removed jobs.

- **URL**: https://apify.com/redomic/ashby-jobs-monitor.md
- **Developed by:** [Redomic](https://apify.com/redomic) (community)
- **Categories:** Jobs, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.42 / 1,000 results

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Ashby Jobs Scraper with Salaries

![Ashby Jobs Scraper with Salaries](https://api.apify.com/v2/key-value-stores/gWkRolOrSe1OMioQD/records/ashby-jobs-monitor-hero)

For recruiters, VC and sales teams who track which startups are hiring: give it an Ashby job board (the name after `jobs.ashbyhq.com/`, for example `ramp` or `openai`) and get every open job as a flat row: title, department, team, location, remote/hybrid, employment type, the level read from the title (`seniority`), posting date, apply link and, when the company publishes it, **the salary range as numbers** (`salaryMin`, `salaryMax`, ISO `salaryCurrency`, `salaryPeriod`) plus whether the role offers equity, bonus or commission.

Real rows from 31 Oct 2026 (default options):

| board | title | team | location | workplaceType | seniority | salaryMin | salaryMax | salaryCurrency | salaryPeriod | offersEquity |
|---|---|---|---|---|---|---|---|---|---|---|
| ramp | Security Engineer, Cloud | Backend | New York, NY (HQ) | Hybrid | Mid | 211,400 | 290,600 | USD | year | true |
| ramp | Mobile Engineer, iOS | Software Engineering - Industry | New York, NY (HQ) | Hybrid | Mid | 189,000 | 330,000 | USD | year | true |
| ramp | Senior Product Marketing Manager - New Products | Product Marketing | New York, NY (HQ) | Hybrid | Senior | 156,000 | 210,000 | USD | year | true |
| openai | Research Engineer | Research | San Francisco | null | Mid | 250,000 | 445,000 | USD | year | true |
| openai | Researcher, Safety Training | Safety Systems | San Francisco | null | Mid | 380,000 | 500,000 | USD | year | true |

### What it does

- Salary as numbers: Ashby companies can publish structured pay ranges; this Actor requests them (`includeCompensation=true`) and returns min, max, ISO currency and period in their own columns. Counts from runs on 1 Nov 2026 with `{"boards": ["<board>"], "maxItems": 1000}`: OpenAI 645 of 812 jobs with a salary range; Ramp 111 of 171 with a salary range plus 59 sales roles with an OTE range. On-target earnings (base + commission, e.g. Ramp's "OTE $310K – $407K") are never reported as salary: they go to `oteMin`/`oteMax`, and `salaryBasis` says whether the salary is stated as base pay. Multi-tier ranges (e.g. per country or level) are kept in `salaryRanges`. When a company publishes no structured range but writes one in the description, it is read from the text (`salarySource: "description"`) and `salaryBasis` becomes `base` or `unspecified`, never `ote`; a published OTE tier stays in `oteMin`/`oteMax`. Companies that publish no pay (e.g. Linear) get `null`: nothing is estimated.
- Seniority on every row (`Intern`, `Junior`, `Mid`, `Senior`, `Lead/Principal`): read from the title (`Senior Product Marketing Manager - New Products` is `Senior`, `Software Engineering Intern, Backend` is `Intern`, `Principal Brand Designer` is `Lead/Principal`, no signal is `Mid`). Filter with `seniority` to get only the level you are hiring for.
- Monitor mode: Schedule it and get only what changed since the last run: new jobs, changed jobs (title, team, location, workplace type or pay range) and removed jobs, each tagged `new`, `changed` or `removed`. Unchanged jobs are free.
- Flat, clean columns: City, region and country split out of the address; secondary locations as a list; employment type as `Full-time`, `Internship`...; titles trimmed. No nested objects in the main table.
- Dead inputs are reported: A misspelled board (Ashby answers 404) or a board with no open jobs pushes a status row with the reason, and its count shows up as `boardsFailed`/`boardsEmpty` in the summary row. The run itself still succeeds (0 rows, $0 charged), so a scheduled monitor does not fail when a company pauses hiring or a board name is mistyped; filter on `_status` to alert on the input. A run fails only when Ashby cannot be reached or answers something that is not a job board. Example status row for a misspelled name:

  ```json
  {"_status": "failed", "board": "ramp-typo", "mode": "jobs", "rows": 0, "reason": "board not found (404): check the name after jobs.ashbyhq.com/"}
  ```
- One request per board: Ashby's public posting API returns a whole board, descriptions included, in one call. No proxy, no browser; runs take seconds.

### Input

| Field | Type | Default | Meaning |
|---|---|---|---|
| `boards` | array of strings | `["ramp", "openai"]` | Ashby board names (after `jobs.ashbyhq.com/`). Full board or job URLs are accepted |
| `mode` | enum | `monitor` | `monitor` returns only new, changed and removed jobs since the last run; `jobs` lists every open job |
| `search` | string | `""` | Keep jobs whose title, department, team, location, workplace type or description contains this text |
| `seniority` | array of strings | `[]` | Keep only these levels: `Intern`, `Junior`, `Mid`, `Senior`, `Lead/Principal`. Empty = every level |
| `includeDescription` | boolean | `false` | Add the full description (plain text and HTML) to each row. Same single request |
| `maxItems` | integer | `50` | Max job rows per board, 1 to 1000 (3 boards x 50 = at most 150 rows) |

```json
{ "boards": ["ramp", "openai"], "mode": "monitor", "maxItems": 50 }
```

### Output

One row per job, then one status row per board, then a summary row.

| Field | Example | Meaning |
|---|---|---|
| `id` | `34413f8d-26bf-4bbc-8ade-eb309a0e2245` | Ashby job id, stable across runs |
| `board` | `ramp` | The board you asked for |
| `title` | `Security Engineer, Cloud` | Job title |
| `department`, `team` | `Engineering`, `Backend` | As the company files the job |
| `location` | `New York, NY (HQ)` | Primary location as listed |
| `city`, `region`, `country` | `New York City`, `NY`, `USA` | From the job's address, when given |
| `secondaryLocations` | `["Remote (Canada)", "Remote (US)", "Miami, FL"]` | Other locations the job is open in |
| `isRemote` | `true` | Ashby's remote flag |
| `workplaceType` | `Hybrid` | `Remote`, `Hybrid` or `Onsite`, when set |
| `employmentType` | `Full-time` | `Full-time`, `Part-time`, `Contract`, `Internship` or `Temporary` |
| `seniority` | `Senior` | `Intern`, `Junior`, `Mid`, `Senior` or `Lead/Principal`. From the title; no signal is `Mid` |
| `publishedAt` | `2026-04-07T17:12:35Z` | Posting date, UTC |
| `url` | `https://jobs.ashbyhq.com/ramp/34413f8d-...` | Job posting |
| `applyUrl` | `https://jobs.ashbyhq.com/ramp/34413f8d-.../application` | Application form |
| `salaryMin`, `salaryMax` | `211400`, `290600` | Published base salary range (across tiers with the same currency and period) |
| `salaryCurrency`, `salaryPeriod` | `USD`, `year` | ISO 4217 code; `year`, `month`, `week`, `day` or `hour` |
| `salaryText` | `USD 211,400 - 290,600 per year` | The range as one string |
| `salaryBasis` | `base` | `base` (stated as base pay), `unspecified` (a salary range with no basis given) or `ote` (only an on-target-earnings range is published) |
| `oteMin`, `oteMax` | `310000`, `407000` | On-target earnings (base + commission) when the company publishes them, e.g. sales roles |
| `salarySource` | `ashbyCompensation` | `ashbyCompensation` (structured), `description` (read from the text) or `null` |
| `salaryRanges` | `[{"min": 211400, "max": 290600, "currency": "USD", "period": "year", "label": null}]` | Every published salary tier |
| `offersEquity`, `offersBonus`, `offersCommission` | `true`, `false`, `false` | From the compensation components; `null` when no compensation is published |
| `compensationSummary` | `$211.4K – $290.6K • Offers Equity` | Ashby's own one-line summary |
| `descriptionText`, `descriptionHtml` | `ABOUT RAMP ...` | Only with `includeDescription` |
| `changeType` | `new` | Monitor mode only: `new`, `changed` or `removed` |
| `scrapedAt` | `2026-10-31T00:00:00Z` | When the row was fetched |

Status rows carry `_status` (`ok`, `empty`, `failed`), `board`, `mode`, `rows`, `reason` and `total` (open jobs on the board). The last row has `_status: "summary"` with run totals and, in monitor mode, `new`, `changed`, `removed`, `firstRun`.

#### Seniority, real counts

With `{"boards": ["ramp"], "seniority": ["Senior"], "maxItems": 1000}` on the 31 Oct 2026 capture (171 open jobs) the 27 `Senior` rows are `Senior Manager, Account Executive | Mid-Market` and `Senior Product Marketing Manager - New Products` (27 of 171; the same capture has 5 `Intern`, 3 `Junior` and 115 `Mid`, plus 21 `Lead/Principal` including `Lead Recruiter | Design` and `Principal Brand Designer`).

### Pricing

`result`: $0.002 per job row delivered, nothing else: no start fee. 1,000 jobs ≈ $2.00. In monitor mode only new and changed jobs are charged; unchanged and removed jobs are free.

### Monitor recipe

1. Save a task with `{"boards": ["ramp", "openai", "notion"], "mode": "monitor"}`.
2. Schedule it daily (or hourly) in Apify Console.
3. Add a "run succeeded" webhook that sends the rows to Slack, Make or n8n, or export the dataset to Google Sheets.
4. Filter on `changeType`: `new` = fresh openings (a hiring signal), `changed` = retitled or re-priced roles, `removed` = filled or closed.

### FAQ

- **How do I find a board name?** Open the company's careers page; Ashby boards live at `jobs.ashbyhq.com/<name>`. Paste the whole URL if you like.
- **Can I search all Ashby companies at once?** No. Ashby's public API is per board; list as many boards as you need in `boards`.
- **Why is salary null for some companies?** They do not publish pay on Ashby and do not write a range in the description. The Actor does not estimate.

### What this does not do

It reads only Ashby's public posting API: no logins, no applicant data, no personal contact details. It does not guess missing salaries and does not search across companies you did not list.

Sibling Actors: **Greenhouse Jobs Scraper with Salaries** (the same row shape for Greenhouse boards) and **Google Jobs API Scraper**.

### Use it from AI agents (MCP) and Python

**AI agents.** Add this Actor as a tool in Claude, Cursor, VS Code or any MCP client through the Apify MCP server. The agent reads the input schema, so a plain request is enough.

```json
{
  "mcpServers": {
    "ashby-jobs-monitor": {
      "url": "https://mcp.apify.com?tools=redomic/ashby-jobs-monitor",
      "headers": {
        "Authorization": "Bearer <YOUR_APIFY_TOKEN>"
      }
    }
  }
}
```

**Python.** `pip install apify-client`, then:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("redomic/ashby-jobs-monitor").call(run_input={"boards": ["ramp"], "mode": "jobs", "maxItems": 20})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)
```

### More from us

- [ATS Jobs Search: Greenhouse, Lever & Ashby Jobs API](https://apify.com/redomic/ats-jobs-search): Search 328,000+ open jobs from 11,000+ companies on Greenhouse, Lever and Ashby in one run
- [Google Jobs Scraper - Job Postings API](https://apify.com/redomic/google-jobs): Google Jobs and job postings scraper: get job title, company, location, posted date, salary, full description and every apply link for any search
- [Google Trends API: Trending Now & Interest Over Time](https://apify.com/redomic/google-trends): Trending-now searches and new-trends alerts/monitoring per country with the news behind them, plus interest over time, interest by region, and related and rising queries for any keyword
- [Greenhouse Jobs Monitor & Scraper](https://apify.com/redomic/greenhouse-jobs-monitor): Monitor Greenhouse jobs from any company careers board (Airbnb, Stripe, Discord and thousands more)
- [Lever Jobs Scraper with Salaries](https://apify.com/redomic/lever-jobs-monitor): Lever jobs scraper for any company's careers page (Palantir, Spotify, Zoox, Shield AI and thousands more): flat rows with title, team, location, workplace, commitment, apply link, salary as numbers and the full description merged

# Changelog

This Actor's version history is a separate document: https://apify.com/redomic/ashby-jobs-monitor/changelog.md

# Actor input Schema

## `boards` (type: `array`):

Ashby job board names: the part after jobs.ashbyhq.com/, for example ramp, openai, notion. You can also paste full board or job URLs; only the name is kept.

## `mode` (type: `string`):

monitor reports only new, changed (including a changed pay range) and removed jobs since the last run of the same board and keyword; jobs lists every open job.

## `search` (type: `string`):

Keep only jobs whose title, department, team, location, workplace type or description contains this text. Case-insensitive. Leave empty for every open job.

## `seniority` (type: `array`):

Keep only jobs at these seniority levels. Empty = every level. Seniority is read from the job title (no signal = Mid).

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

Add the full job description (plain text and HTML) to every row. Off by default to keep rows small; the keyword filter searches descriptions either way. No extra request: the board comes in one response.

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

Upper limit on job rows per board (and so on what you pay): 3 boards x 50 = at most 150 rows. Default 50, maximum 1000.

## Actor input object example

```json
{
  "boards": [
    "ramp",
    "openai"
  ],
  "mode": "monitor",
  "search": "",
  "seniority": [],
  "includeDescription": false,
  "maxItems": 50
}
```

# Actor output Schema

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

All result rows from the default dataset.

# 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 = {
    "boards": [
        "ramp",
        "openai"
    ],
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("redomic/ashby-jobs-monitor").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 = {
    "boards": [
        "ramp",
        "openai",
    ],
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("redomic/ashby-jobs-monitor").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 '{
  "boards": [
    "ramp",
    "openai"
  ],
  "maxItems": 50
}' |
apify call redomic/ashby-jobs-monitor --silent --output-dataset

```

## MCP server setup

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

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/i8AaJBqaT2RGuBDdo/builds/sq0cbY4wBtcQMrvH5/openapi.json
