# Job Posting Aggregator (`ghost_banana/job-posting-aggregator`) Actor

Scrape and normalize job postings from Indeed, LinkedIn, Greenhouse, and Lever.

- **URL**: https://apify.com/ghost\_banana/job-posting-aggregator.md
- **Developed by:** [Miguel Fernandez](https://apify.com/ghost_banana) (community)
- **Categories:** Jobs, Lead generation
- **Stats:** 9 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-usage

## 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

1|# Job Posting Aggregator
2|
3|Aggregate job postings from multiple job boards into one normalized dataset. Search by title, location, and recency — output is a clean, consistent JSON schema regardless of which source the job came from.
4|
5|## Changelog
6|
7|### v1.2 (2026-04-06)
8|- **FIX**: Country-specific proxy and domain support
9|  - Proxy country now auto-detected from location (was hardcoded to US)
10|  - Indeed uses country-specific domains (indeed.com.sg, indeed.co.uk, indeed.ca, indeed.com.au)
11|  - Fixes issue where searching "Singapore" would return US results
12|  - Supports: US, Singapore (SG), UK (GB), Canada (CA), Australia (AU)
13|
14|### v1.1 (2026-04-03)
15|- Updated selectors for 2025 DOM
16|- Improved bot-block detection and retry logic
17|- Added residential proxy support documentation
18|
19|---
20|
21|## Supported Sources
22|
23|| Source | Coverage | Notes |
24||--------|----------|-------|
25|| **Indeed** | Largest job board — all industries, all experience levels | High success rate; occasional CAPTCHA with heavy use |
26|| **LinkedIn Jobs** | Professional network public listings | No login required; proxy recommended for high volume |
27|| **Greenhouse** | ATS used by 5,000+ tech companies | Scrapes the global Greenhouse job search |
28|| **Lever** | ATS used by fast-growing startups | Scraped via Google (site:jobs.lever.co) |
29|
30|---
31|
32|## What You Get
33|
34|| Field | Description |
35||-------|-------------|
36|| `title` | Job title |
37|| `company` | Company name |
38|| `location` | City/state or "Remote" |
39|| `salary` | Salary range when listed (Indeed only) |
40|| `seniority` | Inferred level: `intern`, `junior`, `mid`, `senior`, `manager`, `executive` |
41|| `url` | Direct link to the job posting |
42|| `postedDate` | When posted (format varies by source) |
43|| `snippet` | Short description excerpt (when available) |
44|| `department` | Department (Greenhouse only) |
45|| `source` | Which job board (`indeed`, `linkedin`, `greenhouse`, `lever`) |
46|| `searchTerm` | The search term that found this job |
47|| `searchLocation` | The location filter used |
48|| `scrapedAt` | ISO 8601 timestamp |
49|
50|---
51|
52|## Input
53|
54|| Field | Type | Required | Description | Default |
55||-------|------|----------|-------------|---------|
56|| `searchTerms` | Array of strings | **Yes** | Job titles or keywords to search | — |
57|| `locations` | Array of strings | No | Locations to search (`"Remote"`, `"Austin, TX"`, etc.) | `["Remote"]` |
58|| `sources` | Array of strings | No | Which boards: `indeed`, `linkedin`, `greenhouse`, `lever` | `["indeed"]` |
59|| `maxResultsPerSource` | Integer | No | Max results per source per search (1–200) | `50` |
60|| `postedWithinDays` | Integer | No | Only show jobs posted within N days (1–30) | `7` |
61|| `useResidentialProxy` | Boolean | No | Route requests through residential proxies (country auto-detected from location) — improves success rate, especially for LinkedIn | `false` |
62|
63|### Example Input
64|
65|`json
    66|{
    67|    "searchTerms": ["software engineer", "product manager", "data scientist"],
    68|    "locations": ["San Francisco, CA", "Austin, TX", "Remote"],
    69|    "sources": ["indeed", "linkedin"],
    70|    "maxResultsPerSource": 50,
    71|    "postedWithinDays": 7,
    72|    "useResidentialProxy": true
    73|}
    74|`
75|
76|---
77|
78|## Example Output
79|
80|`json
    81|{
    82|    "title": "Senior Software Engineer",
    83|    "company": "Acme Corp",
    84|    "location": "Austin, TX",
    85|    "salary": "$140,000 - $180,000 a year",
    86|    "seniority": "senior",
    87|    "url": "https://www.indeed.com/viewjob?jk=abc123",
    88|    "postedDate": "2 days ago",
    89|    "snippet": "We're looking for a Senior Software Engineer to join our platform team...",
    90|    "source": "indeed",
    91|    "searchTerm": "software engineer",
    92|    "searchLocation": "Austin, TX",
    93|    "scrapedAt": "2026-04-03T14:30:00.000Z"
    94|}
    95|`
96|
97|---
98|
99|## Use Cases
100|
101|- **Recruiting** — Aggregate listings from all major boards into one spreadsheet or database
102|- **Market research** — Analyze hiring trends by role, location, company size, or seniority
103|- **Sales intelligence** — Track which companies are actively hiring (strong buying signal for SaaS and B2B)
104|- **Competitor analysis** — Monitor competitor hiring patterns to understand their growth strategy
105|- **Salary benchmarking** — Collect salary data across roles and locations (availability varies by listing)
106|- **Job alert automation** — Run on a schedule, filter by new results, and pipe to Slack or email
107|
108|---
109|
110|## How It Works
111|
112|1. Builds search URLs for each combination of `searchTerm × location × source`
113|2. Loads each page through a headless Chromium browser
114|3. Detects bot-block pages (CAPTCHA, login walls) and retries with backoff before giving up
115|4. Extracts job cards using source-specific selectors with multiple fallbacks
116|5. Normalizes all fields into a consistent output schema
117|6. Infers seniority from the job title using keyword matching
118|7. Pushes each result to the dataset as it's found (streaming output)
119|
120|---
121|
122|## Reliability & Proxy Notes
123|
124|Job boards actively detect and block scrapers. Here's what to expect:
125|
126|| Source | Without Proxy | With Residential Proxy |
127||--------|--------------|------------------------|
128|| Indeed | Good | Excellent |
129|| LinkedIn | Fair (login walls common) | Good |
130|| Greenhouse | Excellent (no anti-bot) | N/A |
131|| Lever (via Google) | Fair | Good |
132|
133|**Recommendation**: Enable `useResidentialProxy: true` when scraping LinkedIn or running large volumes. The actor uses Apify's built-in residential proxy pool with country auto-detection from your location parameter (supports US, SG, GB, CA, AU).
134|
135|---
136|
137|## Seniority Inference
138|
139|The `seniority` field is inferred from the job title using keyword matching — it is not sourced from the job board directly.
140|
141|| Level | Keywords matched |
142||-------|-----------------|
143|| `intern` | intern, internship |
144|| `junior` | junior, jr., entry level, entry-level, associate |
145|| `senior` | senior, sr., staff, principal, lead |
146|| `manager` | manager, mgr |
147|| `executive` | director, vp, vice president, head of, chief |
148|| `mid` | (default — none of the above matched) |
149|
150|---
151|
152|## Pricing
153|
154|Pay-per-result on the Apify platform. Typical cost: **$2–5 per 100 job postings**.
155|
156|Enabling residential proxy adds proxy bandwidth cost (billed separately by Apify).
157|
158|---
159|
160|## Limitations
161|
162|- **LinkedIn**: Public-only listings. LinkedIn has strong anti-scraping measures — residential proxy recommended for reliable results.
163|- **Greenhouse**: The global search at `boards.greenhouse.io/search` covers all companies using the Greenhouse ATS. Individual company boards (e.g. `boards.greenhouse.io/stripe`) are not supported via the `greenhouse` source — use the company's direct URL if needed.
164|- **Lever**: No central Lever job listing exists. Jobs are discovered via Google search (`site:jobs.lever.co`). Google's SERP structure changes periodically.
165|- **Salary data**: Only Indeed reliably includes salary. LinkedIn, Greenhouse, and Lever typically don't.
166|- **Deduplication**: Jobs are not deduplicated across sources — the same role may appear from Indeed and LinkedIn.
167|

# Actor input Schema

## `searchTerms` (type: `array`):

Job titles or skills to search for. Use specific titles for best results (e.g. 'software engineer', 'product manager', 'data analyst', 'UX designer').

## `locations` (type: `array`):

Where to search. Use 'Remote' for remote-only, city+state for local searches (e.g. 'San Francisco, CA', 'Austin, TX', 'New York, NY').

## `sources` (type: `array`):

Which job boards to search. Valid values: 'indeed', 'linkedin', 'greenhouse', 'lever'. Use 'indeed' for broadest coverage. Add 'linkedin' for professional roles. Add 'greenhouse' and 'lever' for startup/tech company roles.

## `maxResultsPerSource` (type: `integer`):

Maximum job postings to extract per source, per search term, per location. Total results = searchTerms Ã— locations Ã— sources Ã— maxResultsPerSource. Clamped to 1â€“200.

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

Filter to jobs posted within this many days. 1 = last 24 hours, 7 = last week, 30 = last month. Clamped to 1â€“30. Note: Greenhouse and Lever do not support date filtering.

## `useResidentialProxy` (type: `boolean`):

Route requests through Apify's residential proxy pool. Country is auto-detected from location (US, SG, GB, CA, AU supported). Strongly recommended when scraping LinkedIn â€” reduces login wall and rate-limit blocks significantly. Adds proxy bandwidth cost to the run.

## Actor input object example

```json
{
  "searchTerms": [
    "software engineer"
  ],
  "locations": [
    "Remote"
  ],
  "sources": [
    "indeed"
  ],
  "maxResultsPerSource": 50,
  "postedWithinDays": 7,
  "useResidentialProxy": false
}
```

# 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 = {
    "searchTerms": [
        "software engineer"
    ],
    "locations": [
        "Remote"
    ],
    "sources": [
        "indeed"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("ghost_banana/job-posting-aggregator").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 = {
    "searchTerms": ["software engineer"],
    "locations": ["Remote"],
    "sources": ["indeed"],
}

# Run the Actor and wait for it to finish
run = client.actor("ghost_banana/job-posting-aggregator").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 '{
  "searchTerms": [
    "software engineer"
  ],
  "locations": [
    "Remote"
  ],
  "sources": [
    "indeed"
  ]
}' |
apify call ghost_banana/job-posting-aggregator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=ghost_banana/job-posting-aggregator",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/JZAyYUPMvYiIjeHzy/builds/TYXDdpd8Us1K6xXwT/openapi.json
