# Ashby Jobs Scraper: Salary, Equity & Job Alerts (`tallowlanelabs/ashby-jobs-scraper`) Actor

Scrape any company's Ashby job board through Ashby's official public API: salary, equity and bonus tiers, every location, remote/hybrid. Schedule it to get only new, updated and closed jobs. $1.50 per 1,000 jobs. Unofficial; not affiliated with Ashby.

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

## Pricing

from $1.50 / 1,000 job items

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: salary, equity and new-job alerts from any Ashby job board

**Get every job on any company's Ashby board (OpenAI, Ramp, Notion, Linear and thousands of startups) from Ashby's official public Job Posting API, with the full published pay package: salary range, equity, bonus and commission tiers. Schedule it and each run returns only new, updated and closed jobs.**

*By Tallow Lane Labs. Unofficial tool; not affiliated with, endorsed by or sponsored by Ashby.*

### Why this one

- **Full compensation, not just a salary string.** `salary` is normalized (min, max, currency, interval) for filtering; `compensation` keeps Ashby's summary, the `offersEquity` / `offersBonus` / `offersCommission` flags and **every tier** with each component's range.
- **Every location.** `location`, `secondaryLocations` (city, region, country) and Ashby's own `workplaceType` (remote, hybrid, onsite).
- **Change alerts.** `changes` mode returns only `new`, `updated` (with `changedFields` and `previous`) and `removed` jobs. An outage guard prevents false "everything closed" alerts.
- **Pay filters:** `salaryOnly` and `minSalary` (top of the yearly range).
- **Paste anything:** `https://jobs.ashbyhq.com/ramp`, a job link, `ramp`, `ashby:ramp`, or a careers page that embeds Ashby.
- **Official API, no proxies, no browser.** Runs in seconds at 256–512 MB.

### Use cases

- **Startup hiring signals** for sales teams and investors (Ashby is popular with fast-growing startups).
- **Salary and equity benchmarking** across companies and levels.
- **Recruiting and sourcing alerts** for target companies.
- **Job seekers:** daily alerts for remote roles above a pay floor.
- **Job boards and newsletters:** a clean feed with pay data.

### Quick start

1. Click **Try for free** and run the example board, or replace it with your own.
2. Add boards, e.g. `ramp`, `openai`, `https://jobs.ashbyhq.com/notion`.
3. Keep `mode: changes` and schedule daily; or use `snapshot` for a full list each run.

### Input example

```json
{
  "boards": ["https://jobs.ashbyhq.com/ramp", "openai", "ashby:notion"],
  "mode": "changes",
  "keywords": ["engineer"],
  "workplaceTypes": ["remote", "hybrid"],
  "minSalary": 180000
}
```

Main options: `boards` (leave empty for a 5-job demo), `mode`, `changeTypes`, `emitBaseline`, filters (`keywords`, `excludeKeywords`, `locations`, `departments`, `workplaceTypes`, `postedWithinDays`, `salaryOnly`, `minSalary`), `maxJobsPerBoard`, `includeDescriptions`, `descriptionFormats`, `redactContactInfo`, `includeUnlisted`, `stateStoreName` / `stateKeyPrefix`. See the **Input** tab for all fields.

### Output example

One record per job (or per change). Real record from a test run on Ramp's board (description shortened):

```json
{
  "recordType": "job",
  "id": "ashby:ramp:34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "ats": "ashby",
  "board": "ramp",
  "companyName": null,
  "jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "title": "Security Engineer, Cloud",
  "department": "Engineering",
  "team": "Backend",
  "employmentType": "full_time",
  "workplaceType": "hybrid",
  "isRemote": false,
  "location": "New York, NY (HQ)",
  "locations": ["New York, NY (HQ)", "Remote (Canada)", "Remote (US)", "Miami, FL"],
  "salary": { "min": 211400, "max": 290600, "currency": "USD", "interval": "year", "text": "$211.4K - $290.6K" },
  "compensation": {
    "summary": "$211.4K – $290.6K • Offers Equity",
    "offersEquity": true,
    "offersBonus": false,
    "offersCommission": false,
    "tiers": [{ "summary": "$211.4K – $290.6K • Offers Equity", "components": [
      { "type": "EquityPercentage", "summary": "Offers Equity", "min": null, "max": null, "currency": null, "interval": null },
      { "type": "Salary", "summary": "$211.4K – $290.6K", "min": 211400, "max": 290600, "currency": "USD", "interval": "year" }
    ] }]
  },
  "secondaryLocations": [{ "location": "Remote (US)", "city": null, "region": null, "country": "United States" }],
  "offersRemote": true,
  "workplaceTypeRaw": "Hybrid",
  "isListed": true,
  "postedAt": "2026-04-07T17:12:35.753Z",
  "url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
  "descriptionText": "ABOUT RAMP\n\nRamp is building the smart infrastructure for finance teams ...",
  "changeType": "baseline",
  "firstSeenAt": "2026-10-28T18:23:51.043Z",
  "detectedAt": "2026-10-28T18:23:51.043Z"
}
```

- `updated` records add `changedFields` and `previous`; `removed` records carry the last known values plus `lastSeenAt`.
- Board errors are `recordType: "error"` rows, never charged. `RUN_SUMMARY` has per-board counts.
- Dataset views: **Jobs & changes**, **Changes only**, **Compensation**, **Board errors**.

### Pricing (pay per event)

| Event | Price | When |
|---|---|---|
| Job item | **$0.0015** | Each job in `snapshot` mode, and each `baseline` job on a board's first `changes` run |
| Job change | $0.002 | Each `new`, `updated` or `removed` record later |
| Board check | $0.001 | Each board checked per run (per started 1,000 jobs) |
| Actor start | $0.005 | Once per run (same at 256, 512 or 1,024 MB) |

Not charged: error rows, failed boards, filtered-out jobs, unlisted jobs you didn't ask for.

- Empty-input demo (5 jobs): about $0.014.
- 6 companies, no changes: $0.011 per run (about $0.33 a month daily).
- OpenAI's full board (about 810 jobs) as a snapshot: about $1.22.
- Set Apify's **maximum cost per run** for a hard cap; the Actor stops cleanly and saves its state.

### Integrations

- **Make, Zapier, n8n:** Apify's official app/node → *Run Actor* `tallowlanelabs/ashby-jobs-scraper` → send each new job to Slack, Google Sheets, Airtable or your CRM.
- **Apify API and clients:** `POST https://api.apify.com/v2/acts/tallowlanelabs~ashby-jobs-scraper/run-sync-get-dataset-items`, or

```python
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("tallowlanelabs/ashby-jobs-scraper").call(
    run_input={"boards": ["openai"], "mode": "snapshot", "salaryOnly": True})
jobs = list(client.dataset(run["defaultDatasetId"]).iterate_items())
```

- **AI agents (MCP):** `https://mcp.apify.com?tools=tallowlanelabs/ashby-jobs-scraper` lets an AI assistant answer "what does OpenAI pay for research engineers?" from live data.
- **Webhooks, Slack, Google Sheets:** on the Actor's **Integrations** tab.

### FAQ

**Which companies use Ashby?** Many startups and scale-ups, for example OpenAI, Ramp, Notion, Linear, Perplexity and Cursor. If the careers link contains `jobs.ashbyhq.com/<name>`, it works.

**Do all jobs have salaries?** Only when the company publishes pay on Ashby. On Oct 29, 2026, for example, 161 of Ramp's 168 jobs and 646 of OpenAI's 812 had compensation; Linear's and Notion's had none.

**Why is `companyName` null?** Ashby's API doesn't return a display name; use `board`.

**How are updates detected?** Ashby only publishes a posting date, so the Actor compares content between runs and lists `changedFields`.

**Can I watch several lists separately?** Yes, give each task its own `stateStoreName` or `stateKeyPrefix`.

**Is it legal?** It reads Ashby's public, unauthenticated Job Posting API, which Ashby provides so published jobs can be shown anywhere. No recruiter names are returned. Use the data in line with Ashby's and employers' terms and laws such as GDPR and CCPA.

### Limits

Public postings only (unlisted with `includeUnlisted`); pay only when published; no `updatedAt` from Ashby. Memory: 512 MB handles boards up to about 2,000 jobs; use 1,024 MB for larger (`RUN_SUMMARY.memory` advises).

### Support

Open an issue on the **Issues** tab or email Tallow Lane Labs at wdeornellas+tallowlanelabs@gmail.com.

# Actor input Schema

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

One entry per company. Accepts an Ashby job-board URL (jobs.ashbyhq.com/ramp), a board name (ramp) or ashby:ramp, or a company careers-page URL (the Actor loads it once and looks for an embedded Ashby board). Optional: leave empty to run a quick 5-job demo from a public example board (snapshot mode).

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

"changes" (default, monitor) returns only jobs that are new, updated or removed since the last run for each board; the first run for a board returns the full baseline. "snapshot" returns every current job on every run (each record still says whether it is new, updated or unchanged).

## `changeTypes` (type: `array`):

Which changes to output in "changes" mode.

## `emitBaseline` (type: `boolean`):

In "changes" mode, the first run for a board has nothing to compare against. If on, all current jobs are output (charged as job items) with changeType "baseline". If off, the first run only stores the baseline silently (nothing output, nothing charged) and alerts start on the next run.

## `keywords` (type: `array`):

Only output jobs whose title contains at least one of these (case-insensitive). Leave empty for all.

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

Skip jobs whose title contains any of these.

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

Only output jobs whose location, city, region or country contains one of these strings, e.g. "New York", "Germany", "remote".

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

Only output jobs whose department or team contains one of these strings, e.g. "Engineering".

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

Only output these workplace types. Empty = all. "unknown" covers postings that do not say.

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

Only output jobs first published within this many days (where the ATS gives a date).

## `salaryOnly` (type: `boolean`):

Only output jobs where Ashby publishes a pay range.

## `minSalary` (type: `integer`):

Only output jobs whose posted yearly pay range reaches at least this amount (top of the range, in the posting's own currency). Jobs without a posted range, or with an hourly or monthly range, are skipped when this is set.

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

Cap the number of records output per board per run (after filters). Change detection still sees the whole board.

## `includeDescriptions` (type: `boolean`):

Include the full description. Turning this off makes runs faster and the dataset smaller.

## `descriptionFormats` (type: `array`):

Formats to include when descriptions are on.

## `redactContactInfo` (type: `boolean`):

Some employers put a recruiter email or phone in the public job text. Turn on to replace them with \[email removed] / \[phone removed].

## `includeUnlisted` (type: `boolean`):

Ashby marks some postings isListed=false (meant to be reachable only by direct link). They are skipped by default.

## `stateStoreName` (type: `string`):

Named key-value store that keeps each board's last-seen jobs between runs. Use a different name (or a key prefix) to run independent monitors.

## `stateKeyPrefix` (type: `string`):

Optional prefix so several saved tasks can monitor the same board independently.

## `resetState` (type: `boolean`):

Ignore stored state for the boards in this run and treat it as a first run.

## `detectFromCareerPages` (type: `boolean`):

For URLs that are not a Ashby domain, load the page once and look for an embedded Ashby job board.

## `boardConcurrency` (type: `integer`):

How many boards to process at once. Request spacing still applies. Memory depends on the jobs on these boards: 512 MB (default) fits up to 3,000 live jobs across the boards processed at once (no single board over 2,000); use 1024 MB above that, or set this to 1 for very large boards.

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

Global cap on in-flight requests across all ATS APIs.

## `removalSafetyMinJobs` (type: `integer`):

If a board that had at least this many jobs suddenly returns zero, removals are not reported that run (it is usually an API hiccup).

## Actor input object example

```json
{
  "boards": [
    "ashby:ramp"
  ],
  "mode": "snapshot",
  "changeTypes": [
    "new",
    "updated",
    "removed"
  ],
  "emitBaseline": true,
  "keywords": [],
  "excludeKeywords": [],
  "locations": [],
  "departments": [],
  "workplaceTypes": [],
  "salaryOnly": false,
  "maxJobsPerBoard": 5,
  "includeDescriptions": true,
  "descriptionFormats": [
    "text",
    "markdown"
  ],
  "redactContactInfo": false,
  "includeUnlisted": false,
  "stateStoreName": "ashby-jobs-scraper-state",
  "stateKeyPrefix": "",
  "resetState": false,
  "detectFromCareerPages": true,
  "boardConcurrency": 3,
  "maxConcurrency": 4,
  "removalSafetyMinJobs": 5
}
```

# Actor output Schema

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

All job and change records from this run in the default dataset.

## `runSummary` (type: `string`):

JSON summary of the run: boards checked, job counts, change counts and errors.

# 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": [
        "ashby:ramp"
    ],
    "mode": "snapshot",
    "maxJobsPerBoard": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("tallowlanelabs/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": ["ashby:ramp"],
    "mode": "snapshot",
    "maxJobsPerBoard": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("tallowlanelabs/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": [
    "ashby:ramp"
  ],
  "mode": "snapshot",
  "maxJobsPerBoard": 5
}' |
apify call tallowlanelabs/ashby-jobs-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tallowlanelabs/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/IZx3PiwvxU6mm418F/builds/JMvJOuesMj0CRrDgx/openapi.json
