Nimbryxo Studio

Use Ashby Job Board Compensation & Openings Scraper with Python

Scrape public Ashby job boards for any company hiring on jobs.ashbyhq.com. Get every open role with title, department, team, location, remote status, salary/compensation ranges, apply links, and full job descriptions. No login, no API key.

1. Choose the data you need

Review the published README for supported collection modes, examples, and limits. The recipe below uses the current published input definition.

2. Prepare a source-based input

This JSON contains published prefills and required-field defaults. Review every value for your task before running it; it is not a promise of successful collection.

{
  "employmentType": "any",
  "hasSalaryOnly": false,
  "includeCompanyInfo": false,
  "includeDescriptionHtml": false,
  "includeDescriptionPlain": false,
  "includeDetails": false,
  "includeUnlisted": false,
  "isRemoteOnly": false,
  "jobUrls": [],
  "maxItems": 1,
  "maxItemsPerOrg": 0,
  "mode": "byOrg",
  "orgSlugs": [
    "Ramp"
  ],
  "salaryCurrency": "any",
  "sortBy": "default",
  "workplaceType": "any"
}

Download input.example.json and save your reviewed copy as input.json.

FieldTypeRequiredDescription
modestringrequiredWhat to collect. `byOrg` lists jobs from one or more companies; `jobDetail` fetches specific postings by URL or ID; `boardSummary` returns one departments/locations/compensation summary per company. The legacy values are kept so saved tasks continue to run.
orgSlugsarrayoptionalAshby board slugs, company names, or board URLs. Examples: `Ramp`, `Neko Health`, `https://jobs.ashbyhq.com/Ramp`. Names are matched to the closest live board automatically.
jobUrlsarrayoptionalPublic Ashby job posting URLs, e.g. `https://jobs.ashbyhq.com/Ramp/<job-id>`. The `/application` suffix and query strings are accepted.
orgSlugstringoptionalUsed with `jobId` when you do not have a full job URL. Example: `Ramp`.
jobIdstringoptionalThe Ashby posting UUID or a full job URL. Used alone or with `orgSlug`.
searchQuerystringoptionalCase-insensitive text match over title, department, team, location, and description. Descriptions are searched even when `includeDescriptionPlain` is off and in `boardSummary` mode. Examples: `engineer`, `payments`, `München`.
departmentstringoptionalCase-insensitive text match over department and team. Examples: `Engineering`, `Sales`, `Design`.
locationstringoptionalCase-insensitive text match over the primary location, secondary locations, city, region, and country. Examples: `New York`, `Remote`, `London`.
workplaceTypestringoptionalKeep only fully remote, hybrid, or on-site jobs. Unlike `isRemoteOnly`, this separates Remote from Hybrid. Jobs with no workplace type are excluded when a specific value is chosen.
employmentTypestringoptionalKeep only one employment type.
isRemoteOnlybooleanoptionalKeep jobs the board marks as remote. Ashby sets this for both Remote and Hybrid roles; use `workplaceType=Remote` for fully remote jobs.
publishedAfterstringoptionalOnly jobs published on or after this date. Accepts `YYYY-MM-DD`, an ISO datetime, or a relative window such as `24h`, `7d`, or `2w`.
publishedBeforestringoptionalOnly jobs published on or before this date. Accepts `YYYY-MM-DD`, an ISO datetime, or a relative window.
minAnnualSalaryintegeroptionalKeep jobs whose published annual salary range overlaps this minimum. Hourly, monthly, and weekly ranges are annualized. Jobs with no published salary are kept unless `hasSalaryOnly` is on.
maxAnnualSalaryintegeroptionalKeep jobs whose published annual salary range overlaps this maximum. Use with `minAnnualSalary` for a range.
salaryCurrencystringoptionalOnly compare salaries in this currency. `any` compares the numeric range as published (currencies are not converted).
hasSalaryOnlybooleanoptionalDrop jobs where the employer did not publish a salary range.
sortBystringoptionalOrder of the returned job records. `default` keeps the board's own order.
includeUnlistedbooleanoptionalInclude jobs marked `isListed:false` (direct-link-only postings that are not shown on the public board). Applies to every mode: `jobDetail` returns a `job_not_listed` error for an unlisted job unless this is on. Off by default.
includeCompanyInfobooleanoptionalFetch each company's public board page once for its display name, logo, and timezone. Adds one request per company. Off in the daily test prefill.
includeDetailsbooleanoptionalFetch each returned job's public page for all team names, confidentiality, compensation philosophy, direct-apply flag, and remote-eligible countries. Adds one request per job; slower on large runs.
includeDescriptionPlainbooleanoptionalAdd `descriptionPlain` to each job. On by default; turn it off for compact metadata-only rows.
includeDescriptionHtmlbooleanoptionalAdd the original `descriptionHtml` to each job. Increases dataset size.
maxItemsPerOrgintegeroptionalCap the number of job records from each company after filters. `0` means no per-company cap.
maxItemsintegeroptionalHard cap on the number of job records (or company summaries) pushed to the dataset. Error records for invalid inputs are additional diagnostics and do not count.

3. Start an Actor run

Set APIFY_TOKEN in your environment. Python 3.10 or newer is sufficient; this recipe uses no third-party packages.

Execution may incur charges. Review current pricing on Apify. The request asks for a 120-second timeout and a charge limit; these settings are subject to Apify’s pricing and do not guarantee an all-inclusive spending cap.

import json
import os
import urllib.request

# Save the schema-derived example as input.json; review and edit it first.
with open("input.json", encoding="utf-8") as stream:
    actor_input = json.load(stream)

token = os.environ["APIFY_TOKEN"]
request = urllib.request.Request(
    "https://api.apify.com/v2/acts/uLc5okVRk3hoaiEtP/runs?timeout=120&maxTotalChargeUsd=0.50",
    data=json.dumps(actor_input).encode("utf-8"),
    headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
    method="POST",
)
# This starts a potentially billable run. Do not automatically retry this POST.
with urllib.request.urlopen(request, timeout=40) as response:
    run = json.load(response)["data"]
print("Run ID:", run["id"])
print("Status:", run["status"])
# Inspect and download results in Apify Console once the run succeeds.

4. Inspect the actual results

Open the run in Apify Console and wait for a successful completion. Use the published output schema to locate results and the dataset schema to understand declared views. Missing or optional record fields must not be treated as guaranteed values.

Jobs and company board summaries from public Ashby job boards - titles, departments, teams, structured locations, workplace types, compensation ranges, descriptions, apply links, and optional company metadata.

Questions before you run

Which inputs are required?

The published required fields are mode

What does this guide verify?

The fields, prefills, and schema links are rendered from the published build. This is a usage recipe, not a paid live extraction test. Source availability and collection results can vary.

What if the run request times out?

Check Apify Console before retrying. The first request may already have created a run. Do not automatically repeat a run-creation POST.

Generated from source build yzfUD4dKeKLHXPa89 and its README and schemas. Source changes regenerate this guide; no third-party editorial claims are inferred.