# Bahrain Product Facts (`vixendata/bahrain-product-facts`) Actor

Independent public product facts, exact BHD prices and source availability. Bounded homepage discovery.

- **URL**: https://apify.com/vixendata/bahrain-product-facts.md
- **Developed by:** [Vixendata](https://apify.com/vixendata) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 successfully parsed product rows

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Bahrain Product Facts

Independent extractor of factual product data from public Cazasouq Bahrain pages. Returns SKU, model, title, BHD price, original displayed price, explicit source availability and stock quantity when numeric. No logo, affiliation, copied descriptions, reviews, images or inferred variants.

### Input and coverage

Supply `productUrls`, or enable `discoverProducts` to discover product cards on the public homepage. Omitted/empty URLs never inject an example. See `smoke/direct.json` and `smoke/discovery.json` for minimal one-product runs.

- `productUrls`: up to 100 clean HTTPS www.cazasouq.com product URLs ending in a numeric identifier. Queries, fragments, PHP routes and other hosts are rejected.
- `discoverProducts`: default false. One homepage only; no category, pagination or full-catalog claim. Discovery currently exposes a bounded subset of homepage products, which can change.
- `maxProducts`: 1–100, default 5, after SKU deduplication.
- `maxRequests`: 2–150, default 15; includes robots, redirects, homepage and products. Exhaustion fails closed.

The source sitemap index was reachable on 7 October 2026, but its product shard twice returned HTTP 200 containing a 504 error page. This release intentionally does not fetch or advertise sitemap discovery.

### Data meaning

BHD uses three decimals and integer fils: source `245.50` becomes price `245.500`, priceFils `245500`. Decimal arithmetic rejects sub-fils precision. `sourcePrice` retains the original numeric string. `originalPrice` is the struck-through displayed source price, not a verified MSRP or price history. Prices and availability are snapshots, not checkout quotes.

`availability` preserves the source Schema.org value; `inStock` is true only for explicit InStock, false for explicit OutOfStock/SoldOut/Discontinued, and null otherwise. `stockText` preserves the visible source field. `stockQuantity` is populated only for a fully numeric source stock field, not inferred from in-stock status. Missing optional facts are null and listed in `missingFields`. Brand is not inferred from the title. Language variants deduplicate by SKU; no translation or variant completeness is claimed.

The source emits duplicate Product JSON-LD blocks. Their SKU/model/name and offer URL/currency/price/availability must agree. The parser checks canonical URL, visible SKU/model and current price where present. Source date, descriptions, image sizes and seller metadata are not extracted. Unsupported offer lists or ambiguous products fail closed.

### Access and restrictions

Robots is fetched and checked before public content. Sequential requests are paced at least one second, increased for robots crawl-delay/request-rate. HTTP 401/403/429, active challenge, unsafe redirect, missing identity, request or byte budget exhaustion fail the run without partial dataset publication. No login, proxy rotation, CAPTCHA solving, browser bypass, internal APIs, query filters, sorting or pagination. Every request is bounded to 30 seconds and 12 MB (robots 1 MB); redirects are limited and revalidated.

Cazasouq is identified solely as the factual source. Terms: https://www.cazasouq.com/الأحكام-والشروط . Their terms protect original content and require written permission for trademark use in products/services. This package uses generic independent branding, no logo or copied long-form content and no affiliation claim. Research found no explicit blanket scraping prohibition; that is a limited screen, not legal advice or a permission grant.

### Reproduction and validation

Python 3.12. Install requirements.txt, then `python -m src.main` under Apify/local Apify storage. Tests: install `jsonschema==4.26.0` as a test-only dependency and run `python -m unittest discover -s tests -v`. Official Apify input/dataset metaschemas are bundled for self-contained metadata tests. Runtime pins: Apify 4.0.2, Crawlee 1.10.4, Pydantic 2.13.5.

Two genuine product HTML responses and homepage links were sanitized to factual extraction markup; provenance is in tests/fixtures/provenance.json. Out-of-stock, unknown stock, JSON-LD graph, conflicting data, access denials and precision cases are synthetic regression tests. They do not imply live coverage of those conditions. No full-catalog or all-language coverage is claimed. `sample-output.json` contains fixture-derived rows, not a hosted production run. Hosted build and smoke verification are separate deployment steps.

Local direct live smoke on 7 October 2026 stopped at robots DNS resolution in the restricted executor. No source HTTP denial occurred and no alternative route was attempted. Fixture and mocked transport checks passed; hosted verification remains required. Rebuild upload map/manifest with `python package.py`.

### Pricing

The price is $3 per 1,000 product rows saved to the default dataset, plus $0.00005 per run start, with usage included; check the Store pricing panel for the price applying to your run.

# Actor input Schema

## `productUrls` (type: `array`):

Public clean Cazasouq product URLs ending in a numeric identifier. Empty or omitted stays empty; no example fallback.

## `discoverProducts` (type: `boolean`):

Discover only product cards on the public homepage. Bounded, not a complete catalog.

## `maxProducts` (type: `integer`):

Maximum product rows; SKU deduplication applies.

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

Total HTTP requests including robots, homepage and redirects. Exhaustion fails closed.

## Actor input object example

```json
{
  "discoverProducts": false,
  "maxProducts": 5,
  "maxRequests": 15
}
```

# Actor output Schema

## `results` (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("vixendata/bahrain-product-facts").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("vixendata/bahrain-product-facts").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 vixendata/bahrain-product-facts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vixendata/bahrain-product-facts"
        }
    }
}
```

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/pvQbYzecq75f6TE5z/builds/JVxEHI78NgdzRtOsr/openapi.json
