# Ashby Jobs Scraper - Every Opening From Any Ashby Board (`practical_ophthalmologist_iuq/ashby-jobs-scraper`) Actor

Salary data actually shows up here. Ashby boards publish compensation far more often than other ATS platforms - pull every opening with its range, team, location and apply link via the official public API. Paste domains.

- **URL**: https://apify.com/practical\_ophthalmologist\_iuq/ashby-jobs-scraper.md
- **Developed by:** [Scrappeer](https://apify.com/practical_ophthalmologist_iuq) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 job openings

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/platform/actors/running/actors-in-store#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 - Every Opening From Any Ashby Board

**A company that is hiring is a company that is spending. Pull every live opening from any Ashby board via the official public API, published salary ranges included. Paste a company domain - no board slug to look up.**

***

### What it does

Give it a list of Ashby board tokens. It calls the official public Ashby
board API for each one and returns every live opening in a flat,
spreadsheet-ready schema.

This reads the API Ashby already publishes for its customers' own careers
pages. That means **no proxy, no headless browser, no anti-bot arms race** — and
nothing that silently breaks when a page layout changes.

Ashby is the richest of the platforms: department, team, employment type, workplace type and **published salary ranges** all come through.

***

### Input

```json
{
  "boards": ["ramp.com", "https://jobs.ashbyhq.com/1x"],
  "titleKeywords": ["engineer"],
  "locations": ["Remote"],
  "postedWithinDays": 7
}
```

The board token is **the company slug in jobs.ashbyhq.com/<company>** — but you rarely need to look it up.
Paste the company **domain** (`acme.com`, `www.acme.com`) or the full careers URL
and the Actor pulls the company name out for you.

| Field | Default | Notes |
|---|---|---|
| `boards` | — | Required. Tokens or full careers URLs. |
| `titleKeywords` | — | Keep only titles containing any of these. |
| `excludeKeywords` | — | Drop titles or departments matching any of these. |
| `locations` | — | Keep only matching locations. |
| `departments` | — | Matches department **or** team. |
| `remoteOnly` | `false` | Remote positions only. |
| `postedWithinDays` | `0` | `0` = no limit. Set `1` for a daily new-jobs feed. |
| `includeDescription` | `false` | Adds full description text. Much larger results. |
| `onlyNewSinceLastRun` | `false` | Return only what opened or closed since the last run. |
| `maxJobsPerBoard` | `0` | `0` = no limit. The console starts at `10` so a first trial run stays cheap. |
| `concurrency` | `5` | Boards fetched in parallel. |

***

### Output

One row per opening:

| Field | Description |
|---|---|
| `companyName`, `boardToken`, `ats` | Who, and where it was read from |
| `jobId` | Stable ID on Ashby |
| `title` | Job title |
| `department`, `team` | Org placement |
| `employmentType` | Full-time, contract, intern… |
| `location`, `isRemote`, `workplaceType` | Where the work happens |
| `salary` | Compensation range, when published |
| `publishedAt`, `updatedAt` | ISO 8601 timestamps |
| `jobUrl`, `applyUrl` | Public posting and application links |
| `globalId` | `{ats}:{token}:{jobId}` — stable key for joining and deduping |
| `description` | Full text, only when requested |

Export as **Excel, CSV, JSON or XML**, or pull it through the API.

A board that cannot be found returns one row with an `error` and a hint, so a bad
token never silently vanishes from your results.

***

### Monitoring: only what changed

Set **`onlyNewSinceLastRun: true`** and schedule it. The first run records a
baseline and returns everything. Every run after that returns only:

- jobs that **opened** since your last run — `isNew: true`
- jobs that have since **closed** — `isClosed: true`

A morning with no hiring activity returns **nothing at all** and costs only the
start fee. You are never billed for re-reading a job you already saw.

Two things worth knowing:

- **Changing the boards or filters starts a fresh baseline.** Otherwise every
  job you stopped asking about would be reported as newly closed, which is a
  wrong answer rather than a noisy one.
- **A closed row carries what was recorded when the job was last seen** —
  company, title and link. Once a posting is gone there is nothing left to
  re-read, so the remaining fields are omitted rather than shown stale.

#### Without monitoring

`postedWithinDays: 1` also gives you a daily feed, based on each board's own
posted date. It is simpler, but it cannot tell you when a role closed, and it
depends on the company having set a date at all. Use `onlyNewSinceLastRun` if
you care about either.

***

### Quality notes

- Timestamps are normalised to ISO 8601, so date filters behave predictably.
- Postings that cannot be read are skipped and **reported in the log** rather
  than quietly dropped — and you are not billed for them.
- One malformed posting never takes down the rest of the board.

***

### Limits

- Ashby boards only. For a company on a different platform, see the
  multi-platform version of this Actor.
- Boards set to private or password-protected are not accessible.
- `department` and `team` are only as good as what the company fills in.

***

### The endpoint this reads

```
https://api.ashbyhq.com/posting-api/job-board/<company>
```

That is Ashby's own public board API - the one that fills its customers'
careers pages. It needs no key, no login and no proxy, which is why this Actor
does not use any.

***

### FAQ

**Does Ashby have a public jobs API?**
Yes, for job boards. The endpoint above returns the live postings for one
company as JSON. It is public because the careers page itself is public.

**Do I need an API key, a login, or a proxy?**
No. None of the three.

**Where do I find the board token?**
You do not have to. Paste the company's domain, the URL of any job posting, or
the identifier itself - `ramp.com` is a working example - and this Actor
resolves it. If you already know the token - the company slug in jobs.ashbyhq.com/<company> - that works too.

**Why would I use this instead of calling the endpoint myself?**
For one company, you probably should not - it is one HTTP request, and this
README just told you the URL. This Actor earns its keep at the point where that
stops being true: dozens of companies at once, tokens you would otherwise have
to look up by hand, a flat schema shared across six ATS platforms, a
`globalId` you can join on, and a mode that returns only what changed since the
last run instead of the whole board every morning.

**Can I get only the jobs that opened or closed since last time?**
Yes. Set `onlyNewSinceLastRun` to true and each run returns the new postings,
plus a row for every posting that disappeared. See the monitoring section above.

**What does a run cost?**
You pay per row returned - about a tenth of a cent each - plus a fraction of a
cent to start the run. A company with 40 openings lands around five cents. With
`onlyNewSinceLastRun` on, a scheduled daily run usually returns a handful of
rows rather than the whole board.

**A company is not found. Why?**
Either it is not on Ashby - most large enterprises use Workday, Taleo, iCIMS
or SuccessFactors - or its board is private. The run tells you which, per
company, in a row you can read rather than a silent gap.

***

### Support

A board that will not resolve? Open an issue on the **Issues** tab with the
careers URL.

# Actor input Schema

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

Company domains, names, or careers URLs - paste your list as-is. acme.com, www.acme.com and the full board URL all work. The board token is the company slug in jobs.ashbyhq.com/<company>.

## `titleKeywords` (type: `array`):

Keep only jobs whose title contains any of these.

## `excludeKeywords` (type: `array`):

Drop jobs whose title or department matches any of these.

## `locations` (type: `array`):

Keep only jobs whose location matches any of these.

## `departments` (type: `array`):

Keep only jobs in a matching department or team.

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

Return only remote positions.

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

Only jobs posted in the last N days. 0 means no limit. Set 1 for a daily new-jobs feed.

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

Add the full job description text. Makes results much larger.

## `outputMode` (type: `string`):

jobs = one row per opening. companies = one row per board: how many roles are open, which teams are growing, what opened since your last run - built for account lists rather than job lists. both = job rows followed by the summaries.

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

Return only jobs that opened since your last run (isNew) and jobs that have since closed (isClosed). The first run records a baseline and returns everything. Built for a daily schedule: a run with no changes returns nothing and costs only the start fee. Changing the boards or filters starts a fresh baseline.

## `maxJobsPerBoard` (type: `integer`):

Cap results per board. 0 means no limit. Starts at 10 so a first trial run stays cheap - clear it to get everything.

## `concurrency` (type: `integer`):

How many boards to fetch in parallel.

## Actor input object example

```json
{
  "boards": [
    "ramp.com",
    "https://jobs.ashbyhq.com/1x"
  ],
  "remoteOnly": false,
  "postedWithinDays": 0,
  "includeDescription": false,
  "outputMode": "jobs",
  "onlyNewSinceLastRun": false,
  "maxJobsPerBoard": 10,
  "concurrency": 5
}
```

# Actor output Schema

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

Every row the run produced, newest board first.

# 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.com",
        "https://jobs.ashbyhq.com/1x"
    ],
    "maxJobsPerBoard": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("practical_ophthalmologist_iuq/ashby-jobs-scraper").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.com",
        "https://jobs.ashbyhq.com/1x",
    ],
    "maxJobsPerBoard": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("practical_ophthalmologist_iuq/ashby-jobs-scraper").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.com",
    "https://jobs.ashbyhq.com/1x"
  ],
  "maxJobsPerBoard": 10
}' |
apify call practical_ophthalmologist_iuq/ashby-jobs-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/uKHwzwBj5SzXhIhZS/builds/YVjkyzMR34dSPQfZr/openapi.json
