# Kuwait Tender Radar — CAPT Tenders & Winning Bids (`dottti/kuwait-capt-tenders`) Actor

Open tenders and winning bids from Kuwait's Central Agency for Public Tenders (capt.gov.kw). Clean JSON with tender number, agency, subject in Arabic and English, request and closing dates, document price and bond in KWD, and winners with award totals.

- **URL**: https://apify.com/dottti/kuwait-capt-tenders.md
- **Developed by:** [Dottti](https://apify.com/dottti) (community)
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $7.00 / 1,000 tender delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## Kuwait Tender Radar — CAPT Tenders & Winning Bids

Get Kuwait government tenders from the [Central Agency for Public Tenders](https://capt.gov.kw) (CAPT) as clean JSON, CSV or Excel.

Two views:

- **Open tenders**: tender number, agency, subject, request date, closing date, document price and bond in Kuwaiti dinars (KWD), bid type and a direct link.
- **Winning bids**: the public awards list with each winner, the amount in KWD, the award meeting date and the agency.

Schedule it every morning and you have a tender alert feed for Kuwait.

### Who uses this

- **Contractors and suppliers bidding in Kuwait** get new tenders each day, with the closing date and the cost of the documents.
- **Bid teams** see who won similar tenders and at what price before they price their own bid.
- **Consultancies and analysts** track which agencies buy what, and which companies win.

Working in Saudi Arabia or Qatar too? See [Etimad Tender Radar](https://apify.com/dottti/etimad-tender-radar) for Saudi government tenders and [Monaqasat Awards](https://apify.com/dottti/monaqasat-awards) for Qatar tender awards.

### Daily tender alert in 3 steps

1. Pick the view `open`, and add keywords for your sector, for example `صيانة` and `maintenance`.
2. Click **Save as a new task**, then add a daily schedule in the **Schedules** tab.
3. Connect the task to email, Slack or Google Sheets from the **Integrations** tab.

New tenders then arrive every morning. Keep `maxItems` at the number you want to pay for each day.

### Arabic-safe search

Agencies spell the same word in different ways. This actor folds those spellings before it matches your keywords:

- `صيانه` matches `صيانة` (ta marbuta)
- `الوطني` matches `الوطنى` (ya and alef maqsura)
- `انشاء` matches `إنشاء` (alef and hamza forms)
- Persian `ی` and `ک` match Arabic `ي` and `ك`
- Arabic-Indic digits `٢٠٢٦` match `2026`

The original text is kept in a `raw` object, so you can still search the portal for the exact wording.

Some agencies publish the subject in both Arabic and English. The actor splits it into `titleAr` and `titleEn`. It splits only where the text itself changes script. It does not translate anything. When the agency published only Arabic, `titleEn` is `null`.

### Output

#### Open tender

Taken from a run on 20 October 2026 (`view: "open"`, `language: "en"`):

```json
{
  "tenderNumber": "23/2025/2026",
  "title": "توفير مستشارين نظم المعلومات",
  "titleAr": "توفير مستشارين نظم المعلومات",
  "titleEn": null,
  "agency": "Public Presidency for the National Guard",
  "agencyCode": "51",
  "bidType": "General",
  "publishDate": "2026-10-18",
  "publishDateText": "Oct. 18, 2026",
  "closingDate": "2027-01-12",
  "closingDateText": "Jan. 12, 2027",
  "currency": "KWD",
  "documentPriceKwd": 150,
  "documentPriceText": "150.000 KWD",
  "bondKwd": null,
  "bondText": "0.000 KD",
  "bondByItems": true,
  "alternativeOffers": "لا يقبل عروض بديلة",
  "divisible": "قابلة للتجزئة",
  "notes": null,
  "hasInitialMeeting": true,
  "hasSiteVisit": false,
  "raw": {
    "title": "توفير مستشارين نظم المعلومات",
    "agency": "Public Presidency for the National Guard"
  },
  "language": "en",
  "url": "https://capt.gov.kw/en/tenders/opening-tenders/?ministry_code=51&tender_no=23%2F2025%2F2026",
  "sourceUrl": "https://capt.gov.kw/en/tenders/opening-tenders/",
  "scrapedAt": "2026-10-20T12:15:34.922Z"
}
```

#### Winning bid, one winner

```json
{
  "itemNumber": "11",
  "tenderNumber": "RFP-2134797",
  "title": "خدمات الصيانة لشبكة الطرق لمنطقة حقول شركة نفط الكويت في شمال و غرب الكويت MAINTENANCE SERVICES FOR KOC FIELDS AREA ROADS NETWORK AT NORTH & WEST KUWAIT",
  "titleAr": "خدمات الصيانة لشبكة الطرق لمنطقة حقول شركة نفط الكويت في شمال و غرب الكويت",
  "titleEn": "MAINTENANCE SERVICES FOR KOC FIELDS AREA ROADS NETWORK AT NORTH & WEST KUWAIT",
  "agency": "Kuwait Oil Company",
  "agencyCode": "60",
  "awardDate": "2026-10-14",
  "awardDateText": "Oct. 14, 2026",
  "currency": "KWD",
  "winners": [
    {
      "item": null,
      "name": "الشركة الكويتية لبناء المعامل والمقاولات ش م ك ع",
      "amountKwd": 10620000,
      "amountPercent": null,
      "amountText": "10,620,000.000 KD"
    }
  ],
  "totalKwd": 10620000,
  "totalPercent": null,
  "totalText": "10,620,000.000 KD",
  "awardedByItems": false,
  "itemsUrl": null,
  "url": "https://capt.gov.kw/en/tenders/winning-bids/"
}
```

#### Winning bid, awarded item by item

```json
{
  "itemNumber": "12",
  "tenderNumber": "RFP-2143095",
  "titleAr": "خدمات الصيانة لشبكة الطرق في مدينة الأحمدي ومناطق حقول شركة نفط الكويت في جنوب وشرق الكويت",
  "titleEn": "MAINTENANCE SERVICES FOR ROADS NETWORK AT AHMADI TOWNSHIP AND KOC FIELDS AREA AT SOUTH &EAST KUWAIT",
  "agency": "Kuwait Oil Company",
  "awardDate": "2026-10-14",
  "currency": "KWD",
  "winners": [
    { "item": "60526", "name": "الشركة الكويتية لبناء المعامل والمقاولات ش م ك ع", "amountKwd": 7575000, "amountText": "7575000.000 KD" },
    { "item": "0", "name": "شركة المجموعة المشتركة للمقاولات مساهمة عامة", "amountKwd": 8645880, "amountText": "8645880.000 KD" },
    { "item": "60525", "name": "شركة المجموعة المشتركة للمقاولات مساهمة عامة", "amountKwd": 10175875, "amountText": "10175875.000 KD" }
  ],
  "totalKwd": null,
  "awardedByItems": true,
  "itemsUrl": "https://capt.gov.kw/en/tenders/winning-bids-popup/RFP-2143095--60"
}
```

(Shortened. Real records carry every field shown in the first award example.)

#### Notes on the fields

- **Dates** are Kuwait calendar dates in `YYYY-MM-DD`. The portal shows no time, so none is added. The original text is in `publishDateText`, `closingDateText` and `awardDateText`.
- **Money** is in Kuwaiti dinars. `currency` is always `KWD` and the original text sits next to every amount.
- **Percent totals.** Some awards show a percentage, for example `-35%`, in place of an amount. That value goes to `totalPercent` and `totalKwd` stays `null`. The portal does not say what the percentage is applied to.
- **Bond set per item.** When the bond is set per item, the portal prints `0.000 KD` and puts the amounts behind a login. `bondByItems` is then `true` and `bondKwd` is `null`.
- **Items awarded separately.** The list then shows an Items button in place of the winner. The actor opens it and returns one entry per item in `winners`. The portal prints no total for these, so `totalKwd` is `null`. Some itemised rows print one winner and `- KD`. Those are returned as printed.
- **Links.** For open tenders, `url` opens the portal's own search for that one tender. Winning bids have no page of their own on the portal, so `url` is the list page where the award appeared.
- **Missing values** are `null`. Nothing is guessed.

### Input

| Field | What it does |
| --- | --- |
| `view` | `open` (default) or `awards`. |
| `keywords` | Words matched against the subject (both languages), agency and tender number. For awards, winner names too. Empty returns everything. |
| `matchMode` | `any` (default) or `all`. |
| `agencyContains` | Keep only one agency, for example `Ministry of Health` or `وزارة الصحة`. Use the language you pick in `language`. |
| `dateFrom` / `dateTo` | `YYYY-MM-DD`. Open tenders use the request date. Awards use the meeting date. |
| `lookbackDays` | How far back to go past the portal's 5-page limit when you set no dates. Default 90. |
| `includeItemWinners` | Read the per-item winners of itemised awards. On by default. |
| `maxItems` | Hard cap on records, and so on cost. Default 50. |
| `language` | `en` or `ar`. Sets the language of agency names, bid type and date text. |
| `proxyConfiguration` | A residential connection in Kuwait by default, because the portal blocks data-centre servers. The proxy cost is included in the price. |
| `requestDelayMs` / `maxRetries` | Pace and retries. The defaults suit most runs. |

#### Example: maintenance tenders from the Ministry of Electricity and Water

```json
{
  "view": "open",
  "keywords": ["صيانة", "maintenance"],
  "agencyContains": "Electricity",
  "maxItems": 100
}
```

#### Example: who won maintenance work in August and September 2026

```json
{
  "view": "awards",
  "language": "ar",
  "keywords": ["صيانه"],
  "dateFrom": "2026-08-01",
  "dateTo": "2026-09-30",
  "maxItems": 200
}
```

### How it reaches more than 50 rows

The portal lists 5 pages of 10 rows for any search. Page 6 shows page 1 again.

The actor uses the portal's own date search to go further. It checks every result against the date range it asked for. If the portal ever returns rows outside the range, the actor stops slicing by date and says so in the log. It always filters your date range locally as well.

When a date window still fills all 5 pages, the actor splits the window in half and tries again, down to a single day.

### Pricing

You pay per record delivered to your dataset:

- Tender: $0.01 each ($10 per 1,000)
- Winning bid: $0.03 each ($30 per 1,000)

Only records that pass your filters and are stored are charged. `maxItems` caps the cost of every run. A run that is blocked before it delivers anything is charged nothing.

### Notes and limits

- **Cloudflare.** capt.gov.kw sits behind Cloudflare. The actor runs a real Chrome browser, which normally passes the check in a few seconds with no input from anyone. The actor never solves a captcha or clicks a human check. If Cloudflare does not let the browser through, the run stops, logs a clear error, charges nothing and ends as failed. The default residential connection in Kuwait passed in our tests on Apify. Data-centre connections were blocked.
- **Memory.** A browser needs memory. The actor asks for at least 1,024 MB.
- **Speed.** A run of 15 records took 8 to 13 seconds on a laptop in our tests, plus browser start-up on the platform.
- **Documents.** Tender documents and the per-item bond list need a CAPT company login. The actor does not log in and does not download them.
- **Category.** The portal can filter by contractor category but does not print the category on each tender, so records carry no category field.
- **Data source.** Public pages of capt.gov.kw only. No login, no credentials, no personal data beyond the company names CAPT publishes.

### Development

```bash
npm install
npm test                 # parser tests on saved CAPT pages
apify run --purge        # local run, reads storage/key_value_stores/default/INPUT.json
```

# Actor input Schema

## `view` (type: `string`):

open: tenders now open for bids (charged per tender). awards: the public winning-bids list with winner and total (charged per winning bid).

## `keywords` (type: `array`):

Words matched against the subject (Arabic and English parts), agency and tender number. For winning bids, winner names are searched too. Arabic is matched after normalisation, so alef/hamza, ة/ه, ى/ي and Persian ی/ک spellings all match. Leave empty to return everything.

## `matchMode` (type: `string`):

any: keep a record when at least one keyword matches. all: keep it only when every keyword matches.

## `agencyContains` (type: `string`):

Keep only records whose agency name contains this text, for example وزارة الصحة or Ministry of Health. Agency names follow the language you pick below.

## `dateFrom` (type: `string`):

YYYY-MM-DD. Open tenders: request (publish) date. Winning bids: meeting (award) date. The actor sends this to the portal's own date search, checks the results really fall in the range, and always filters locally as well.

## `dateTo` (type: `string`):

YYYY-MM-DD, inclusive. Leave empty for today.

## `lookbackDays` (type: `integer`):

The portal lists only 5 pages (50 rows) per search. When you have no date range and want more rows, the actor continues in 14-day date windows going back this many days. Set 0 to stop at the 5 pages.

## `includeItemWinners` (type: `boolean`):

Some tenders are awarded item by item. The list then shows an Items button in place of the winner. When on, the actor opens that pop-up and returns each item's winner. Adds one page load per such award.

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

Hard cap on delivered records. You pay per record delivered, so this is also your cost cap.

## `language` (type: `string`):

Language of agency names, bid type and dates as the portal prints them. The tender subject is usually Arabic in both; English text is returned in titleEn when the agency published it.

## `proxyConfiguration` (type: `object`):

Kuwait's tender portal blocks data-centre servers. The default uses a residential connection in Kuwait, which the portal accepts. The proxy cost is included in the price.

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

Pause before each page fetch. Keeps the load on the government portal low.

## `maxRetries` (type: `integer`):

Retries per page on network errors, and fresh browser attempts when Cloudflare does not clear.

## Actor input object example

```json
{
  "view": "open",
  "matchMode": "any",
  "lookbackDays": 90,
  "includeItemWinners": true,
  "maxItems": 20,
  "language": "en",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "KW"
  },
  "requestDelayMs": 1000,
  "maxRetries": 3
}
```

# Actor output Schema

## `records` (type: `string`):

Open tenders (number, agency, subject, publish and closing dates, document price and bond in KWD, bid type, link) or winning bids (winners, amounts in KWD, award meeting date).

# 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 = {
    "view": "open",
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "KW"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dottti/kuwait-capt-tenders").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 = {
    "view": "open",
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "KW",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("dottti/kuwait-capt-tenders").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 '{
  "view": "open",
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "KW"
  }
}' |
apify call dottti/kuwait-capt-tenders --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dottti/kuwait-capt-tenders"
        }
    }
}
```

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/q81ImIpiJ5tXg1iX2/builds/JzIyOsu9ZU48PtBJR/openapi.json
