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

Type company names or paste Ashby job-board URLs and get every open job from those companies' Ashby career pages: full descriptions, structured salary where published, one clean format. Filter by keyword, location, remote or date, or get only jobs posted since your last run.

- **URL**: https://apify.com/humble-echidna/ashby-jobs.md
- **Developed by:** [Michael Costa](https://apify.com/humble-echidna) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 jobs

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

### What does Ashby Jobs Scraper do?

You tell it **which companies you care about**. It returns **every open job those companies post on Ashby**, straight from each company's own Ashby career page, in **one clean, consistent format**.

It is **not** a job-board search: it doesn't search Indeed, LinkedIn, or all of Ashby. Ashby doesn't publish a directory of the companies that use it, so you list the companies, and you get every job each of them has open, the moment it's listed.

**Three ways to name a company**, one per line, mixed freely:

| You type | Example | How it's found |
|---|---|---|
| **Company name** | `Linear` | Looked up on Ashby only. It's used only when the board's own company name agrees with what you typed. |
| **Ashby board URL** | `https://jobs.ashbyhq.com/supabase` or the short form `ashby:supabase` | Used as is. Links to a single job work too. |
| **Careers page** | `https://example.com/careers` | Scanned for a link to the company's Ashby board. Works when the page links it directly; many big-company pages load jobs with JavaScript, and then the name or the Ashby URL works better. |

Each run's log and the `RUN_STATS` record show what every name or page was matched to, and anything that couldn't be used, with the reason. A company on another hiring platform is skipped with a clear message and the rest of the run continues; for those, use [Company Career Page Jobs Scraper](https://apify.com/humble-echidna/ats-jobs), which covers Ashby and 5 other platforms with the same output format.

**Try it in one click:** the input comes pre-filled with 2 example companies, one typed as a name and one as a URL: Zapier and Modal, both of which publish structured salaries on Ashby. That's about 50 jobs, or roughly $0.10 (50 × $0.002 per job, plus $0.00005 for the run start). **Then replace them with the companies you actually want.**

It reads Ashby's **public job-board API**, the same data the company's own careers page shows. No login, no browser, no proxies, so runs are fast and cheap.

### Who it's for

Niche job boards and aggregators that list jobs from a known set of employers, recruiters and sales teams watching
target companies' hiring, and job seekers with a shortlist of companies. The recurring job: keep a list of companies
that hire on Ashby, run it daily with **Only jobs new since my last run**, and get each new posting once.

### Example: two companies, then a daily re-check

The pre-filled input, with **Only jobs new since my last run** turned on:

```json
{"companies": ["Zapier", "https://jobs.ashbyhq.com/modal"], "onlyNewJobs": true, "includeDescription": true}
```

It returned 48 jobs. One of them (real output from a local run on 2026-09-25; `description` shortened):

```json
{
  "id": "ashby:zapier:cb1aec2c-05cd-4598-8117-bd1f7ed9a49f",
  "platform": "ashby",
  "company": "zapier",
  "companyName": "Zapier",
  "title": "Finance Manager, Marketing",
  "department": "Finance",
  "location": "NAMER",
  "country": "United States",
  "workplaceType": "remote",
  "employmentType": "FULL_TIME",
  "postedAt": "2026-09-21T20:13:45.355000Z",
  "url": "https://jobs.ashbyhq.com/zapier/cb1aec2c-05cd-4598-8117-bd1f7ed9a49f",
  "salaryMin": 158300,
  "salaryMax": 237500,
  "salaryCurrency": "USD",
  "salaryPeriod": "YEAR",
  "salaryText": "$158.3K - $237.5K",
  "salarySource": "ats",
  "description": "AI AT ZAPIER\n\nAt Zapier https://zapier.com/about, we build and use automation every day to make work..."
}
```

Run again straight after, the same input returned **0 jobs**, so only the start fee was charged.

**What that cost:** 48 jobs × $0.002 = $0.096, plus the $0.00005 start fee; the repeat run
$0.00005. **A month, for example:** 50 companies with 2,000 open jobs between them, checked daily: the first run is
2,000 × $0.002 = $4.00; if 30 new jobs a day are posted after that, 870 × $0.002 = $1.74: **about
$5.74**, plus 30 starts ($0.0015).

### Run it on a schedule, or from your own code

1. Fill in the input and click **Start** once to check the results.
2. Save the input as a **task**, then add the task to a **schedule** in Apify Console (Schedules): for example daily, with **Only jobs new since my last run** on.
3. Pick up the results the way that suits you: download the dataset as JSON, CSV or Excel; fetch the latest run's results from the API (`GET https://api.apify.com/v2/actor-tasks/<task id>/runs/last/dataset/items?status=SUCCEEDED&format=csv`, with your API token); let a webhook tell your system when a run succeeds; or connect it to Make, Zapier or n8n through Apify's integrations. AI agents can call it through Apify's MCP server.

### Related actors

- [Company Career Page Jobs Scraper](https://apify.com/humble-echidna/ats-jobs): one run for companies on Greenhouse, Lever, Ashby,
  Recruitee, Personio and Teamtailor, with this same output format. Use it when your list mixes platforms.
- [Greenhouse Jobs Scraper](https://apify.com/humble-echidna/greenhouse-jobs): the same, for companies hiring on Greenhouse.
- [Lever Jobs Scraper](https://apify.com/humble-echidna/lever-jobs): the same, for companies hiring on Lever.
- [Remote Jobs Scraper](https://apify.com/humble-echidna/remote-jobs): remote jobs from We Work Remotely and Remote OK, same format.
- [Dataset Transformer: Filter, Dedupe, Convert](https://apify.com/humble-echidna/dataset-transform): filter and export to CSV for your board.

### Why this one?

- **Just type the company name.** "Linear" finds Linear's Ashby board, checked against the board's own company name, so a different company that happens to use that name as its board address is rejected.
- **Structured compensation.** When a company publishes pay on Ashby, you get it as numbers (min, max, currency, period) plus Ashby's own summary text.
- **Company names filled in.** Ashby's job feed leaves out the company's display name; it's taken from the board page, so `companyName` is filled.
- **Reliable.** A broken or unreachable company never affects the others in your run. A malformed job record is skipped instead of failing its company. Rate limits are handled automatically. The run log and the `RUN_STATS` record say exactly which company had a problem and why.
- **Clean, consistent fields.** Employment type is one of `FULL_TIME`, `PART_TIME`, `CONTRACT`, `TEMPORARY`, `INTERNSHIP`, `VOLUNTEER` and workplace type one of `remote`, `hybrid` or `onsite` (`null` when the job doesn't say); dates are ISO 8601 UTC. The format is identical to our other job scrapers, so results from several platforms can go in one table.
- **Salary where it's published.** Ashby's structured compensation is used when the company publishes it. Otherwise pay is extracted from pay-transparency text in the description ("$150,000 - $200,000 USD", "$28.50/hr"), and `salarySource` says which. Extraction is conservative: bonuses, stipends and funding amounts are ignored.

### Use cases

- **Job boards and aggregators:** keep listings fresh straight from the source.
- **Recruiters and sales teams:** track which Ashby companies are hiring for what (hiring signals).
- **Job alerts:** turn on **Only jobs new since my last run** and schedule it daily.
- **Salary and market research:** pay ranges as numbers, with currency and period.

### How much does it cost?

You pay per job returned: **$2.00 per 1,000 jobs**, plus $0.00005 each time a run starts. Filters are applied before you're charged, so you only pay for jobs you keep.

Two ways to cap a run, and it stops cleanly at whichever comes first:

- **Max jobs per run** in the input.
- **Maximum cost per run** in the run options. It stops fetching as soon as the limit is covered, so a capped run is also a fast one.

### Input

| Field | What it does |
|---|---|
| **Companies** | One per line: a company name, an Ashby board URL (or `ashby:slug`), or a careers page that links to its Ashby board. |
| Title keywords / Exclude keywords | Keep or drop jobs by words in the title (case-insensitive). |
| Locations | Keep jobs whose location or country contains any of these. Add `remote` to include remote jobs. |
| Remote jobs only | Only jobs marked remote. |
| Posted within (days) | Only recently published jobs. |
| Only jobs new since my last run | First run returns everything that matches; later runs return only jobs posted since. Remembered in your own Apify storage, per company and per filter combination. |
| Include full job description | Plain-text description (on by default). |
| Max jobs per run | Cap the total number of jobs across all companies. |
| Max jobs per company | Cap results per company. |

```json
{
  "companies": [
    "Linear",
    "https://jobs.ashbyhq.com/supabase",
    "ashby:openai"
  ],
  "keywords": [
    "engineer"
  ],
  "locations": [
    "San Francisco",
    "remote"
  ],
  "postedWithinDays": 30
}
```

### Output

Every job has the same fields. Fields Ashby doesn't publish for a job are `null`.

```json
{
  "id": "ashby:supabase:68a397e7-8a1a-4566-ae41-e8c61dd6ed93",
  "platform": "ashby",
  "company": "supabase",
  "companyName": "Supabase",
  "title": "Product Manager - Strategic Partner Integrations (Bay Area based)",
  "department": "Product",
  "team": "Product",
  "location": "Remote, Bay Area, CA",
  "locations": [
    "Remote, Bay Area, CA"
  ],
  "country": null,
  "remote": true,
  "workplaceType": "remote",
  "employmentType": "FULL_TIME",
  "postedAt": "2026-08-21T11:40:44.050000Z",
  "updatedAt": null,
  "url": "https://jobs.ashbyhq.com/supabase/68a397e7-8a1a-4566-ae41-e8c61dd6ed93",
  "applyUrl": "https://jobs.ashbyhq.com/supabase/68a397e7-8a1a-4566-ae41-e8c61dd6ed93/application",
  "salaryMin": 200000,
  "salaryMax": 270000,
  "salaryCurrency": "USD",
  "salaryPeriod": "YEAR",
  "salaryText": "$200K - $270K",
  "salarySource": "ats",
  "description": "ABOUT SUPABASE\n\nSupabase is the Postgres development platform, built by developers for…",
  "scrapedAt": "2026-09-24T15:58:36.895518Z"
}
```

`id` is stable across runs (`platform:company:jobId`), so you can use it to deduplicate.

### FAQ

**Can I get every job on Ashby, from every company?** No. This actor fetches the companies you list. Ashby doesn't publish a directory of its customers, so no scraper can list "all Ashby jobs" from Ashby itself. Use this one when you know which companies you want to track and need their complete, fresh listings.

**A company wasn't found.** The run log and `RUN_STATS` (`unresolved`) say why, and the rest of the run continues. Common reasons: it hires on another platform, its careers page loads jobs with JavaScript (type the name instead), or it has no open jobs right now.

**It matched the wrong company.** Names are only matched when the Ashby board's own company name agrees with what you typed, but two companies can share a name. Paste the exact Ashby URL to pin it down; `company` and `companyName` on every job show which board it came from.

**How does "Only jobs new since my last run" know about my last run?** It stores the IDs of the jobs it returned in a named key-value store (`ashby-jobs-seen`) in **your** Apify account, which persists between runs. Each company and filter combination has its own memory, so a daily "engineer" alert and a weekly "sales" alert on the same company don't interfere. Changing the filters starts a fresh search, and so does deleting that store. Jobs held back by **Max jobs per company** are not marked as seen, so they come through on a later run.

**A company that used to work now fails or returns fewer jobs.** Ashby can change its public feed without notice, so a site change is the most likely cause. The run log names the company and what didn't parse, and every other company in the run is unaffected. Please open an issue with the company so it can be looked at.

**Some of my companies use another platform.** A job-board URL from another platform is skipped with a message saying which platform it is, and a name that isn't found on this platform is reported as not found; the rest of the run continues. [Company Career Page Jobs Scraper](https://apify.com/humble-echidna/ats-jobs) reads Greenhouse, Lever, Ashby, Recruitee, Personio and Teamtailor in one run, with this exact output format.

**Is this legal?** It reads the public job-board data each company publishes on Ashby for its own careers page. It collects job postings only: no recruiter names, emails, or other personal data. It identifies itself honestly (User-Agent `HumbleEchidnaApify`) and follows Ashby's robots.txt: anything the site disallows is never requested, and the company is reported with the reason instead of being skipped silently; crawl delays are respected.

**Which addresses does it request?** Only public web addresses on the standard web ports (80 and 443). A careers
page on another port, or on a domain that points at a private network, is refused with the reason, and a careers
domain that doesn't exist is reported as such. Each site's robots.txt is read once per run; a site that asks for
more than 30 seconds between requests is reported rather than fetched slowly.

### Versions

Current version: **1.0**. See the Changelog tab for what changed in each version.

# Changelog

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

# Actor input Schema

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

One company per line, in any of three forms: a company name (e.g. Zapier), its Ashby job-board URL (e.g. https://jobs.ashbyhq.com/modal, or the short form ashby:modal), or its careers page if that page links to its Ashby board. Names are looked up on Ashby only. The list starts with 2 examples; replace them with your own.

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

Keep only jobs whose title contains any of these words (case-insensitive). Leave empty for all jobs.

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

Drop jobs whose title contains any of these words, e.g. intern, senior.

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

Keep only jobs whose location or country contains any of these, e.g. London, Germany, US. Add "remote" to also keep remote jobs.

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

Keep only jobs the ATS marks as remote.

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

Keep only jobs published in the last N days. Leave empty for all.

## `onlyNewJobs` (type: `boolean`):

The first run returns all matching jobs; later runs return only jobs posted since. The actor remembers what it already returned in a storage in your own Apify account, separately for each company and each combination of filters, so different searches never hide each other's jobs. Changing the filters starts a fresh search. Use it with a schedule for job alerts or hiring-signal monitoring.

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

Adds the plain-text job description. Turn off for faster, smaller results.

## `maxJobs` (type: `integer`):

Stop after this many jobs in total, across all companies. Leave empty for no limit. The run also stops cleanly at the maximum cost per run you set in the run options, whichever comes first.

## `maxJobsPerCompany` (type: `integer`):

Stop after this many matching jobs per company. Leave empty for no limit.

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

How many companies are fetched at the same time. The default suits almost every run; lower it if a career site starts rate-limiting.

## Actor input object example

```json
{
  "companies": [
    "Zapier",
    "https://jobs.ashbyhq.com/modal"
  ],
  "remoteOnly": false,
  "onlyNewJobs": false,
  "includeDescription": true,
  "maxConcurrency": 5
}
```

# Actor output Schema

## `jobs` (type: `string`):

No description

## `runStats` (type: `string`):

No description

# 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 = {
    "companies": [
        "Zapier",
        "https://jobs.ashbyhq.com/modal"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("humble-echidna/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 = { "companies": [
        "Zapier",
        "https://jobs.ashbyhq.com/modal",
    ] }

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

```

## MCP server setup

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