# CareerJunction Jobs Scraper (`crawlerbros/careerjunction-scraper`) Actor

Scrape live job listings from CareerJunction.co.za, South Africa's largest job board. Search by keyword and category, filter by location/salary/date, or fetch full job details from listing URLs. No login, no cookies, no paid proxy required.

- **URL**: https://apify.com/crawlerbros/careerjunction-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Jobs, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 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/platform/actors/running/actors-in-store#pay-per-event

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

## CareerJunction Jobs Scraper

Scrape live job listings from [CareerJunction.co.za](https://www.careerjunction.co.za), South Africa's largest job board. Search by keyword, category, and province/region, filter by salary and posting date, or fetch full job details from listing URLs. No login, no cookies, no paid proxy required.

> **Note:** the originally requested `fuzu.com` target already has a dedicated actor in this catalog (Fuzu Jobs Scraper). To avoid a duplicate, this actor instead covers **CareerJunction** — South Africa's leading job board, giving broader coverage of the African job-market category.

### What this actor does

- **Two modes:** `search` (keyword + category browse) and `byUrls` (direct listing fetch)
- **26 job categories** — IT, Finance, Sales, Medical & Health, Engineering, and more
- **27 provinces/regions** — server-side filter (Gauteng, Western Cape, KwaZulu-Natal, Work From Home, International, and more)
- **Filters:** keyword, category, region (server-side), sort order, location text (client-side substring), employment type, seniority level, Employment Equity (EE) flag, minimum salary, posted-within-days
- **Rich fields per job:** full description (HTML + plain text), salary band (when disclosed), employer info, posting/expiry dates
- **Empty fields are omitted** — every record only contains data actually present on the listing

### Output per job

- `jobId` — CareerJunction's internal listing ID
- `title`, `companyName`, `companyUrl`, `companyLogoUrl` (permanent URL — see note below), `companyLogoUrlOriginal` (CareerJunction's original time-limited link, when rehosting succeeded)
- `location`, `country`, `countryCode`
- `industry` — employer's industry classification
- `positionText` — employment type + seniority (e.g. "Permanent Senior position")
- `employmentType`, `seniorityLevel`, `isEmploymentEquity` — parsed from `positionText` (only when the label matches a known pattern)
- `salaryText`, `salaryMin`, `salaryMax`, `salaryCurrency`, `salaryPeriod` (only when disclosed)
- `descriptionHtml`, `descriptionText`
- `datePosted`, `validThrough` — ISO 8601 timestamps
- `postedText`, `expiresText`, `jobRef` — original site-formatted text
- `sourceUrl` — canonical listing URL
- `recordType: "job"`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | select | `search` | `search` / `byUrls` |
| `keywords` | string | `developer` | Free-text search query (mode=search). Leave empty to browse a whole category or the latest listings. |
| `category` | select | – | One of 26 CareerJunction job categories |
| `sortBy` | select | `relevance` | `relevance` / `newest` |
| `region` | select | – | One of 27 CareerJunction provinces/regions (server-side filter, e.g. `westernCape`, `gauteng`, `workFromHome`) |
| `location` | string | – | Keep only jobs whose location text contains this (e.g. `Sandton`) — client-side substring match for a specific city/suburb not covered by `region` |
| `employmentTypes` | array (enum) | – | Keep only these employment type(s): `Permanent` / `Contract` / `Temporary` / `Casual / Part Time` / `Internship` / `Learnership` |
| `seniorityLevels` | array (enum) | – | Keep only these seniority level(s): `Junior` / `Intermediate` / `Senior` / `Management` / `Specialist` / `Executive` |
| `employmentEquityOnly` | boolean | `false` | Only keep jobs explicitly tagged as an Employment Equity (EE) position |
| `minSalary` | integer | – | Drop jobs with disclosed salary below this (ZAR). Undisclosed-salary jobs always pass. |
| `postedWithinDays` | integer | – | Keep only jobs posted within this many days |
| `fetchFullDetails` | boolean | `true` | Visit each job's detail page for full description/salary/employer/dates |
| `jobUrls` | array | – | CareerJunction job listing URLs (mode=byUrls) |
| `maxItems` | integer | `30` | Hard cap on emitted records (1–500) |

#### Example: IT jobs in Cape Town, newest first

```json
{
  "mode": "search",
  "keywords": "",
  "category": "informationTechnology",
  "region": "westernCape",
  "sortBy": "newest",
  "maxItems": 50
}
```

#### Example: fetch specific listings

```json
{
  "mode": "byUrls",
  "jobUrls": [
    "https://www.careerjunction.co.za/digital-coordinator-job-2642426.aspx",
    "https://www.careerjunction.co.za/senior-backend-developer-job-2642491.aspx"
  ]
}
```

### Use cases

- **Recruitment intelligence** — track live South African vacancies by category/location
- **Salary benchmarking** — aggregate disclosed salary bands across roles
- **Job-alert bots** — poll `postedWithinDays` for fresh listings
- **Labour-market research** — analyze demand by industry/category over time
- **Talent sourcing** — bulk-collect employer contact / job data for outreach

### FAQ

**What's CareerJunction?** South Africa's largest job board, spanning every province and 26 job categories, from entry-level to executive roles. See [careerjunction.co.za](https://www.careerjunction.co.za).

**Is a login or API key required?** No — search and job-detail pages are fully public.

**Why is `salaryText` sometimes missing?** Many South African employers don't disclose salary; the actor never fabricates a value — the field (and its derived `salaryMin`/`salaryMax`/`salaryPeriod`) is simply omitted.

**How do `employmentTypes`/`seniorityLevels`/`employmentEquityOnly` work?**
CareerJunction encodes employment type + seniority (and, when applicable, an "EE" Employment Equity tag) into a single label on every listing, e.g. `"Permanent Senior EE position"`. The actor parses this into structured `employmentType`/`seniorityLevel`/`isEmploymentEquity` fields and filters on them client-side, since these are not exposed as their own server-side query parameters. Listings whose label doesn't match a known pattern always pass through unfiltered rather than being silently dropped.

**What's the difference between `region` and `location`?** `region` is applied server-side — it's the exact same `Location=<id>` parameter CareerJunction's own search form sends for its 27 province/region options, so it's fast and always accurate. `location` is a client-side, case-insensitive substring match against each job's parsed location text (e.g. filtering `"Sandton"`) — use it to narrow to a specific city/suburb not covered by the broader `region` list.

**What does `fetchFullDetails=false` skip?** With it off, the actor only uses fields visible on the search-results page (title, company, location, salary snippet, position text) and skips the extra per-job detail-page fetch — faster, but no full description.

**Does this actor cover employer/company profile pages?** No — CareerJunction's `/companies/...` pages are protected by a bot-challenge that blocks plain HTTP access. Job search and job detail pages are unaffected.

**How fresh is the data?** Real-time — every run hits CareerJunction's live search and detail pages directly.

**Why does `sourceUrl` redirect to `/not-found` when I `curl` it directly?** CareerJunction's job pages sit behind bot-detection that 302-redirects plain, non-browser HTTP clients (no real browser fingerprint/session) to `/not-found`, even with a browser User-Agent set. The URL itself is correct and always resolves to the live job posting when opened in an actual browser — this actor's own fetcher passes the bot check the same way a real browser would. This is the same class of protection as Canadian Tire's Akamai challenge, just applied by a different vendor.

**Why is `companyLogoUrl` different from what CareerJunction shows?** CareerJunction serves employer logos from a time-limited (presigned, ~24h) S3 link that would silently break shortly after your run. This actor downloads the logo once during the run and rehosts it in the dataset's own storage, so `companyLogoUrl` stays valid indefinitely. The original CareerJunction link (still valid for ~24h after the run) is kept in `companyLogoUrlOriginal` for traceability.

# Actor input Schema

## `mode` (type: `string`):

What to fetch.

## `keywords` (type: `string`):

Free-text job search query, e.g. `developer`, `accountant`, `sales manager`. Leave empty to browse the latest / a whole category.

## `category` (type: `string`):

Restrict results to a CareerJunction job category.

## `sortBy` (type: `string`):

Order of results (mode=search).

## `region` (type: `string`):

Restrict results to a CareerJunction province/region. Applied server-side (same as CareerJunction's own "Location" search filter).

## `location` (type: `string`):

Only keep jobs whose location text contains this (e.g. `Sandton`, `Rosebank`). Matched client-side against each job's location, case-insensitive substring -- use this for a specific city/suburb not covered by the broader `region` filter above.

## `minSalary` (type: `integer`):

Drop jobs whose disclosed salary is below this amount. Jobs with an undisclosed salary always pass through (not penalized for missing data).

## `employmentTypes` (type: `array`):

Keep only jobs of these employment type(s), parsed from the listing's own position label. Matched client-side (CareerJunction's own facet is JS-driven, see FAQ). Leave empty for all.

## `seniorityLevels` (type: `array`):

Keep only jobs of these seniority level(s), parsed from the listing's own position label. Matched client-side. Leave empty for all.

## `employmentEquityOnly` (type: `boolean`):

Only keep jobs explicitly tagged by the employer as an Employment Equity (EE) position. Matched client-side; jobs not tagged EE are never assumed to be non-EE.

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

Only keep jobs posted within this many days.

## `fetchFullDetails` (type: `boolean`):

Visit each job's detail page for the full description, salary breakdown, employer info, and posting/expiry dates. Turn off for a faster, lighter-weight run using only search-result-card fields (title, company, location, salary snippet).

## `jobUrls` (type: `array`):

CareerJunction job listing URLs, e.g. `https://www.careerjunction.co.za/digital-coordinator-job-2642426.aspx`.

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

Hard cap on emitted records.

## Actor input object example

```json
{
  "mode": "search",
  "keywords": "developer",
  "category": "",
  "sortBy": "relevance",
  "region": "",
  "employmentTypes": [],
  "seniorityLevels": [],
  "employmentEquityOnly": false,
  "fetchFullDetails": true,
  "jobUrls": [],
  "maxItems": 30
}
```

# Actor output Schema

## `jobs` (type: `string`):

Dataset containing all scraped CareerJunction job listings.

# 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 = {
    "mode": "search",
    "keywords": "developer",
    "category": "",
    "sortBy": "relevance",
    "region": "",
    "fetchFullDetails": true,
    "jobUrls": [],
    "maxItems": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/careerjunction-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 = {
    "mode": "search",
    "keywords": "developer",
    "category": "",
    "sortBy": "relevance",
    "region": "",
    "fetchFullDetails": True,
    "jobUrls": [],
    "maxItems": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/careerjunction-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 '{
  "mode": "search",
  "keywords": "developer",
  "category": "",
  "sortBy": "relevance",
  "region": "",
  "fetchFullDetails": true,
  "jobUrls": [],
  "maxItems": 30
}' |
apify call crawlerbros/careerjunction-scraper --silent --output-dataset

```

## MCP server setup

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