# Ashby Jobs Scraper API (`kman-br/ashby-jobs`) Actor

Job postings from public Ashby job boards (jobs.ashbyhq.com) through Ashby's public Job Postings API. Title, location, department, employment type, pay summary, posting date and clean job descriptions, with recruiter contacts removed.

- **URL**: https://apify.com/kman-br/ashby-jobs.md
- **Developed by:** [Kman](https://apify.com/kman-br) (community)
- **Categories:** Jobs, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

Job postings from public **Ashby job boards** (`jobs.ashbyhq.com`), read from Ashby's public Job Postings API. One item per job, with the job description as clean text and recruiter contact details removed.

> Independent tool. Not affiliated with, endorsed or sponsored by Ashby or any company whose job board it reads. Company names and trademarks belong to their owners.

### What it does

- Takes one or more job boards as a slug (`ramp`) or a URL (`https://jobs.ashbyhq.com/ramp`). A single job URL such as `https://jobs.ashbyhq.com/ramp/<jobId>` also works and reads its board. Any other host is rejected, and that entry gets an error item with no charge.
- For each board, makes one request to `GET https://api.ashbyhq.com/posting-api/job-board/{organization}` (with `?includeCompensation=true` when compensation is on). The response lists the board's published jobs with their descriptions, so there are no detail requests and no paging.
- Applies the filters to that data and charges one item per job that passes.
- Never logs in and never calls application or candidate endpoints.

### Input

```json
{
  "organizations": ["ramp"],
  "searchText": "engineer",
  "maxItems": 20,
  "includeDescription": true,
  "includeCompensation": true,
  "remoteOnly": false,
  "employmentType": "FullTime",
  "departmentContains": "Engineering",
  "postedWithinDays": 30,
  "locationContains": "New York"
}
```

| Field | Type | Default | Notes |
|---|---|---|---|
| `organizations` | string\[] | required | Board slugs or URLs on `jobs.ashbyhq.com`. Max 50 per run. Duplicates count once. |
| `searchText` | string | — | Case- and accent-insensitive. Matched in the job title and description. Not Ashby's own search. |
| `maxItems` | integer | `50` | Jobs charged per run. Hard cap 1000, enforced in code. |
| `includeDescription` | boolean | `true` | Adds `descriptionText` and `descriptionHtml`. Off = both `null`. Search still reads the description. |
| `includeCompensation` | boolean | `true` | Adds `compensation`, the pay summary strings Ashby publishes for the job. |
| `remoteOnly` | boolean | `false` | Keeps only jobs with `isRemote` true. |
| `employmentType` | enum | — | `FullTime`, `PartTime`, `Intern`, `Contract` or `Temporary`. |
| `departmentContains` | string | — | Case- and accent-insensitive, matched in `department`. |
| `postedWithinDays` | integer ≥ 0 | — | Whole days since `publishedAt`. Jobs without a readable date are dropped when set. |
| `locationContains` | string | — | Case- and accent-insensitive, matched in `location` and in every `secondaryLocations` entry. |

### Output (one item per job)

```json
{
  "company": "ramp",
  "title": "Security Engineer, Cloud",
  "jobId": "34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "url": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245",
  "applyUrl": "https://jobs.ashbyhq.com/ramp/34413f8d-26bf-4bbc-8ade-eb309a0e2245/application",
  "department": "Engineering",
  "team": "Backend",
  "employmentType": "FullTime",
  "location": "New York, NY (HQ)",
  "secondaryLocations": ["Remote (Canada)", "Remote (US)", "Miami, FL"],
  "country": "USA",
  "isRemote": true,
  "workplaceType": "Hybrid",
  "compensation": ["$211.4K – $290.6K • Offers Equity", "$211.4K - $290.6K"],
  "postedDate": "2026-04-07",
  "descriptionText": "About Ramp\nRamp is building ...",
  "descriptionHtml": "<h1><strong>About Ramp</strong></h1><p>...",
  "source_url": "https://jobs.ashbyhq.com/ramp",
  "retrieved_at": "2026-10-23T16:12:49.478Z",
  "source_name": "Ashby",
  "status": "ok",
  "error_message": null,
  "warnings": []
}
```

- `status` is `ok` (a job, charged) or `erro` (a board or URL that could not be read, free). `error_message` says why.
- Every field can be `null` (or an empty list) when Ashby does not publish it for that job.
- `postedDate` is the date part of `publishedAt`.
- Warnings: `contato_removido` (a contact was removed from the description), `teto_margem:<organization>`, `maxItems_limitado:<n>`.

The key-value record `RUN_SUMMARY` has the run `status` (`ok`, `sem_resultado`, `parcial`, `fonte_indisponivel`, `erro_url`, `erro_input`), `completo`, the stop reason `motivo` (`maxItems`, `teto_cobranca`, `prazo_interno`, `teto_margem`), per-board HTTP status and counts, charged events and a `margem` block.

### Pricing (pay per event)

| Event | When | Proposed price |
|---|---|---|
| `inicio-run` | Once per run, after the input is valid and at least one board URL is valid | US$0.005 |
| `job` | Each job item with status `ok` | US$0.001 |

Invalid input and runs where no board is valid are not charged. Error items are free. The maximum charge per run (`ACTOR_MAX_TOTAL_CHARGE_USD`) is respected.

### Limits

- Memory 256 MB, timeout 300 s. The Actor stops on its own at 270 s (or 20 s before the platform timeout, if that comes first) and keeps what it already saved.
- One request per board, at least 1 s apart. Up to 3 tries on HTTP 429 and 5xx, with `Retry-After` honored up to 10 s.
- A source failure never fails the run. A board that is down, returns 404, or returns a non-JSON page gets an `erro` item and the run ends `SUCCEEDED`.
- **Cost guard.** Before each request the Actor checks that the estimated cost (compute time since the start, plus a 20 s tail for the request in flight, plus download bytes at US$0.20/GB) stays under 80% of the revenue charged so far, minus US$0.0005. When it would not, the run stops with `motivo: teto_margem` and `status: parcial`. With only the start charge, the budget is about 16 MB of download (enough for a 14 MB board such as OpenAI's) or about four minutes of compute. Each charged job adds to it.
- A board comes back as one response. If that response is bigger than the budget left, the board is not read and the run stops with `teto_margem`.

### Polite crawling

- Public, unauthenticated GET only. One request per board, at least 1 s apart, with capped backoff on 429 and 5xx.
- Ashby documents this endpoint as a public API for job boards. Check each board's terms of use before large runs. You are responsible for how you use the data.

### Data use and privacy

Job postings are company data. The only personal data that can appear is a recruiter's contact inside the free text. The Actor removes:

- e-mail addresses (`[email removido]`),
- Brazilian and US-format phone numbers (`[telefone removido]`),
- lines such as `Recruiter: Name`, `Recrutador(a): Nome` or `Talent Acquisition: Name` (in the text and in the HTML),

and adds the `contato_removido` warning. No candidate data is read or stored.

The filter is pattern based. Check the output before you use it for mailings or a CRM, and make sure your use meets the rules that apply to you (for example LGPD or GDPR).

### Known limits

- Phone detection covers Brazilian and US formats. Other formats may pass through.
- `searchText` is a plain text match over the title and description. Ashby's public endpoint has no search parameter, so the match runs on the data the Actor already read.
- `postedWithinDays` counts whole days from `publishedAt`, rounded down.
- `compensation` is only the summary strings Ashby publishes. Structured pay components are not included.
- Field values (`department`, `team`, `workplaceType`, `employmentType`) are passed as each board publishes them.

***

### Português

Vagas publicadas em **boards do Ashby** (`jobs.ashbyhq.com`), lidas pela API pública de vagas do Ashby. Um item por vaga, com a descrição em texto limpo e sem contato pessoal de recrutador.

**Entrada:** `organizations`, com slugs ou URLs de `jobs.ashbyhq.com`. Outros hosts são recusados com um item `erro`, sem cobrança. **Saída:** um item por vaga. **Erros de fonte não derrubam o run:** ele termina com sucesso e registra um item `erro`.

**Preços:** `inicio-run` US$0,005 por run com board válido; `job` US$0,001 por vaga com `status: ok`. Erros não são cobrados.

**Limites:** até 50 boards por run; `maxItems` padrão 50, teto de 1000 no código; 256 MB; prazo interno de 270 s; uma requisição por board, com 1 s entre elas. A trava de custo para o run antes de gastar mais que 80% da receita cobrada.

**Privacidade (LGPD):** a vaga é dado da empresa. E-mails, telefones (BR e EUA) e linhas do tipo `Recrutador(a): Nome` saem da descrição, com o aviso `contato_removido`. Nenhum dado de candidato é lido ou guardado.

### Related Actors

More Actors from the same publisher:

- [Greenhouse Jobs Scraper API](https://apify.com/kman-br/greenhouse-jobs): Job postings from public Greenhouse job boards, with departments and clean descriptions.
- [Lever Jobs Scraper API](https://apify.com/kman-br/lever-jobs): Job postings from public Lever job boards, with clean descriptions.
- [Workday Jobs Scraper API](https://apify.com/kman-br/workday-jobs): Job postings from public Workday career sites (myworkdayjobs.com), with clean descriptions.
- [Brazil Jobs Scraper: Greenhouse & Lever (Vagas BR)](https://apify.com/kman-br/vagas-ats): Job openings at Brazilian companies from public Greenhouse and Lever job boards, with normalized city, state and work model.

Browse the full catalog: [kman-br.github.io](https://kman-br.github.io/) ([Português](https://kman-br.github.io/pt/)).

# Actor input Schema

## `organizations` (type: `array`):

Ashby job board slugs or URLs, for example ramp or https://jobs.ashbyhq.com/ramp. A job URL such as https://jobs.ashbyhq.com/ramp/<jobId> also works. Only jobs.ashbyhq.com is accepted. Maximum 50 per run.

## `searchText` (type: `string`):

Case- and accent-insensitive text that must appear in the job title or description, for example "engineer". Leave empty for all jobs.

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

Maximum number of job items charged in the run. The hard cap is 1000, enforced in the code.

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

Adds each job's description as clean text and as HTML. Turn off for a lighter run with the job fields only. Search text still reads the description when this is off.

## `includeCompensation` (type: `boolean`):

Adds the compensation summary strings that Ashby publishes for each job, when the board publishes them.

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

Keeps only jobs that Ashby flags as remote (isRemote).

## `employmentType` (type: `string`):

Keeps only jobs with this employment type as Ashby publishes it. Leave empty for all types.

## `departmentContains` (type: `string`):

Case- and accent-insensitive text that must appear in the job department.

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

Keeps only jobs published within this many days, counted from Ashby's publishedAt date (whole days). Jobs without a readable publishedAt are dropped when this filter is set.

## `locationContains` (type: `string`):

Case- and accent-insensitive text that must appear in the job location or in any of its secondary locations.

## Actor input object example

```json
{
  "organizations": [
    "ramp"
  ],
  "maxItems": 20,
  "includeDescription": true,
  "includeCompensation": true,
  "remoteOnly": false
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

RUN_SUMMARY: status, motivo (maxItems, teto_cobranca, prazo_interno, teto_margem), organizations checked and charged events.

# 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 = {
    "organizations": [
        "ramp"
    ],
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("kman-br/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 = {
    "organizations": ["ramp"],
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("kman-br/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 '{
  "organizations": [
    "ramp"
  ],
  "maxItems": 20
}' |
apify call kman-br/ashby-jobs --silent --output-dataset

```

## MCP server setup

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