# Ashby Jobs Scraper (`boncross/ashby-jobs`) Actor

Get every open job on an Ashby careers board. Paste the board URL and the scraper returns the job title, employer, location, team, contract type, posting date and the full advert. Filter by title, location or remote working, then export to CSV, JSON or Excel.

- **URL**: https://apify.com/boncross/ashby-jobs.md
- **Developed by:** [BonCross API](https://apify.com/boncross) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 94.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.70 / 1,000 job-details

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

### What is Ashby Jobs Scraper?

Many employers on Ashby publish pay ranges on their boards, and Ashby Jobs Scraper gets them out as numbers. On Ramp's board, 139 of 146 jobs published a range. You get each one as a minimum, a maximum, a currency and a period, beside the full advert, the department, the work arrangement and each location the job is open in.

Paste a board URL of the form `jobs.ashbyhq.com/company`. The whole board is read.

Three things it does that matter for analysis:

- **Salary ranges as numbers.** Where a job lists several pay bands, the row has the lowest minimum and the highest maximum in the first currency. Nothing is estimated: no published range means null. How often you'll see one depends on the employer.
- **Multi-location jobs handled properly.** 90 of Ramp's 146 jobs list more than one location. Ask for Miami and you find the 24 jobs open there, though not one of them names Miami first.
- **Remote that means remote.** `isRemote` is true for a remote job and for any job with a remote location among its locations. A hybrid job with no remote location is false.

It's quick as well: 72 boards at 10 jobs each returned 582 rows in 11 seconds on 2 October 2026.

**BonCross Verified** means we tested this actor against the live site before release and kept the record:

- 25 of 25 live checks on its inputs and filters passed on 7 October 2026
- 5 boards run the same day, one made up: 41 jobs, 1 error row
- 8 of 8 sampled links name the job they lead to
- Charges matched the rows: 1 start, 4 boards, 41 jobs

### What data can you extract from an Ashby board?

| Field | Field |
|---|---|
| 💼 Job title | 🏢 Employer |
| 📍 Location | Team or department |
| 📄 Full advert text | Advert HTML, optional |
| Contract type | Remote flag |
| 🏠 On-site, hybrid or remote | |
| 📅 Posted date | 🔗 Advert link |
| 💷 Salary from and to | Salary currency and period |
| Job id | Board name |
| Board URL | When the row was collected |
| Every location a job is open in | |

### How to scrape an Ashby board

1. [Create a free Apify account](https://console.apify.com/sign-up).
2. Open [Ashby Jobs Scraper](https://apify.com/boncross/ashby-jobs).
3. Paste the Ashby board URLs you want.
4. Filter by title, location or work type if you need to, and set a cap if you don't want every job.
5. Press Start. A 150-job board takes 2 to 4 seconds.
6. Download the rows as Excel, CSV or JSON, salary columns included.

### ⬇️ Input

You only need board URLs. They can also be loaded in bulk through Start URLs, from a file or a Google Sheet.

"Max jobs per board" limits each board. "Max jobs for the whole run" limits the total. At 0, the default for both, you get everything.

**One board**

```json
{
  "boardUrls": ["https://jobs.ashbyhq.com/ramp"],
  "maxItemsPerBoard": 200
}
```

**Engineering roles only, several boards**

```json
{
  "boardUrls": ["https://jobs.ashbyhq.com/ramp", "https://jobs.ashbyhq.com/openai"],
  "titleIncludes": ["engineer", "developer"],
  "maxItems": 500
}
```

**Hybrid or remote jobs only**

```json
{
  "boardUrls": ["https://jobs.ashbyhq.com/ramp"],
  "workplaceTypes": ["Hybrid", "Remote"],
  "includeUnknownWorkplace": false
}
```

**Remote jobs no more than a week old**

```json
{
  "boardUrls": ["https://jobs.ashbyhq.com/ramp"],
  "onlyRemote": true,
  "maxDaysOld": 7
}
```

**New postings only, for a scheduled run**

```json
{
  "boardUrls": ["https://jobs.ashbyhq.com/ramp"],
  "onlyNew": true,
  "dedupStoreName": "ashby-seen"
}
```

Filters you can add:

- **Keyword**, matched against job titles.
- **Title must contain** or **Title must not contain**, to keep or drop jobs by title words (case is ignored).
- **Location must contain**, matched against all of a job's locations and not only the first.
- **Work type**, which keeps jobs whose `workplaceType` is On-site, Hybrid or Remote. Jobs with no stated arrangement stay in unless you switch off "Keep jobs with no stated work type". One whose location or title says remote counts as Remote.
- **Remote jobs only**, which keeps jobs the employer marked as remote and drops jobs that don't say.
- **Posted within (days)**, where 0 keeps everything.

"Include advert text" is on unless you turn it off. "Include advert HTML" adds the board's original HTML and makes rows considerably larger.

#### For developers

- Keys: `boardUrls`, `startUrls`, `maxItemsPerBoard`, `maxItems`, `keyword`, `titleIncludes`, `titleExcludes`, `locationIncludes`, `onlyRemote`, `workplaceTypes`, `includeUnknownWorkplace`, `maxDaysOld`, `includeDescription`, `includeDescriptionHtml`, `onlyNew`, `dedupStoreName`, `maxConcurrency`, `delayMs`.
- A board link copied after filtering on the board's own page keeps its filters: department (with the teams under it), location, employment type and work type.
- `onlyNew` with a `dedupStoreName` skips jobs returned by an earlier run that used the same store name. Without a store name nothing is remembered between runs. The store is written every minute.
- Boards are read one after another, well under a second each on average.
- `maxConcurrency` (default 8) was the fastest level measured clean, and `delayMs` (default 0) adds a pause per request. You shouldn't need to touch either.
- Each row is saved as it's collected. A run that times out keeps what it delivered. A run the platform restarts picks up where it stopped and never delivers or charges a job twice.

### ⬆️ Output

One row per job. The Overview view shows title, employer, location, team, contract, remote flag and posting date.

```json
{
  "id": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "title": "Security Engineer, Cloud",
  "company": "ramp",
  "location": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)", "Remote (Canada)", "Remote (US)", "Miami, FL"],
  "department": "Engineering",
  "employmentType": "FullTime",
  "isRemote": true,
  "workplaceType": "Hybrid",
  "postedAt": "2026-04-07T17:12:35.753Z",
  "salaryMin": 211400,
  "salaryMax": 290600,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "description": "About Ramp\nRamp is building the smart infrastructure for finance teams…",
  "ats": "ashby",
  "tenant": "ramp",
  "scrapedAt": "2026-09-26T17:08:30.418Z",
  "boardUrl": "https://jobs.ashbyhq.com/ramp"
}
```

- `location` is the board's primary location. `locations` is every location the job is open in, primary first.
- `workplaceType` is the board's own `On-site`, `Hybrid` or `Remote`.
- `salaryMin`, `salaryMax`, `salaryCurrency` and `salaryPeriod` are filled when the board publishes a range and null otherwise. Equity is left out.
- `company` is the board's own name where Ashby gives one, and otherwise the board id from the URL.
- `description` is the advert as plain text. `descriptionHtml` is the same advert as the board's original HTML, present only when "Include advert HTML" is on.

### How much does it cost to scrape Ashby jobs?

| Event | Free plan | Starter | Scale | Business |
|---|---|---|---|---|
| `actor-start`, per run | $0.001 | $0.001 | $0.001 | $0.001 |
| `board-scanned`, per careers board read | $0.0002 | $0.0002 | $0.0002 | $0.0002 |
| `job-detail`, per job with the full advert | $0.001 | $0.0009 | $0.0008 | $0.0007 |
| `job-listed`, per job without the full advert | $0.0005 | $0.00045 | $0.0004 | $0.00035 |

Enterprise plans pay the Business price.

**$1.00 per 1,000 jobs** with the full advert on the Free plan ($0.70 on Business). If you only want titles, locations and salaries, turn the advert off and it's $0.50 per 1,000 on the Free plan, $0.35 on Business. The salary columns are in the row either way. No proxy is needed and none is charged, and platform usage is included.

- 500 jobs with full adverts from one board: $0.001 + $0.0002 + 500 × $0.001 = **$0.5012** ($0.3512 on Business).
- 500 jobs without the advert: $0.001 + $0.0002 + 500 × $0.0005 = **$0.2512** ($0.1762 on Business).

You're billed once per job: `job-detail` when the row has the advert (advert text or HTML switched on), `job-listed` when both are switched off, plus one `board-scanned` per board.

For a salary study, the $5 of free credits on Apify's Free plan each month goes a long way: about 9,700 jobs without adverts, or about 4,900 with them.

### How many jobs, and how fast?

Ramp's board had 147 jobs when measured. Runs on Apify, 2 October 2026:

| Run | Result |
|---|---|
| 1 board, 150 jobs | 150 rows in 2 to 4 seconds |
| 6 boards, 50 jobs each | 230 rows in 2 to 5 seconds |
| 72 boards, 10 jobs each | 582 rows in 11 seconds |

Several thousand boards fit inside the default one-hour run. The job id, link, title, employer, posting date and advert were on every row.

### FAQ

#### How complete is the salary data?

It depends on the employer, and Ashby employers often publish. On Ramp's board it was 139 of 146 jobs. In our varied release run across four other boards, salary was on about 15% of jobs. A job with no published range has null salary fields.

#### What happens when a job has more than one pay band?

The row has the lowest minimum and the highest maximum, in the first currency listed. Equity and bonus components aren't included.

#### Why does a hybrid job in New York show `isRemote: true`?

Because one of its other locations is remote. A job based at an office can also be open to remote candidates, and `locations` shows you where. Filter on `workplaceType` if you want the job's own stated arrangement.

#### How do I find all jobs open in one city?

Use "Location must contain". It checks all of a job's locations, so a job listed first under New York and also open in Miami is found by "Miami".

#### Do I need a proxy or an Ashby account?

No. The boards are public and nothing has to be set up.

#### What are the limits?

- Ashby boards only. Another platform's URL returns a row saying so, and the other boards in your list still run.
- Location and team are only as good as the employer's listing.
- Salary appears only where the employer published one.
- Applications can't be submitted through this actor.

#### Is scraping Ashby boards legal?

These are job adverts employers have chosen to publish, pay ranges included, and they're read without any login. Collecting public data is generally lawful in the UK and EU, and what you do with it is your responsibility.

### Feeding a pay benchmark or a job feed

For compensation benchmarking, schedule a weekly run over your list of boards with adverts off and send the dataset to Google Sheets or through Airbyte. For a new-jobs feed, switch on "Only jobs not seen before" and let a webhook or the Slack integration deliver each run.

From code, Apify's run-sync-get-dataset-items endpoint runs the actor and hands back the rows in one call. There's an `apify-client` for Python and one for JavaScript.

An AI agent connected to `https://mcp.apify.com` can call the actor as a tool and compare, say, the published pay for senior engineers across five companies.

### More from BonCross

- [ATS Jobs Scraper](https://apify.com/boncross/ats-jobs): Jobs from any company careers page.
- [Greenhouse Jobs Scraper](https://apify.com/boncross/greenhouse-jobs): Every open job from a Greenhouse careers board.
- [Lever Jobs Scraper](https://apify.com/boncross/lever-jobs): A Lever careers board for every open job.
- [Workday Jobs Scraper](https://apify.com/boncross/workday-jobs): Every open job from a Workday careers board.
- [CharityJob Scraper](https://apify.com/boncross/charityjob-jobs): UK charity and non-profit jobs from CharityJob.
- [Glassdoor Job Scraper](https://apify.com/boncross/glassdoor-jobs): Glassdoor job listings for a role and location.

### Your feedback

If a salary range here doesn't match what the board shows, send us the job link on the Issues tab in Apify Console. We read every report and check it against the live board.

# Changelog

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

# Actor input Schema

## `boardUrls` (type: `array`):

One or more Ashby careers boards, for example https://jobs.ashbyhq.com/ramp

## `startUrls` (type: `array`):

The same thing, but able to bulk-load Ashby boards from a file or a Google Sheet.

## `maxItemsPerBoard` (type: `integer`):

Row limit for each board. 0 means no limit.

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

Row ceiling across every board in the run. 0 means no limit.

## `keyword` (type: `string`):

Ashby returns the whole board, so this is matched against job titles.

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

Keep a job only if its title contains at least one of these words. Case-insensitive.

## `titleExcludes` (type: `array`):

Drop a job if its title contains any of these words. Case-insensitive.

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

Keep a job only if its location contains one of these words, for example "London".

## `onlyRemote` (type: `boolean`):

Keep only jobs the employer marked as remote. Jobs where the board says nothing about remote working are dropped, not assumed.

## `workplaceTypes` (type: `array`):

Keep only jobs with these working arrangements, as the employer states them (the workplaceType column). Leave empty for all. Jobs whose board does not say are kept unless you turn off the option below; a job with no stated arrangement whose location or title says remote counts as Remote.

## `includeUnknownWorkplace` (type: `boolean`):

Only used with Work type. On (the default): jobs whose board says nothing about the arrangement are kept, because many employers never state it. Off: only jobs that state one of the chosen arrangements are kept.

## `maxDaysOld` (type: `integer`):

Keep only jobs posted in the last N days. 0 keeps everything.

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

Include the job advert as plain text.

## `includeDescriptionHtml` (type: `boolean`):

Also include the advert's original HTML. Makes rows considerably larger.

## `onlyNew` (type: `boolean`):

Skip jobs returned by an earlier run that used the same store name below.

## `dedupStoreName` (type: `string`):

Named key-value store that remembers job ids between runs. Leave empty to remember nothing.

## `maxConcurrency` (type: `integer`):

How many requests run at once across the whole run. The default is the fastest level measured clean; lower it only if a run reports refusals or errors.

## `delayMs` (type: `integer`):

Optional pause before each call, in milliseconds. 0 (the default) runs as fast as the parallel limit allows.

## Actor input object example

```json
{
  "boardUrls": [
    "https://jobs.ashbyhq.com/ramp"
  ],
  "startUrls": [],
  "maxItemsPerBoard": 0,
  "maxItems": 0,
  "keyword": "",
  "titleIncludes": [],
  "titleExcludes": [],
  "locationIncludes": [],
  "onlyRemote": false,
  "workplaceTypes": [],
  "includeUnknownWorkplace": true,
  "maxDaysOld": 0,
  "includeDescription": true,
  "includeDescriptionHtml": false,
  "onlyNew": false,
  "dedupStoreName": "",
  "maxConcurrency": 8,
  "delayMs": 0
}
```

# Actor output Schema

## `dataset` (type: `string`):

Every job the run returned.

## `stats` (type: `string`):

Counts for the run.

# 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 = {
    "boardUrls": [
        "https://jobs.ashbyhq.com/ramp"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("boncross/ashby-jobs").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 = { "boardUrls": ["https://jobs.ashbyhq.com/ramp"] }

# Run the Actor and wait for it to finish
run = client.actor("boncross/ashby-jobs").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 '{
  "boardUrls": [
    "https://jobs.ashbyhq.com/ramp"
  ]
}' |
apify call boncross/ashby-jobs --silent --output-dataset

```

## MCP server setup

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

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/18YQx0SFk5nSGp7cb/builds/q0jggOtu4yt85lfd1/openapi.json
