# Ashby Jobs + Expiry History — top AI companies, with salary (`hyperbach/ashby-jobs-history`) Actor

Hiring at the top AI-native companies — OpenAI, Notion, Ramp, Cursor, Linear, Supabase and more — straight from each company's own Ashby board, with salary bands. Exact-employer data, not a job-board aggregation, kept as roles open and close. Served at database speed.

- **URL**: https://apify.com/hyperbach/ashby-jobs-history.md
- **Developed by:** [Hyperbach](https://apify.com/hyperbach) (community)
- **Categories:** Jobs
- **Stats:** 3 total users, 2 monthly users, 21.1% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 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/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 + Expiry History — top AI companies, with salary

**The hiring of the top AI-native companies — OpenAI, Notion, Ramp, Cursor, Linear, Supabase, and more — straight from each company's own Ashby board, with salary.** Not a job-board aggregation: every posting is tied to the exact employer, kept as roles open and close. Served at database speed.

### Why this feed, not a generic ATS scrape

- **Exact employer, from its own ATS.** Every posting comes from a specific company's own Ashby board (`company_slug` is the employer, `url` is its Ashby page) — not a name guessed across an aggregator. When it says OpenAI, it *is* OpenAI.
- **A curated set of the companies people actually watch.** Maintained fresh across the top AI-native and dev-tools employers — OpenAI, Notion, Ramp, Cursor, Linear, Supabase, Vanta, Sierra, Harvey, Modal, Replicate, PostHog — so you don't have to hunt down board tokens. The set grows.
- **Salary, where the company posts it.** About two-thirds of roles carry a compensation band with currency and period — Ashby surfaces it and so do we. Real numbers from real employers, not estimates.
- **Kept after they close.** When a role leaves the board it's retained with the date it closed, so you can see how long positions stay open and which roles a company fills fast — history a live scrape structurally cannot produce.
- **Database speed, and unblockable.** You read our maintained archive, not the ATS, so a run answers as a query and is never rate-limited or blocked.

### Who it's for

- **Recruiters & talent researchers** — track exactly what the top AI companies are hiring, in which functions, and how fast roles turn over — a competitive-hiring and sourcing signal you can act on.
- **Compensation analysts** — real salary bands from OpenAI, Notion, Ramp and peers, by role and location — benchmark against the companies setting the market, not survey estimates.
- **Market & investment researchers** — which AI-native companies are scaling, in what functions (eng vs GTM vs ops), and when they pause hiring — read straight off the per-company feed and its close dates.
- **Founders & sales teams** — who's hiring what at the companies you sell to — an org-growth signal tied to the exact employer and its own careers page.

### Quick start

**One company's board**

```json
{
  "company": "openai"
}
```

**Engineering roles at a company**

```json
{
  "company": "notion",
  "keyword": "engineer"
}
```

**What has closed**

```json
{
  "company": "ramp",
  "status": "gone"
}
```

### Output

One record per posting:

| field | meaning |
|---|---|
| `job_id` | Stable unique key for the posting, `<source>:<id>`. Deduplicate across runs and join to your own data on this. |
| `url` | Canonical posting URL. |
| `title` | Job title. |
| `company` | Employer name as the board lists it. |
| `company_domain` | The employer's own web domain, resolved from the posting. |
| `company_slug` | The board's identifier for the employer. |
| `company_logo_url` | Employer logo, when the board provides one. |
| `location_restriction` | Where the posting says the role may be based. |
| `employment_type` | Full time, contract, and so on, as declared by the employer. |
| `categories` | Category tags the board assigned to the posting. |
| `description` | Full posting description. |
| `salary_min` | Lower bound of the advertised band. |
| `salary_max` | Upper bound of the advertised band. |
| `salary_currency` | ISO currency of the advertised band. |
| `salary_unit` | Period the band refers to (e.g. `YEAR`, `HOUR`). |
| `posted_at` | When the posting first appeared. |
| `first_seen_at` | When our archive first captured it. |
| `status` | `live` while listed, `gone` once removed. |
| `gone_reason` | Set when the role leaves the board. Ashby posts no expiry date, so this is `unknown` — it records that the role closed (filled or pulled), and `status`/the timestamps tell you when. |
| `gone_at` | The date the posting was first observed gone. Null while it is live. Naukri overwrites its expired list nightly, so this is only knowable to whoever was watching that day. |
| `apply_url` | Where to apply for the role. |
| `enriched` | Whether salary, category and logo have been resolved yet — a posting is returned as soon as it is found, enriched or not. |

Example record:

```json
{
  "job_id": "ashby_ats:10c7fcdc-1f07-41e3-8e37-4d930898a648",
  "url": "https://jobs.ashbyhq.com/openai/10c7fcdc-1f07-41e3-8e37-4d930898a648",
  "title": "Growth Product Marketing Manager",
  "company": "openai",
  "company_domain": "https://openai.com/",
  "company_slug": "openai",
  "company_logo_url": "https://app.ashbyhq.com/api/images/org-theme-social/0c0ad039-c59c-4f86-b7ea-e7bebb8a8041/33531299-c352-4642-8992-b14bae985251/97265fdb-643c-406f-98e2-fa0372f46a17.png",
  "location_restriction": "San Francisco",
  "employment_type": "FullTime",
  "categories": [
    "Applied AI"
  ],
  "description": "About the Team\n\nThe Growth team drives user and revenue growth across ChatGPT’s consumer and business segments as well as other OpenAI products worldwide. We operate across the ful …(truncated for display)",
  "salary_min": 144000.0,
  "salary_max": 210000.0,
  "salary_currency": "USD",
  "salary_unit": "YEAR",
  "posted_at": "2026-07-29 21:07:17.483+00",
  "first_seen_at": "2026-07-26 19:42:30.661453+00",
  "status": "live",
  "gone_reason": null,
  "gone_at": null,
  "apply_url": "https://jobs.ashbyhq.com/openai/10c7fcdc-1f07-41e3-8e37-4d930898a648/application",
  "enriched": true
}
```

### Pricing

**$0.003 per result** — pay only for the postings a run returns, **no start fee**. Above the $0.001–0.002 generic ATS scrapers because you get a *curated, maintained* set of the companies worth watching, the **salary band** where they post it, and the **archive** (roles kept after they close) — none of which a live board scrape gives you. A rung below the Himalayas archive ($0.004).

### Usage patterns

- **Track one company** — Pass `company` (e.g. `openai`, `ramp`, `notion`) to get that employer's full current board, then poll to watch it change over time.
- **Salary benchmarking** — Pull a role across the set and read `salary_min`/`salary_max` (annual, with currency) to benchmark comp at the companies setting the market.
- **What closed, and when** — Set `status=gone` to see roles that have left the board, with the date — a company that fills a role in two weeks is a different signal than one that leaves it open for months.

### Input configuration

| field | type | default | what it does |
|---|---|---|---|
| `keyword` | `string` |  | Matches job title or category. |
| `company` | `string` |  | Employer name as the board lists it. Partial matches work. |
| `postedSince` | `string` |  | ISO timestamp. Leave empty and the Actor remembers where it stopped, returning only new postings each run. |
| `goneSince` | `string` |  | Only postings observed dead at or after this ISO timestamp. The query this archive exists to answer. |
| `status` | `live` / `gone` / `any` | `"live"` | 'Gone' returns postings that have been removed, with why — the part no live scraper can give you. |
| `location` | `string` |  | Substring match on the posting's location. |
| `remoteOnly` | `boolean` | `false` | Only postings the employer marks as remote. |
| `descriptionSearch` | `string` |  | Substring match inside the job description. |
| `excludeTitle` | `string` |  | Drop postings whose title contains this. |
| `maxItems` | `integer` | `500` | Upper bound on results returned in a single run. |
| `fullRefresh` | `boolean` | `false` | By default each run returns only what appeared since your last one, so a schedule never re-delivers the same page. Tick this to ignore that bookmark and re-pull the full result set for your filters — and it clears the stored cursor, so your next ordinary run starts clean. |
| `enrichedOnly` | `boolean` | `false` | Return only postings whose salary, employment type and description have been parsed. Off by default: an unenriched record still carries title, company, location and dates, and withholding it trades a real answer for a slower better one. |

### FAQ

**How is this different from a generic Ashby scraper?**

Generic scrapers take any board token and return whatever is live right now. This is a *maintained, curated* feed of the top AI-native companies with the **salary band** where posted, and it's an **archive**: roles are kept after they close (`status=gone`), so you get hiring *history* — which roles turn over fast, when a company stops hiring — that a live scrape can't produce.

**Which companies are covered?**

A curated set of top AI-native and dev-tools employers — currently OpenAI, Notion, Ramp, Cursor, Linear, Supabase, Vanta, Sierra, Harvey, Modal, Replicate and PostHog — and growing. Filter with `company`.

**Does every job have a salary?**

About two-thirds do — Ashby surfaces the band only when the company sets one, so roles without a posted band come back with salary null (never guessed). Currency is included and the period is annual.

**Is the archive live, and what does `gone` mean?**

It's a continuously maintained archive: new roles appear within a day, and closed ones stay with the date they left the board. Ashby posts no expiry date, so `gone_reason` is `unknown` — the signal is *that* a role closed and *when*, not filled-vs-expired.

### Integration

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/ashby-jobs-history').call({"company": "openai"});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

#### Python

```python
from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/ashby-jobs-history').call(run_input={'company': 'openai'})
items = client.dataset(run['defaultDatasetId']).list_items().items
```

#### CLI

```bash
apify call hyperbach/ashby-jobs-history --input '{"company": "openai"}'
```

#### REST

```bash
curl -X POST "https://api.apify.com/v2/acts/hyperbach~ashby-jobs-history/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H 'Content-Type: application/json' -d '{"company": "openai"}'
```

### Support

support@hyperbach.com

*This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.*

# Actor input Schema

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

Matches job title or category.

## `company` (type: `string`):

Employer name as the board lists it. Partial matches work.

## `postedSince` (type: `string`):

ISO timestamp. Leave empty and the Actor remembers where it stopped, returning only new postings each run.

## `goneSince` (type: `string`):

Only postings observed dead at or after this ISO timestamp. The query this archive exists to answer.

## `status` (type: `string`):

'Gone' returns postings that have been removed, with why — the part no live scraper can give you.

## `location` (type: `string`):

Substring match on the posting's location.

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

Only postings the employer marks as remote.

## `descriptionSearch` (type: `string`):

Substring match inside the job description.

## `excludeTitle` (type: `string`):

Drop postings whose title contains this.

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

Upper bound on results returned in a single run.

## `fullRefresh` (type: `boolean`):

By default each run returns only what appeared since your last one, so a schedule never re-delivers the same page. Tick this to ignore that bookmark and re-pull the full result set for your filters — and it clears the stored cursor, so your next ordinary run starts clean.

## `enrichedOnly` (type: `boolean`):

Return only postings whose salary, employment type and description have been parsed. Off by default: an unenriched record still carries title, company, location and dates, and withholding it trades a real answer for a slower better one.

## Actor input object example

```json
{
  "status": "live",
  "remoteOnly": false,
  "maxItems": 500,
  "fullRefresh": false,
  "enrichedOnly": false
}
```

# Actor output Schema

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

All scraped records in the default dataset. One record per posting:

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("hyperbach/ashby-jobs-history").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("hyperbach/ashby-jobs-history").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call hyperbach/ashby-jobs-history --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=hyperbach/ashby-jobs-history",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/2myybaTqvhgmSIERO/builds/oMxU6yDiSUhDeVy6e/openapi.json
