# hiring.cafe Jobs Scraper (`scrapyx/hiring-cafe-jobs-scraper`) Actor

Scrapes job listings from hiring.cafe, a US job aggregator pulling postings from hundreds of ATS platforms (ADP, Workday, iCIMS, Dayforce...) and AI-extracting ~80 fields each: compensation range, seniority, remote policy, degree requirements, plus enriched company data.

- **URL**: https://apify.com/scrapyx/hiring-cafe-jobs-scraper.md
- **Developed by:** [Ibnu Adzim](https://apify.com/scrapyx) (community)
- **Categories:** Jobs, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.56 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## hiring.cafe Jobs Scraper

Scrapes job listings from **[hiring.cafe](https://hiring.cafe)** — a job
aggregator that pulls postings from hundreds of upstream ATS platforms
(ADP, Workday, iCIMS, Dayforce, SmartRecruiters...) and runs every one
through an internal "AI search" pipeline that extracts ~80 structured
fields per job: compensation range, seniority level, remote/onsite/hybrid
policy, degree requirements, shift/travel requirements, visa sponsorship,
plus ~18 fields of enriched company data (funding stage, employee count,
industry). Never covered by this portfolio before.

Public data only. No login, no cookies, no browser.

### The one thing you need to know before using this

**No WAF was found** — 8 TLS profiles, all clean, cold. The catch is
`robots.txt`: it explicitly disallows the site's own search-UI query
strings (`?searchState=`, `?page=`), but explicitly `Allow`s the plain
`/jobs/{slug}` path form. This actor speaks ONLY that allowed path form —
one request per query, one page of results (hiring.cafe's own ceiling,
normally 20), no query string ever touched. See
[`CRAWLING_METHOD.md`](CRAWLING_METHOD.md) for the full trail, including
why a working deeper-pagination mechanism was found and deliberately not
used (policy, not a technical wall).

### What you get

Three record types share one dataset, told apart by `recordType`.

#### `JOB` — one row per listing

Convenience fields (title, company, compensation, seniority, workplace
type, apply URL...) are pulled to the top level for the dataset table; the
**full upstream object is also nested verbatim** under `job` — nothing is
lost to the projection, including the raw `v5_processed_job_data` and
`enriched_company_data` blocks if you want every one of the ~80+18 fields.

#### `SEARCH_SUMMARY` — one row per query

Upstream's own `totalCount` / `companyCount`, plus its own human-readable
`headingText` (e.g. *"6,118 software engineer jobs at 1,678 companies in
New York"*) as an independent sanity check on the structured numbers.

#### `ERROR` — one row per input that failed

So every entry in **Search queries** maps to at least one output row.

### Input

| Field | What it does |
| --- | --- |
| **Search queries** | free text — job title, US state/city, work type, or a combined phrase (`'data scientist austin tx'`, `'remote nurse'`) — one request per entry |
| **Max concurrent requests / Min seconds between requests** | tuned generously — no bot-mitigation gate observed |

#### Example

```json
{
  "queries": ["software engineer", "Austin, TX", "remote data analyst", "part time"]
}
```

There is deliberately **no separate location filter**: combining two
canonical slugs as extra path segments does not work upstream (verified
live), but folding both into one query string does — put the whole
title+location phrase in one entry.

### Notes on reliability

- **Bogus query → genuinely honest zero results.** Verified live
  (`notarealslugxyz999` → `totalCount: 0`) — not the silent-widen-to-
  national-baseline trap that dominates elsewhere in this portfolio, so no
  defensive gate was needed.
- **Hard ceiling: one page per query, normally 20 rows.** There is no
  robots-compliant way to go deeper — `?page=` is disallowed by policy,
  and a second path segment (`/jobs/akron-oh/2`) is parsed as a second,
  unrelated slug and returns zero results, not page 2. **Add more/narrower
  queries to get more rows, not a page count.**
- Every result was `is_expired: false` and `estimated_publish_date`
  same-day in recon — hiring.cafe appears to already filter to live
  postings server-side.
- Compensation fields are `null` when the original posting did not
  disclose pay (`compensation.isTransparent: false`), not a parsing gap.

### Output envelope

Every record carries `_input`, `_source` and `_scrapedAt`. Upstream field
names pass through **verbatim**, nested under `job` — no renaming.

See [`CRAWLING_METHOD.md`](CRAWLING_METHOD.md) for the full
reverse-engineering trail.

# Actor input Schema

## `queries` (type: `array`):

Free text, one search per entry -- job title ('software engineer'), US state/city ('Texas', 'Austin, TX'), work type ('remote', 'part time'), or a combined phrase ('data scientist austin tx', 'remote nurse'). Each entry becomes ONE request and returns hiring.cafe's own top ~20 freshest matches plus a SEARCH_SUMMARY row with the true upstream total. There is no deeper pagination (see README) -- add more/narrower queries to get more rows, not a page count.

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

Upper bound on queries in flight at once. No bot-mitigation gate was observed for this target in recon, so this can be raised more freely than most actors in this portfolio.

## `minRequestInterval` (type: `integer`):

Paces request starts (not held inside a concurrency slot) rather than raw concurrency. Kept small by default as a courtesy -- no request-velocity sensitivity was observed for this target.

## `proxyConfiguration` (type: `object`):

No WAF or bot-mitigation gate was observed for hiring.cafe in recon. Residential proxy pinned to the US is still the default, as this portfolio's standard baseline for a single-country public site.

## Actor input object example

```json
{
  "queries": [
    "software engineer",
    "remote data analyst"
  ],
  "maxConcurrency": 6,
  "minRequestInterval": 1,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `items` (type: `string`):

One row per scraped record. See the dataset's default view for field definitions.

# 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 = {
    "queries": [
        "software engineer",
        "remote data analyst"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapyx/hiring-cafe-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 = { "queries": [
        "software engineer",
        "remote data analyst",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("scrapyx/hiring-cafe-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 '{
  "queries": [
    "software engineer",
    "remote data analyst"
  ]
}' |
apify call scrapyx/hiring-cafe-jobs-scraper --silent --output-dataset

```

## MCP server setup

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