# Gulf Government Tenders — Saudi, Qatar, Oman & Jordan (`dottti/gulf-tenders`) Actor

Open government tenders from Etimad (Saudi Arabia), Monaqasat (Qatar), the Oman Tender Board and JONEPS (Jordan) in one JSON schema. Arabic-safe keyword search, deadlines in ISO, fees in local currency, optional award results with bidders and prices.

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

## Pricing

from $5.60 / 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

## Gulf Government Tenders — Saudi, Qatar, Oman & Jordan in One Feed

Get open government tenders from four national portals in one dataset. Every record uses the same fields, so one filter, one spreadsheet and one alert cover all four countries.

| Country | Portal | Currency |
|---|---|---|
| Saudi Arabia | [Etimad](https://tenders.etimad.sa) | SAR |
| Qatar | [Monaqasat](https://monaqasat.mof.gov.qa) | QAR |
| Oman | [Oman Tender Board](https://etendering.tenderboard.gov.om) | OMR |
| Jordan | [JONEPS](https://www.joneps.gov.jo) | JOD |

Optional add-on: award results with every bidder and price for Saudi Arabia and Qatar, and bid-opening reports for Oman.

### Who it is for

- **Bid teams** that sell across the Gulf and want one daily list of new tenders in their field.
- **Consultancies** that track public spending for clients in several countries.
- **Market analysts** who compare buyers, categories and prices between countries.

### What you get

One row per tender. Fields:

- `country`, `countryName`, `source`: where the tender comes from.
- `tenderNumber`, `referenceNumber`: the portal's own numbers, as published.
- `title`: Arabic title with display characters repaired. `titleRaw` keeps the portal's exact wording. `titleEn` is the English title when the portal publishes one (Qatar, Oman, Jordan).
- `entity`: the government buyer.
- `category`, `tenderType`: activity or sector, and the procurement type, in the portal's words.
- `publishedAt`, `submissionDeadline`: ISO 8601. Date-times carry the local offset (`+03:00` or `+04:00`). A plain date (`2026-11-25`) means the portal publishes the day only.
- `daysLeft`: calendar days from today (local time in that country) to the deadline. 0 means it closes today.
- `isOpen`: true while the deadline is ahead.
- `documentFee` and `currency`: the price of the tender documents in local currency. One record never mixes currencies, and amounts are never converted.
- `url`: link to the tender on the portal.
- `extra`: country-specific fields, such as the Hijri deadline for Saudi Arabia, the tender bond for Qatar, governorate and bank guarantee for Oman, and procurement method for Jordan.

#### Example tender (Qatar, from a live run on 20 October 2026)

```json
{
  "recordType": "tender",
  "country": "QA",
  "countryName": "Qatar",
  "source": "Monaqasat",
  "tenderNumber": "5262/2026",
  "referenceNumber": null,
  "title": "تصميم وتوريد وتركيب وتشغيل نظام اللوحات الإرشادية وتوجيه المسارات س ل م / ص / 2026/11080",
  "titleRaw": "تصميم وتوريد وتركيب وتشغيل نظام اللوحات الإرشادية وتوجيه المسارات\r\nس ل م / ص / 2026/11080",
  "titleEn": "THE DESIGN, SUPPLY, INSTALLATION AND COMMISSIONING OF SIGNAGE AND WAYFINDING SYSTEM HMC/MTCS/11080/2026",
  "entity": "مؤسسة حمد الطبية",
  "category": "موردين / مقدمى خدمات",
  "tenderType": "مناقصة عامة",
  "publishedAt": "2026-10-20",
  "submissionDeadline": "2026-11-25",
  "daysLeft": 36,
  "isOpen": true,
  "documentFee": 500,
  "currency": "QAR",
  "url": "https://monaqasat.mof.gov.qa/TendersOnlineServices/TenderDetails/661516",
  "scrapedAt": "2026-10-20T12:02:19.874Z",
  "extra": {
    "tenderId": 661516,
    "entityEn": "Hamad Medical Corporation",
    "tenderTypeEn": "Public Tender",
    "tenderBondQar": 20000,
    "datesAsPublished": { "publishDate": "20/10/2026", "closingDate": "25/11/2026" }
  }
}
```

#### Example award (Saudi Arabia, from a live run on 20 October 2026)

Award records have `recordType: "award"`. They list the winners and every bidder with the amount offered.

```json
{
  "recordType": "award",
  "country": "SA",
  "countryName": "Saudi Arabia",
  "source": "Etimad",
  "awardStatus": "awarded",
  "tenderNumber": "PR26-17524",
  "referenceNumber": "260939007294",
  "title": "اقامة فعالية اليوم الوطني لعام 1448 ه لمستشفيات القوات المسلحة بمنطقة الطاتئف",
  "titleRaw": "اقامة فعالية اليوم الوطني لعام 1448 هـ لمستشفيات القوات المسلحة بمنطقة الطاتئف ",
  "titleEn": null,
  "entity": "مستشفيات الهدا والأمير سلطان والأمير منصور ومركز التأهيل",
  "tenderType": "شراء مباشر",
  "submissionDeadline": "2026-09-20T09:59:00+03:00",
  "awardDate": null,
  "winners": [
    { "name": "مؤسسة أفكار العالم للمقاولات", "amount": 25000, "offerAmount": 25000 }
  ],
  "bidders": [
    { "name": "شركة أبو نايف للعقارات", "amount": 97635, "technicalResult": "مطابق" },
    { "name": "مؤسسة شهد خضر الثبيتي", "amount": 66617.2, "technicalResult": "مطابق" },
    { "name": "مؤسسة نوايف الخليج للتجارة", "amount": 99475, "technicalResult": "مطابق" },
    { "name": "مؤسسة أفكار العالم للمقاولات", "amount": 25000, "technicalResult": "مطابق" }
  ],
  "bidderCount": 4,
  "lowestBid": 25000,
  "lowestBidderName": "مؤسسة أفكار العالم للمقاولات",
  "winnerIsLowestBidder": true,
  "totalAward": 25000,
  "currency": "SAR",
  "url": "https://tenders.etimad.sa/Tender/DetailsForVisitor?STenderId=4PcZXfjWKYGlu%20WNpRVDcw%3D%3D",
  "scrapedAt": "2026-10-20T12:04:01.087Z",
  "extra": {
    "tenderId": 1105607,
    "awardType": "ترسية كاملة",
    "category": "تجارة الكماليات",
    "branchName": "الشراء المباشر - الغير طبي"
  }
}
```

`awardStatus` values:

- `awarded`: winners are published (Saudi Arabia, Qatar).
- `offers_only`: Etimad shows the bidders and prices, and the winner is still pending.
- `bids_opened`: an Oman bid-opening report. It lists every bid and price. Oman publishes the winner elsewhere, so `winners` is empty.

### How to use it

1. Pick the **countries**. All four are on by default.
2. Add **keywords** in Arabic or English, for example `صيانة` and `maintenance`. Arabic spelling variants match each other: أ, إ, آ and ا; ة and ه; ى and ي; the Persian letters ی and ک; diacritics and tatweel.
3. Optional: set **Buyer name contains**, for example `وزارة الصحة`.
4. Optional: set **Published within the last N days**. Use 1 for a daily alert and 7 for a weekly one.
5. Set **Maximum tenders per country** and **Maximum tenders in total**. These are your cost caps.
6. Optional: switch on **Add award results**.

#### Example: weekly IT and maintenance digest

```json
{
  "countries": ["SA", "QA", "OM", "JO"],
  "keywords": ["تقنية المعلومات", "صيانة", "software", "maintenance"],
  "publishedWithinDays": 7,
  "maxItemsPerCountry": 100,
  "maxItems": 400
}
```

### Set up a daily or weekly alert

1. Open this Actor in Apify Console, fill in your filters and click **Save as a new task**.
2. In the task, open **Schedules** and add a schedule. For a daily alert use `0 6 * * *` (06:00 UTC, 09:00 in Riyadh). For a weekly one use `0 6 * * 0`. Set **Published within the last N days** to match: 1 for daily, 7 for weekly.
3. In the task, open **Integrations**:
   - **Email**: add the email integration and choose "run succeeded". Each finished run sends an email with a link to the results.
   - **Slack**: add the Slack integration and pick a channel. Each finished run posts a message with a link to the results.
4. Each run then sends only the tenders published in that window.

### Pricing

You pay per record delivered to your dataset.

- **Tender:** $0.008 per tender ($8 per 1,000).
- **Award:** $0.025 per award record or Oman bid-opening report ($25 per 1,000).
- **Tender checked:** $0.0003 per award lookup on Etimad or Monaqasat ($0.30 per 1,000). Each award needs one lookup, and some lookups find no published result yet. This fee only applies when award results are on.

Example: 200 tenders cost $1.60. Adding 20 awards from each of Saudi Arabia, Qatar and Oman adds $1.50, plus lookups (usually a few cents).

A portal that fails in a run delivers nothing, so you pay nothing for it. Records delivered before a failure stay in your dataset.

### Limits per country

| Country | What you get | Limits |
|---|---|---|
| Saudi Arabia | Open tenders, newest first. Awards for tenders whose deadline was 30 to 120 days ago. | Etimad serves 24 rows per page and about 800 rows per search. Larger searches continue region by region. Etimad publishes no English titles. |
| Qatar | Open tenders with English title and buyer. The latest awarded tenders with every bid. | Dates are days only, with no time. |
| Oman | New tenders. With detail pages on: English title, publish date and exact deadline time. Bid-opening reports for tenders under evaluation. | The portal is slow (about 3 to 4 seconds per page). The publish-date filter needs each tender's notice page, so it adds one request per tender. |
| Jordan | Tenders with a deadline today or later. With detail pages on: English title, document fee, exact deadline time. | JONEPS publishes no award results on public pages. Each tender's details take three requests. |

- Requests to each portal are at least one second apart. Runs are polite and slower than a bulk download.
- Countries run one after another. If a portal is down, the run logs it and continues with the next country. The run summary (`RUN_SUMMARY` in the key-value store) shows the count and status per country.
- Typical run times with detail pages on: 10 tenders per country take about 1.5 minutes. 50 per country take about 5 to 6 minutes, mostly in Oman and Jordan.
- Data comes from the public pages of each portal. No login is used.
- Fields a portal leaves empty come back as `null`.

### Single-country actors

Need more filters for one country? Each portal also has its own actor:

- Saudi tenders: [Etimad Tender Radar](https://apify.com/dottti/etimad-tender-radar)
- Saudi award results: [Etimad Award Results](https://apify.com/dottti/etimad-awards)
- Qatar awards and tenders: [Monaqasat Awards](https://apify.com/dottti/monaqasat-awards)
- Oman tenders and bid-opening reports: [Oman Tender Radar](https://apify.com/dottti/oman-tender-radar)
- Jordan tenders: [JONEPS Tender Radar](https://apify.com/dottti/joneps-tender-radar)

### Development

```bash
npm test          # parser and unifier tests on saved live responses
apify run         # local run, reads storage/key_value_stores/default/INPUT.json
```

# Actor input Schema

## `countries` (type: `array`):

Portals to search. They run one after another. If one portal is down, the run logs it and continues with the others.

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

Words to find in the tender title, buyer, category or number. Arabic and English both work. Arabic spelling variants match each other: أ/إ/آ/ا, ة/ه, ى/ي, Persian ی and ک, diacritics and tatweel. Leave empty for all tenders.

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

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

## `entityContains` (type: `string`):

Keep only tenders whose government entity contains this text. Example: وزارة الصحة or Ministry. Matched with the same Arabic spelling rules as keywords.

## `onlyOpen` (type: `boolean`):

Drop tenders whose submission deadline has passed.

## `publishedWithinDays` (type: `integer`):

Keep only tenders published in the last N days. Leave empty for no limit. For Oman this needs the tender notice, so each candidate costs one extra request.

## `maxItemsPerCountry` (type: `integer`):

Cap on tenders delivered from each portal.

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

Cap on tenders across all countries. You pay per tender delivered, so this is your tender cost cap. Award records have their own cap below.

## `includeDetails` (type: `boolean`):

Gives Jordan its document fee, English title and exact deadline time, and Oman its publish date, English title and exact deadline time. Costs one extra request per tender in Oman and three in Jordan, so runs take longer.

## `includeAwards` (type: `boolean`):

Also return award records. Saudi Arabia: winners, bidders and prices for tenders whose deadline was 30 to 120 days ago. Qatar: the latest awarded tenders with every bidder and price. Oman: bid-opening reports with every bid. Jordan publishes no award results.

## `maxAwardsPerCountry` (type: `integer`):

Cap on award records from each of Saudi Arabia, Qatar and Oman.

## `maxPagesPerCountry` (type: `integer`):

Stops scanning a portal after this many list pages. Keeps runs short when narrow keywords match few tenders. Page sizes: Etimad 24, Monaqasat 20, Oman 50, JONEPS 100.

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

Minimum gap between two requests to the same portal. Never below 1000 ms.

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

Retries with exponential backoff on timeouts, HTTP 429 and 5xx.

## Actor input object example

```json
{
  "countries": [
    "SA",
    "QA",
    "OM",
    "JO"
  ],
  "matchMode": "any",
  "onlyOpen": true,
  "maxItemsPerCountry": 10,
  "maxItems": 200,
  "includeDetails": true,
  "includeAwards": false,
  "maxAwardsPerCountry": 20,
  "maxPagesPerCountry": 20,
  "requestDelayMs": 1000,
  "maxRetries": 4
}
```

# Actor output Schema

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

All records: country, title, buyer entity, deadline, document fee with currency, link, and country-specific fields under extra.

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

Per-country counts, run time and any portal that failed in this run.

# 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 = {
    "countries": [
        "SA",
        "QA",
        "OM",
        "JO"
    ],
    "maxItemsPerCountry": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("dottti/gulf-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 = {
    "countries": [
        "SA",
        "QA",
        "OM",
        "JO",
    ],
    "maxItemsPerCountry": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("dottti/gulf-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 '{
  "countries": [
    "SA",
    "QA",
    "OM",
    "JO"
  ],
  "maxItemsPerCountry": 10
}' |
apify call dottti/gulf-tenders --silent --output-dataset

```

## MCP server setup

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