# Hacker News Who is Hiring Scraper: Jobs, Emails & Salary (`oswaldocarabano/hn-who-is-hiring-scraper`) Actor

Scrape Hacker News "Who is hiring?" threads into clean rows: company, domain, roles, tech stack, remote, Latin America eligibility, salary, visa, contact emails and apply links. One row per post or per company. Optional email pattern with MX check.

- **URL**: https://apify.com/oswaldocarabano/hn-who-is-hiring-scraper.md
- **Developed by:** [Oswaldo Carabano](https://apify.com/oswaldocarabano) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 job posts

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

## Hacker News Who is Hiring Scraper: Jobs, Emails, Salary & LATAM

**Turn the monthly Hacker News "Ask HN: Who is hiring?" thread into clean rows** you can
export to CSV, JSON or Excel: an HN jobs API alternative with no API key, no login and no browser.
Every company post becomes one row with the **company, its domain, the roles, the
tech stack, remote or on-site, where remote hires can be, Latin America
eligibility, the salary range and currency, visa sponsorship, contact emails and
apply links**, plus the full text. Optionally, get the **company email pattern**
(for example `first.last@`) with its mail servers checked by DNS.

Or switch **Output** to `companies` and get **one row per hiring company** instead:
every post the company made in the months you choose, merged into one deduplicated
row (months it hired, all roles, stack, salary range, contact emails, apply links).

- **Every month since April 2011**: 184 monthly hiring threads, about 93,700 company posts in total.
- **Parsed, not just copied**: salary as numbers with currency and period, remote regions, LATAM eligibility with its evidence, de-obfuscated emails (`name [at] company [dot] com`).
- **Built for job seekers writing directly to companies**, recruiters mapping who is hiring, and anyone tracking startup hiring and pay.
- **Posts or companies**: one row per post, or one row per company with all its posts aggregated (`outputType`).
- **$0.004 per post, or $0.01 per company.** You never pay for an error row or a duplicate.

***

### What people use it for

| If you are… | You get |
|---|---|
| A **developer looking for a remote job** | posts that are remote, where remote hires can be, time zones, salary and the apply link |
| A **developer in Latin America** | `latamOnly`: posts with a positive signal that LATAM candidates can apply, and the evidence for each one |
| **Writing to the hiring team** directly | the company domain, its published contact emails, and the email pattern of the domain with MX records |
| Researching **startup salaries** | salary min / max / currency / period for every band a post lists, equity, seniority |
| Tracking **who is hiring** month by month | company, domain, Y Combinator batch, funding stage and roles across any months since 2011 |
| Building an **outreach list of hiring companies** | `outputType: companies`: one deduplicated row per company, with the months it hired and how many posts it made |
| Building a **job board or newsletter** | structured rows plus the original text and HTML of every post |

***

### What actually arrives in a row

A post row has **64 fields**. The rates below were **measured on 1 Oct 2026** over
the four latest monthly threads (**July to October 2026, n = 848 company posts**).
We do not promise a field we have not measured.

#### Always there (100%)

`postId` · `postUrl` · `storyTitle` · `month` · `threadType` · `author` ·
`postedAt` · `headerLine` · `rawText` · `rawHtml` · `scrapedAt`

#### Parsed from the post

| Field | Filled | What it is |
|---|---:|---|
| `company` | 91% | from the first line, "Company: …" or "At X we are hiring" |
| `domain` | 80.8% | company domain, from its website, a careers subdomain or a corporate email |
| `companyUrl` | 70% | first link that is not a job board, social network or document |
| `roles` | 76% | role titles from the first line, role lists and bullets |
| `employmentTypes` | 70% | full-time, part-time, contract, internship |
| `applyUrls` | 67% | ATS pages (Greenhouse, Lever, Ashby, Workable…) and careers pages |
| `stack` | 63% | 111 technologies recognised (Python, TypeScript, React, PostgreSQL, Rust, Kubernetes, LLMs…) |
| `locations` | 61% | offices or allowed places, e.g. `San Francisco, CA`, `London, UK` |
| `roleCategories` | 55% | backend, frontend, fullstack, mobile, data, ml-ai, devops-sre, security… |
| `remote` | 53.8% true | remote work offered |
| `seniority` | 45% | intern, junior, senior, staff, principal, lead, founding, director… |
| `salaryMin` / `salaryMax` | 37% | first salary range, as numbers, with `salaryCurrency` and `salaryPeriod` |
| `remoteRegions` | 34% | worldwide, americas, latam, us, canada, europe, uk, emea, apac… |
| `contactEmails` | 28.8% | contact emails, de-obfuscated (corporate and role addresses) |
| `equity` | 25% | equity or stock options mentioned |
| `fundingStage` | 16.5% | seed, series a…, bootstrapped, profitable |
| `visaSponsorship` | 8.7% | stated in 8.7%: true (offers sponsorship) 3.5%, false (requires existing work authorization) 5.2% |
| `timezones` | 6.4% | UTC-3, EST, "US time zones"… |
| `ycBatch` | 3.9% | Y Combinator batch, e.g. `S26` |

Salary currencies measured: USD 252, EUR 19, GBP 4, CAD 4, PLN 3, AUD 1, CHF 1 (of
284 posts with a currency). When a post lists several bands or currencies, all of them
are in `salaries`.

#### Latin America eligibility

`latamEligible` answers "can someone in Latin America apply?" and `latamSignals`
says why:

| Value | Meaning | Share (n = 848) |
|---|---|---:|
| `true` | LATAM or a Latin American country or city in the location, or remote worldwide / Americas (`mention:Brazil`, `remote:worldwide`…) | 7.1% |
| `false` | remote restricted to other regions (`remote-restricted:us`), or an office outside Latin America | 65.9% |
| `null` | the post does not say | 27.0% |

#### Company email pattern (optional add-on)

With `inferEmailPattern`, each company domain gets an `emailPattern` object:
`pattern` (e.g. `first.last`), `confidence` (0–0.99), `evidence` (the addresses
it was learned from), `alternatives`, `mxValid`, `mxHosts`, `emailProvider` and
`catchAll`. Measured on 480 domains: **MX valid 95.6%**, **Google Workspace 71.7%**,
**Microsoft 365 12.3%**.

Most posts publish a role address (`jobs@`) rather than a personal one, so a
pattern backed by observed addresses is rare: **4 of 480 domains (0.8%)** in the
sample above, 15 of 1,510 over the last 12 months. **You are charged only for
those.** For every other domain you still get the MX records and provider and a
best guess marked `inferred: true`, **free**.

No email is ever sent and no mail server is probed (no SMTP): `catchAll` is always
`null`.

***

### One row per company (`outputType: companies`)

Set **Output** to `companies` and every company becomes one row that merges **all its
posts** in the months and filters you chose. Companies are grouped by **domain**
(`companyKey: "acme.com"`); a company whose posts never give a domain is grouped by
its normalized name (`companyKey: "name:acme"`), and a name-only post joins the
domain group when that name appears with exactly one domain in the range. Filters
apply to the posts **before** grouping, so `remoteOnly` gives you companies with
remote posts, built from those posts only. Rows come newest hiring first, and
`maxItems` counts companies.

What a company row adds: `monthsHiring`, `monthsCount`, `postCount`,
`firstPostedAt`, `lastPostedAt`, `lastPostUrl`, `lastHeaderLine`, `lastPostText`,
`postIds` / `postUrls`, `authors`, `companyNames`, `remotePosts`,
`latamEligiblePosts` and `locationPolicies`, plus the post fields aggregated: roles,
stack, locations, countries, remote regions, `remote` and `latamEligible` (true when
any post says so), salary range (lowest and highest bound of the most common
currency and period, never mixing currencies), every salary band with its month,
contact emails, apply links, ATS, funding stage, YC batch and the email pattern.

**Measured on 2 Oct 2026** over the last 12 monthly threads (November 2025 to October
2026, **n = 1,822 companies** from 3,475 posts):

| Field | Filled |
|---|---:|
| grouped by `domain` | 84.0% |
| `company` | 95.1% |
| `roles` | 81.5% |
| `stack` | 69.0% |
| `applyUrls` | 64.7% |
| `remote` true | 56.3% |
| `contactEmails` | 38.3% |
| `atsProviders` | 34.6% |
| salary range | 34.2% |
| `fundingStage` | 18.4% |
| `latamEligible` true | 6.1% |
| `ycBatch` | 3.4% |

34.6% of those companies posted more than once in the 12 months, and 18.7% in three
months or more (up to 12 posts; 1.91 posts per company on average). The
"Who wants to be hired?" thread is not read in this mode: job seekers are not
companies.

***

### Privacy

- Corporate and role addresses posted by a company are delivered as published.
- **Personal-domain addresses** (gmail, outlook, yahoo, proton, icloud…) are removed
  from `contactEmails` **and hidden in `rawText` and `rawHtml`** unless you enable
  `includePersonalEmails`. In the measured sample, 19 of 269 addresses in company
  posts were on personal domains.
- **"Who wants to be hired?" posts are individuals**, and so are the "SEEKING WORK"
  posts of the Freelancer thread. Both threads are off by default; when you add one,
  every email, phone number and CV link of those posts (including links to a CV
  document) is hidden unless you also enable `includePersonalEmails`. Turn it on only if you have
  a lawful basis to process that data.
- The Hacker News username of the author is delivered, as shown on the site.

***

### Input examples

The latest month, everything (what the daily test runs):

```json
{}
```

Remote engineering posts of September 2026 with the email pattern:

```json
{
  "months": ["2026-09"],
  "roleKeywords": ["engineer"],
  "remoteOnly": true,
  "inferEmailPattern": true,
  "maxItems": 50
}
```

Every company that hired in the last six months, one row each:

```json
{
  "outputType": "companies",
  "months": ["latest", "latest-1", "latest-2", "latest-3", "latest-4", "latest-5"],
  "maxItems": 500
}
```

Three months of posts open to Latin America, for a scheduled run:

```json
{
  "months": ["latest", "latest-1", "latest-2"],
  "latamOnly": true,
  "stack": ["TypeScript", "Python"]
}
```

`months` accepts `2026-10`, `October 2026`, `latest` and `latest-N`. All filters are
applied to the parsed rows, so they filter exactly what you see.

***

### Output example

One row, shortened (`rawText` and `rawHtml` cut; addresses replaced by examples):

```json
{
  "rowType": "post",
  "postId": "49613617",
  "postUrl": "https://news.ycombinator.com/item?id=49613617",
  "storyTitle": "Ask HN: Who is hiring? (September 2026)",
  "threadType": "hiring",
  "postIntent": "hiring",
  "month": "2026-09",
  "author": "hn_user",
  "postedAt": "2026-09-01T16:03:11Z",
  "headerLine": "Search Atlas | Product Engineer | REMOTE (anywhere, EST hours) | Full-time contractor | $23-$34 USD/hour | https://careers.searchatlas.com/jobs/599248-pro",
  "company": "Search Atlas",
  "companyUrl": "https://careers.searchatlas.com/jobs/599248-product-engineer-pm",
  "domain": "searchatlas.com",
  "roles": ["Product Engineer (Product Manager role, not a Full-stack role)"],
  "employmentTypes": ["full-time", "contract"],
  "stack": ["TypeScript", "Python", "React", "Django", "LLMs"],
  "remote": true,
  "locationPolicy": "remote",
  "remoteRegions": ["worldwide"],
  "latamEligible": true,
  "latamSignals": ["remote:worldwide"],
  "timezones": ["EST", "EST hours"],
  "salaryMin": 23,
  "salaryMax": 34,
  "salaryCurrency": "USD",
  "salaryPeriod": "hour",
  "visaSponsorship": null,
  "contactEmails": ["first.last@example.com"],
  "contactEmailsDetail": [{ "email": "first.last@example.com", "type": "named", "domain": "example.com", "obfuscated": false }],
  "personalEmailsHidden": 0,
  "applyUrls": ["https://careers.searchatlas.com/jobs/599248-product-engineer-pm"],
  "emailPattern": {
    "pattern": "first.last",
    "confidence": 0.5,
    "inferred": false,
    "evidenceCount": 1,
    "mxValid": true,
    "mxHosts": ["aspmx.l.google.com"],
    "emailProvider": "Google Workspace",
    "catchAll": null,
    "charged": true
  },
  "rawText": "Search Atlas | Product Engineer | REMOTE (anywhere, EST hours) | …",
  "scrapedAt": "2026-10-01T23:40:12.511Z"
}
```

One company row, shortened (lists cut; the address replaced by an example):

```json
{
  "rowType": "company",
  "companyKey": "albert.com",
  "company": "ALBERT",
  "domain": "albert.com",
  "monthsHiring": ["2026-09", "2026-08", "2026-07", "2026-06"],
  "monthsCount": 4,
  "postCount": 4,
  "firstPostedAt": "2026-06-03T00:08:25Z",
  "lastPostedAt": "2026-09-08T19:19:18Z",
  "lastPostUrl": "https://news.ycombinator.com/item?id=49615435",
  "lastHeaderLine": "ALBERT | REMOTE ALMOST ANYWHERE IN THE WORLD| Hiring principal and distinguished engineers to build net new products | up to ~ $750k",
  "postIds": ["49615435", "49172196", "48751913", "48377948"],
  "seniority": ["principal"],
  "stack": ["JavaScript", "Python", "React", "React Native", "PostgreSQL"],
  "remote": true,
  "remotePosts": 4,
  "remoteRegions": ["worldwide"],
  "latamEligible": true,
  "latamEligiblePosts": 4,
  "latamSignals": ["remote:worldwide"],
  "salaryMin": 750000,
  "salaryMax": 750000,
  "salaryCurrency": "USD",
  "salaryPeriod": "year",
  "salaries": [{ "min": null, "max": 750000, "currency": "USD", "period": "year", "raw": "$750k", "month": "2026-09", "postId": "49615435" }],
  "equity": true,
  "contactEmails": ["recruiting@example.com"],
  "fundingStage": "profitable",
  "emailPattern": { "pattern": "first.last", "confidence": 0.1, "inferred": true, "mxValid": true, "emailProvider": "Google Workspace", "catchAll": null, "charged": false },
  "lastPostText": "ALBERT | REMOTE ALMOST ANYWHERE IN THE WORLD| Hiring principal and distinguished…"
}
```

***

### Pricing

Pay per event:

| Event | Price |
|---|---|
| Post (one row, `outputType: posts`) | **$0.004** ($4 per 1,000 posts) |
| Hiring company (one row, `outputType: companies`) | **$0.01** ($10 per 1,000 companies) |
| Company email pattern (once per domain per run, only when backed by observed addresses) | **$0.01** |
| Actor start | $0.00001 |

Error rows are never charged, and a post or a company is never charged twice in the same run.
When your maximum cost per run is reached, the run stops cleanly.

#### What real runs cost (measured on the Apify platform, 2 Oct 2026, 512 MB)

| Input | Result | Time | You pay |
|---|---|---:|---:|
| `{}` (latest month, 100 posts) | 100 posts | 5 s | $0.40 |
| September 2026, `engineer`, remote only, email pattern, 50 | 50 posts + 1 email pattern | 6 s | $0.21 |
| Latest 3 months, `latamOnly`, email pattern | 43 posts | 6 s | $0.17 |
| `outputType: companies` (latest month, 100) | 100 companies | 4 s | $1.00 |
| `outputType: companies`, latest 2 months, email pattern, 150 | 150 companies + 3 email patterns | 7 s | $1.53 |
| `outputType: companies`, latest 12 months, 100 | 100 companies (3,483 posts merged) | 55 s | $1.00 |
| Maximum cost per run $0.50, 300 posts asked | 124 posts, then a clean stop | 4 s | $0.50 |
| A month in the future (`2031-01`) | 1 error row explaining why | 1 s | $0.00 |

Set **Max items** to control the cost: you pay for the rows you get, nothing for the
time the run takes.

***

### How it compares (measured on 1 Oct 2026)

We read the output schemas of the 10 Hacker News "Who is hiring" actors in the Apify
Store. The richest declares 25 fields. **None** of them delivers the company domain,
salary as numbers, Latin America eligibility, an email pattern or MX records.

***

### FAQ

**Where does the data come from?** From the public Hacker News search API, the same
one the Hacker News search page uses. No login, no cookies, no browser, no API key.

**Can I export Hacker News jobs to CSV or Excel?** Yes. Every run is an Apify dataset:
download it as CSV, JSON, Excel, XML or HTML, or read it through the Apify API. The
dataset has ready views (overview, outreach, LATAM, compensation, companies).

**Can I run it every month?** Yes: schedule the actor (or a saved task) with
`months: ["latest"]` and send each dataset to Google Sheets, Slack, Zapier or a webhook
with Apify integrations. Each row has `postId` and `postedAt` to keep only the new ones.

**How fast is it?** On the Apify platform (2 Oct 2026, 512 MB): 100 posts in 5
seconds, 300 posts from two months in 6 seconds, and 12 months merged into companies
in 55 seconds, with a memory peak of 102 MB. In local tests: 9,040 posts (12
months of both threads) in 42 seconds. Companies: 1,822 companies from 12 months in
23 seconds; the whole archive (184 threads, 90,427 posts, 24,169 companies) in 3 min
19 s within 512 MB of memory (measured on 2 Oct 2026).

**Posts or companies?** Posts when you want every job ad (one company can post
every month). Companies when you want a deduplicated list of who is hiring, with
how often and for what.

**Which months exist?** Monthly hiring threads exist from April 2011. The
"Freelancer? Seeking freelancer?" thread was last posted in October 2025, so recent
months have none; the run tells you when a requested thread does not exist.

**The current month has few posts.** The thread opens on the first weekday of the
month and grows for days: on 1 Oct 2026 it had about 100 posts, while September 2026
ended with 254.

**Why is a field empty?** Posts are free text and many do not state a salary, a
stack or a location. Every field is always present, with `null` when the post does
not say.

**Does it send emails or verify mailboxes?** No. It only reads published posts and
checks MX records by DNS.

**Can I get the replies?** Rows are top-level posts (the job posts). `replyCount`
says how many direct replies each one has, and `replyIds` lists their Hacker News IDs.

# Actor input Schema

## `outputType` (type: `string`):

posts = one row per Hacker News post (charged per post). companies = one row per hiring company (by domain, or by name when no domain is found) that aggregates all its posts in the chosen months and filters: months it hired, number of posts, roles, stack, locations, remote and Latin America eligibility, salary range, contact emails, apply links, ATS, funding stage, YC batch and its latest post (charged per company). Filters apply to the posts before they are grouped.

## `months` (type: `array`):

Monthly threads to read, as 2026-10, "October 2026", "latest" or "latest-1" (the month before the latest; handy for scheduled runs). Empty = the latest month only. Threads exist from 2011-04; recent months have 100–410 company posts each (measured on 1 Oct 2026).

## `includeWhoWantsToBeHired` (type: `boolean`):

Adds the job-seeker thread of the same months (rows with postIntent = seeking-work). These are individuals: their emails, phone numbers and CV link are hidden unless you also enable "Include personal emails". Not read when Output is "companies" (job seekers are not companies).

## `includeFreelancer` (type: `boolean`):

Adds the freelancer thread of the same months. Its "SEEKING WORK" posts are individuals: their emails, phone numbers and CV link are hidden unless you also enable "Include personal emails". Note: the last freelancer thread was posted in October 2025 (measured on 1 Oct 2026), so recent months have none.

## `roleKeywords` (type: `array`):

Keep posts whose role titles or first line contain any of these words (whole-word, case-insensitive), e.g. "backend", "data engineer", "founding". Empty = all roles.

## `stack` (type: `array`):

Keep posts that mention any of these technologies. Empty = any stack.

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

Keep only posts that offer remote work (remote = true).

## `latamOnly` (type: `boolean`):

Keep only posts with a positive signal that someone in Latin America can apply: LATAM or a Latin American country/city in the location, or remote worldwide / Americas. See latamSignals for the evidence of each row.

## `withEmailOnly` (type: `boolean`):

Keep only posts that publish a contact email (after the privacy rules below).

## `inferEmailPattern` (type: `boolean`):

For each company domain: email pattern (e.g. first.last@), confidence and evidence from the addresses posted on Hacker News, plus MX records and email provider checked by DNS. Charged once per domain and only when the pattern is backed by an observed address; guesses without evidence are delivered free and marked inferred = true. No email is ever sent and no mail server is probed (SMTP).

## `includePersonalEmails` (type: `boolean`):

Off by default. When off, emails on personal domains (gmail, outlook, yahoo, proton, icloud…) are removed from contactEmails and hidden in rawText/rawHtml, and job-seeker posts lose all their contact details. Turn on only if you have a lawful basis to process them.

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

Stop after this many rows of the chosen output: posts, or companies. Each row is charged as one post or one company.

## Actor input object example

```json
{
  "outputType": "posts",
  "months": [
    "2026-09"
  ],
  "includeWhoWantsToBeHired": false,
  "includeFreelancer": false,
  "roleKeywords": [
    "engineer"
  ],
  "stack": [],
  "remoteOnly": true,
  "latamOnly": false,
  "withEmailOnly": false,
  "inferEmailPattern": true,
  "includePersonalEmails": false,
  "maxItems": 50
}
```

# Actor output Schema

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

Posts: one row per top-level post with company, domain, roles, stack, remote and Latin America eligibility, salary, visa, contact emails, apply links, optional email pattern and the full text. Companies: one row per company with all its posts aggregated.

## `runSummary` (type: `string`):

Output type, threads read, rows delivered and charged, companies found and posts aggregated, email pattern counts, network summary and whether the run stopped early.

## `errors` (type: `string`):

Threads that could not be read and input adjustments (always written, empty when all went well). Never charged.

# 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 = {
    "outputType": "posts",
    "months": [
        "2026-09"
    ],
    "roleKeywords": [
        "engineer"
    ],
    "remoteOnly": true,
    "inferEmailPattern": true,
    "maxItems": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("oswaldocarabano/hn-who-is-hiring-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 = {
    "outputType": "posts",
    "months": ["2026-09"],
    "roleKeywords": ["engineer"],
    "remoteOnly": True,
    "inferEmailPattern": True,
    "maxItems": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("oswaldocarabano/hn-who-is-hiring-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 '{
  "outputType": "posts",
  "months": [
    "2026-09"
  ],
  "roleKeywords": [
    "engineer"
  ],
  "remoteOnly": true,
  "inferEmailPattern": true,
  "maxItems": 50
}' |
apify call oswaldocarabano/hn-who-is-hiring-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,oswaldocarabano/hn-who-is-hiring-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/7njJX4qKTR61ujydm/builds/PYoilZcs4SJVHW35r/openapi.json
