# Jordan Tender Radar — JONEPS Government Tenders (Arabic-safe) (`dottti/joneps-tender-radar`) Actor

Live Jordanian government tenders from JONEPS with Arabic-safe keyword matching. Clean JSON: buying entity, tender type, publish date, submission deadline, tender document price in JOD, stage and a direct tender link.

- **URL**: https://apify.com/dottti/joneps-tender-radar.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 $3.50 / 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

## Jordan Tender Radar — JONEPS Government Tenders (Arabic-safe)

Search live Jordanian government tenders published on [JONEPS](https://www.joneps.gov.jo), the Jordan Online E-Procurement System, and get them back as clean JSON, CSV or Excel.

Filter by keyword, buying entity, tender type, publish date and submission deadline. Schedule it to run every morning and you have a tender alert for your sector.

### Who uses this

- **Suppliers and contractors** get a daily list of new tenders they can bid on.
- **Bid teams** see the deadline, the tender document price in JOD and the buying entity in one row.
- **Consultancies and resellers** watch several sectors or ministries for their clients.

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

### What you get

One record per tender. This record comes from a real run on 20 October 2026, with **Include tender details** switched on:

```json
{
  "tenderNumber": "2026004305-00",
  "title": "فتح و تعبيد و كشط و خلطة اسفلتية في شوارع بلدية غرب اربد",
  "titleRaw": "فتح و تعبيد و كشط و خلطة اسفلتية في شوارع بلدية غرب اربد ",
  "titleEn": "road constructions",
  "entity": "بلدية غرب اربد",
  "entityCode": "91210300",
  "tenderType": "أشغال",
  "tenderTypeCode": "EP0015",
  "category": null,
  "procurementMethod": "المناقصة العامة",
  "localOrInternational": "local",
  "tenderStatus": "نشر دعوة عطاء",
  "publishDate": "2026-10-20",
  "publishedAt": "2026-10-20T14:02:00+03:00",
  "submissionDeadlineDate": "2026-11-01",
  "submissionDeadline": "2026-11-01T11:00:00+03:00",
  "enquiryDeadline": "2026-10-28T14:00:00+03:00",
  "openingDate": "2026-11-01T12:00:00+03:00",
  "isOpen": true,
  "currency": "JOD",
  "documentPriceJod": 200,
  "documentPriceRaw": "200.000 JOD",
  "feePaymentDeadline": "2026-11-01T09:00:00+03:00",
  "bidBond": "3% قيمة كفالة الدخول يجب ايداع كفالة الدخول في صندوق العطاءات قبل موعد نهاية ايداع العروض",
  "submissionPlace": "تودع الكفالات ورقيا في صندوق العطاءات في مركز بلدية غرب اربد غرفة 207",
  "url": "https://www.joneps.gov.jo/ep/invt/WorkDetails.do?tendNo=2026004305&tendSeq=00&..."
}
```

Notes on the fields:

- **Without details** you get the list fields: tender number, title, buying entity, tender type, procurement method, local or international, publish date, deadline date, stage and link. Detail fields are `null`.
- **With details** each tender also gets exact deadline and opening times, the tender document price, the bid bond, the English title, the description and the entity code.
- **Money is Jordanian dinars.** The `currency` field says `JOD` and the price field name ends in `Jod`. `documentPriceRaw` keeps the text exactly as JONEPS shows it.
- **Times are Amman time** (UTC+3), written as ISO 8601 with the `+03:00` offset.
- **Missing values are `null`.** When JONEPS leaves a field empty, the Actor does not guess. Some tenders, for example many medicine tenders, publish no document price.
- `titleRaw` keeps the title exactly as published, so you can still search the portal for it.
- `url` opens the tender on JONEPS.

### Arabic-safe matching

JONEPS titles spell the same word in different ways. The same week can have `خلطة اسفلتية` and `بالخلطه الاسفلتيه`. A plain text search finds one and misses the other.

This Actor folds the common variants before matching:

- `أ إ آ ا` all match each other
- `ة` matches `ه`, and `ى` matches `ي`
- diacritics and tatweel (`ـ`) are ignored
- Arabic-Indic digits (`٢٠٢٦`) match Western digits (`2026`)

The output text itself is never changed beyond whitespace cleanup.

### Input

| Field | What it does |
| --- | --- |
| `keywords` | Words matched against title, buying entity, tender number and type. Empty returns everything the other filters allow. |
| `matchMode` | `any` (default) or `all`. |
| `entityContains` | Keep only tenders from buying entities whose name contains this text, for example `وزارة الصحة` or `بلدية`. |
| `status` | `open` (default): deadline today or later. `all`: closed tenders too. |
| `tenderTypes` | `EP0011` supplies, `EP0012` services, `EP0014` technical services, `EP0015` works, `EP0016` medicines. |
| `localOrInternational` | `local`, `international`, or empty for both. |
| `publishedFrom` / `publishedTo` | Publish date window, `YYYY-MM-DD`. JONEPS applies it on its side. |
| `deadlineFrom` / `deadlineTo` | Submission deadline window, `YYYY-MM-DD`. |
| `maxItems` | Hard cap on delivered tenders, and therefore on cost. Default 100. |
| `includeDetails` | Adds deadline times, document price in JOD, bid bond and more. Three extra small requests per tender. |
| `requestDelayMs` | Pause before each request. Minimum 1,000 ms. |
| `maxRetries` | Retries per request on timeouts, 429 and 5xx. |

#### Example: works tenders from municipalities, closing within two weeks

```json
{
  "status": "open",
  "tenderTypes": ["EP0015"],
  "entityContains": "بلدية",
  "deadlineTo": "2026-11-03",
  "includeDetails": true,
  "maxItems": 200
}
```

#### Example: IT and computer tenders published since yesterday

```json
{
  "keywords": ["حاسوب", "حواسيب", "شبكات", "برمجيات", "computer"],
  "matchMode": "any",
  "publishedFrom": "2026-10-19",
  "includeDetails": true,
  "maxItems": 100
}
```

### Daily tender alert in 3 steps

1. Set your filters. For a daily feed, keep `status` on `open` and add your keywords or tender types.
2. Click **Save as a new task**. In the task, open **Schedules** and add a daily schedule, for example 07:00 Amman time.
3. Open the task's **Integrations** tab and connect email, Slack or Google Sheets. New tenders then arrive every morning.

Tip: to receive only new tenders, set `publishedFrom` to yesterday's date in the task, or remove duplicates by `tenderNumber` in your sheet.

### Pricing

Pay per tender delivered. You are charged only for tenders that pass your filters and are stored in your dataset. `maxItems` caps the spend of any run.

### Limits

- Data comes from the public JONEPS tender list. No login, no credentials, no personal data.
- JONEPS is slow at times. Each list page of 100 tenders usually takes 2 to 6 seconds, and requests are spaced at least one second apart. Timeouts are retried with backoff.
- With `status: all` and no deadline dates, JONEPS applies its own default window: deadlines from one month ago to two months ahead. Set `deadlineFrom` and `deadlineTo` to search further back. A window covering 2025 and 2026 returned 7,461 tenders in testing, and deep pages loaded normally.
- Awarded tenders are not included. JONEPS's final-award list did not load within 280 seconds in testing.
- The `stage` field shows the last step JONEPS marks as reached in its progress bar. Newly published tenders often have no step marked yet, so `stage` is `null` for them. `tenderStatus` (with details) gives the portal's own status text.
- Keyword matching uses the list fields. The English title and the description from the detail pages are returned, but they are not searched.
- Tender documents are not downloaded. The record links to the tender page on JONEPS.

### Development

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

# Actor input Schema

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

Words to match against the tender title, the buying entity, the tender number and the tender type. Arabic is matched after normalisation, so alef and hamza forms (أ إ ا), ة and ه, and ى and ي all match each other. JONEPS titles often mix these, for example اسفلتيه and اسفلتية. English words match case-insensitively. Leave empty to return every tender the other filters allow.

## `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 buying entity (الجهة المشترية) contains this text. Normalised the same way as keywords. Examples: وزارة الصحة, بلدية, دائرة المشتريات الحكومية.

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

open: tenders whose submission deadline is today or later (Amman time). all: closed tenders included. Without deadline dates JONEPS applies its own window (deadline from one month ago to two months ahead); set deadlineFrom and deadlineTo to search further back. JONEPS's awarded lists are not offered because they did not load reliably in testing.

## `tenderTypes` (type: `array`):

JONEPS tender types, read from the portal's own نوع العطاء list. Leave empty for all types. One search is made per type.

## `localOrInternational` (type: `string`):

Filter on the portal's محلي/دولي field. Leave empty for both.

## `publishedFrom` (type: `string`):

Date in YYYY-MM-DD format. Applied by JONEPS itself (تاريخ النشر). Example for a daily alert: yesterday's date.

## `publishedTo` (type: `string`):

Date in YYYY-MM-DD format. Applied by JONEPS itself.

## `deadlineFrom` (type: `string`):

Date in YYYY-MM-DD format (آخر موعد لتقديم العروض). With status open, dates before today are moved to today.

## `deadlineTo` (type: `string`):

Date in YYYY-MM-DD format. Set it 7 days ahead for a closing-soon list.

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

Hard cap on delivered tenders. You pay per tender delivered, so this is also your cost cap.

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

Read each matching tender's public detail tabs: exact deadline and opening times, tender document price in JOD, bid bond, sub-type, English title, description and entity code. Adds 3 small requests per tender, so a run takes about 4 seconds longer per tender.

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

Pause before every request to JONEPS. The minimum is 1000 ms; the portal is a government system and slows down under load.

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

Retries with exponential backoff when JONEPS times out or answers 429 or 5xx.

## Actor input object example

```json
{
  "keywords": [
    "صيانة"
  ],
  "matchMode": "any",
  "status": "open",
  "localOrInternational": "",
  "maxItems": 20,
  "includeDetails": true,
  "requestDelayMs": 1500,
  "maxRetries": 4
}
```

# Actor output Schema

## `tenders` (type: `string`):

Matching tenders: number, title, buying entity, tender type, publish date, submission deadline, stage, tender document price in JOD (with details on), and a direct link to the tender on JONEPS.

# 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 = {
    "keywords": [
        "صيانة"
    ],
    "maxItems": 20,
    "includeDetails": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("dottti/joneps-tender-radar").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 = {
    "keywords": ["صيانة"],
    "maxItems": 20,
    "includeDetails": True,
}

# Run the Actor and wait for it to finish
run = client.actor("dottti/joneps-tender-radar").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 '{
  "keywords": [
    "صيانة"
  ],
  "maxItems": 20,
  "includeDetails": true
}' |
apify call dottti/joneps-tender-radar --silent --output-dataset

```

## MCP server setup

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

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/hC2Q7q2iGlWH8BpzA/builds/qOo2eVGUm1aCcvvWH/openapi.json
