# South Africa eTenders Scraper - Government Tenders & RFQs (`superior_typhoon_xgu/sa-etenders-scraper`) Actor

Scrape open, closed and awarded South African government tenders and RFQs from etenders.gov.za: tender number, department, province, closing dates, briefing info and documents. By Musa AI Tools.

- **URL**: https://apify.com/superior_typhoon_xgu/sa-etenders-scraper.md
- **Developed by:** [Musa AI Tools](https://apify.com/superior_typhoon_xgu) (community)
- **Categories:** Lead generation, Business, Other
- **Stats:** 2 total users, 1 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/actors/running/actors-in-store.md#pay-per-usage

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

## South Africa eTenders Scraper – Government Tenders, RFQs & Bids API

**Get every open South African government tender in minutes.** This Actor extracts tenders, RFQs and bids from the official National Treasury **eTender Portal (etenders.gov.za)**, covering national and provincial departments, municipalities and state-owned entities (SOEs). You get clean, structured data as **JSON, CSV, Excel or via API**. Filter by keyword, province and publish date, schedule it daily, and get **tender alerts** straight into Google Sheets, Slack, email or your CRM.

### 🔎 What does SA eTenders Scraper do?

- Scrapes **open, closed, cancelled or awarded** tenders from etenders.gov.za
- Filters by **keyword** (e.g. "water", "security", "cleaning", "construction", "IT")
- Filters by **province**: Gauteng, Western Cape, KwaZulu-Natal, Eastern Cape, Limpopo, Mpumalanga, North West, Free State, Northern Cape or National
- **"New today" mode**: set `publishedSinceDays = 1` and schedule it daily for fresh tender alerts
- Returns **direct download links to tender documents** (PDF bid packs, ToRs, specs)
- Built to be reliable: automatic retries with exponential backoff, generous timeouts and graceful handling of portal outages

### 👥 Who is it for?

- **SMMEs and B-BBEE suppliers** looking for bid opportunities
- **Tender consultants and bid writers** monitoring many sectors at once
- **Construction, engineering, security, cleaning, IT and professional-services** firms
- **Procurement analysts, journalists and researchers** studying public spending in South Africa
- **Tender-alert and lead-gen platforms** that need a dependable eTenders data feed

### 📦 Output data

| Field | Example |
|---|---|
| tenderNumber | RFQ BELF/90/2026 |
| description | Appointment of a service provider to undertake water quality management… |
| category | Other professional, scientific and technical activities |
| type | Request for Quotation / Open Tender |
| department | South African Forestry Company Limited |
| province | Mpumalanga |
| status | Published |
| datePublished / closingDate | 2026-10-19 / 2026-11-03 12:00 |
| briefingSession, briefingCompulsory, briefingDate, briefingVenue | Compulsory briefing details |
| eSubmission | Whether electronic submission is accepted |
| documents | `[{ name, url }]`: direct links to the tender documents |
| company *(awarded only)* | Awarded supplier company name(s), e.g. `RED CHERRY MEDIA HOLDINGS` (multiple joined with `; `) |
| awardDate *(awarded only)* | 2026-10-19 |
| tenderAmount *(awarded only)* | Award amount as a number in ZAR, e.g. `240350` (`tenderAmountText` keeps the original `R240 350,00`) |
| awards *(awarded only)* | `[{ company, enterpriseType, awardDate, tenderAmount }]` per awardee. Company names only: no contact persons or phone numbers |
| sourceUrl | Link to the tender on etenders.gov.za |
| scrapedAt | Timestamp |

Sample item:

```json
{
  "tenderNumber": "RFQ BELF/90/2026",
  "description": "APPOINTMENT OF A SERVICE PROVIDER TO UNDERTAKE WATER QUALITY MANAGEMENT ...",
  "department": "South African Forestry Company Limited",
  "province": "Mpumalanga",
  "type": "Request for Quotation",
  "status": "Published",
  "datePublished": "2026-10-19T00:00:00",
  "closingDate": "2026-11-03T12:00:00",
  "documents": [{ "name": "RFQ BELF902026 ....pdf", "url": "https://www.etenders.gov.za/home/Download/?blobName=..." }],
  "sourceUrl": "https://www.etenders.gov.za/Home/opportunities?id=174586"
}
```

### ⚙️ Input

| Field | Description |
|---|---|
| status | Open/Published (default), Closed, Cancelled or Awarded |
| keyword | Case-insensitive text matched against description, category, department and tender number |
| province | e.g. `Gauteng`, `Western Cape`, `National` |
| publishedSinceDays | `0` = no limit, `1` = only tenders published in the last 24 h |
| maxItems | Maximum number of tenders to return (default 100) |
| includeContacts | Include published procurement contact name/email/phone (off by default, POPIA-friendly) |

### 🚀 How to use it

1. Click **Try for free**, enter a keyword and/or province, and press **Start**.
2. Download results as JSON, CSV, Excel or HTML, or fetch them via the Apify API.
3. For **daily tender alerts**, set `publishedSinceDays = 1` and add a **Schedule** (e.g. every morning at 07:00 SAST). Then connect **Google Sheets, Slack, email, Zapier, Make or webhooks** in the Integrations tab.

### 💰 Cost

This Actor is lightweight: it calls the portal's data endpoint directly, with no browser. Typical runs finish in seconds, and a daily alert run costs only cents.

### ❓ FAQ

**Is scraping etenders.gov.za legal?** The eTender Portal is a public government site that publishes procurement opportunities for open competition. Personal contact data is excluded unless you turn it on. Make sure your use complies with POPIA.

**How fresh is the data?** It is live: every run queries the portal directly.

**What if the portal is down?** The Actor retries each request with exponential backoff. If the portal stays unreachable, it returns whatever it has already collected, or fails with a clear message so you can re-run it later.

**Can I get awarded tenders?** Yes. Set `status` to *Awarded* (API status code `2`). Each record then includes `company`, `awardDate` and `tenderAmount`. Other codes: `1` Open, `3` Closed, `4` Cancelled.

**Need a custom feature?** Open an issue on the Actor's Issues tab.

***

*Keywords: South Africa tenders, eTenders scraper, etenders.gov.za API, government tenders South Africa, RFQ South Africa, municipal tenders, provincial tenders, SOE tenders, tender alerts, bid opportunities, B-BBEE tenders, procurement data API, National Treasury tenders.*

# Actor input Schema

## `status` (type: `string`):

Which tenders to fetch.

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

Only keep tenders whose description/category/department contains this text (case-insensitive).

## `province` (type: `string`):

e.g. Gauteng, Western Cape, National.

## `publishedSinceDays` (type: `integer`):

0 = no limit. Use 1 for daily alerts.

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

Maximum number of tenders to return.

## `includeContacts` (type: `boolean`):

Off by default (POPIA). When on, includes published procurement contact name/email/phone.

## Actor input object example

```json
{
  "status": "1",
  "publishedSinceDays": 0,
  "maxItems": 100,
  "includeContacts": false
}
```

# Actor output Schema

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

All scraped tenders (one item per tender).

# 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("superior_typhoon_xgu/sa-etenders-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("superior_typhoon_xgu/sa-etenders-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 superior_typhoon_xgu/sa-etenders-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,superior_typhoon_xgu/sa-etenders-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/1Z0CEzV9u6WPZMELl/builds/jpp69PlYzXj70lNZD/openapi.json
