# PNet Search Scraper (`jobsapi/pnet-jobs-search-scraper`) Actor

Scrape job listings from PNet.co.za, South Africa's leading job board. Extract job titles, companies, locations, salary ranges, and descriptions for South African recruitment.

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

## Pricing

from $2.99 / 1,000 job details

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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

## PNet Jobs Search Scraper

This Actor retrieves complete public PNet South Africa job postings from the official `www.pnet.co.za` listing and detail pages. It follows the public search redirect, parses server-rendered listing cards, enriches each selected job from its official detail page, and writes only detail-verified records.

### Input modes

- `search`: one query and location, using PNet's official search route.
- `searchMultiple`: several queries with stable job-ID deduplication.
- `single`: one exact official PNet detail URL.
- `multiple`: several exact official PNet detail URLs.
- `startUrls`: mixed official listing/search URLs and detail URLs.
- `jobUrl` and `jobUrls`: compatibility aliases for direct modes.

All URLs must use HTTPS and the PNet host. Search and detail requests are bounded by page, candidate, detail, retry, timeout, and total-request limits.

### Output

Job records include the PNet identifier, title, company, location, salary, contract/work type, publication and closing dates, employment metadata, company links, structured address/coordinates, complete description text and HTML, headings, sections, bullets, links, source evidence, canonical identity checks, request receipts, and data-quality coverage. Published email addresses and telephone numbers are redacted from output text and structured fields.

`RUN_SUMMARY`, `OUTPUT_SUMMARY`, `RUN_DIAGNOSTICS`, `RUN_METADATA`, and `RUN_HEALTH` are stored in the default key-value store. Incomplete, expired, mismatched, blocked, or otherwise unverifiable pages are reported as structured diagnostics and never emitted as job rows.

### Transport policy

The implementation uses ordinary native HTTPS requests and Cheerio in local or Apify cloud runs. It does not use a browser, proxy, fingerprint injection, CAPTCHA/WAF bypass, login, or third-party mirror. Dataset writes are buffered until validation succeeds.

### Local verification

```text
npm ci --ignore-scripts --no-audit --no-fund
npm run check
npm test
npx --yes apify-cli validate-schema .actor/input_schema.json
npx --yes apify-cli run --purge --input-file INPUT.json
npm run validate
```

The exact detail fixtures are sampled URLs and can expire on the source; the negative fixture intentionally verifies fail-closed diagnostics for an official-shaped missing URL.

# Actor input Schema

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

Search, search multiple queries, fetch one or more exact jobs, or use mixed official startUrls.

## `query` (type: `string`):

Keyword or job title sent to PNet's official search route.

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

Compatibility alias for query.

## `keyword` (type: `string`):

Compatibility alias for query.

## `searchQueries` (type: `array`):

Queries used in searchMultiple mode.

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

Compatibility alias for searchQueries.

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

City or region sent to PNet's official search route.

## `startUrls` (type: `array`):

Official HTTPS PNet listing/search URLs or detail URLs in startUrls mode.

## `jobUrl` (type: `string`):

One official HTTPS PNet detail URL.

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

Exact official HTTPS PNet detail URLs.

## `jobs` (type: `array`):

Official PNet detail URLs with optional caller labels.

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

Maximum complete verified records to write.

## `maxJobs` (type: `integer`):

Compatibility alias for maxItems.

## `maxCandidates` (type: `integer`):

Maximum unique listing candidates considered for detail enrichment.

## `maxPages` (type: `integer`):

Bounded official listing-page pagination per search.

## `maxDetailRequests` (type: `integer`):

Hard cap on official detail pages fetched.

## `maxRequests` (type: `integer`):

Hard cap on all official listing and detail requests.

## `requestDelayMs` (type: `integer`):

Polite delay between official PNet requests.

## `maxResponseBytes` (type: `integer`):

Hard response-body limit for each official request.

## `timeoutMs` (type: `integer`):

Timeout for each official request.

## `timeoutSecs` (type: `integer`):

Compatibility alias for timeoutMs.

## `retries` (type: `integer`):

Bounded retries for transient official request failures.

## `maxRequestRetries` (type: `integer`):

Compatibility alias for retries.

## Actor input object example

```json
{
  "mode": "search",
  "query": "developer",
  "location": "Johannesburg",
  "maxItems": 3,
  "maxCandidates": 12,
  "maxPages": 2,
  "maxDetailRequests": 6,
  "maxRequests": 24,
  "requestDelayMs": 250,
  "maxResponseBytes": 2000000,
  "timeoutMs": 20000,
  "retries": 1
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("jobsapi/pnet-jobs-search-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("jobsapi/pnet-jobs-search-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 '{}' |
apify call jobsapi/pnet-jobs-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jobsapi/pnet-jobs-search-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/qkzDdNha8D6z47PSj/builds/CyHhgkwkkEINRuDI0/openapi.json
