# SACO Saudi Products (`vixendata/saco-saudi-products`) Actor

Extract public SACO Saudi product prices in SAR, availability, brands and identifiers from product URLs or bounded sitemap discovery.

- **URL**: https://apify.com/vixendata/saco-saudi-products.md
- **Developed by:** [Vixendata](https://apify.com/vixendata) (community)
- **Stats:** 2 total users, 1 monthly users, 77.8% 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

## SACO Saudi Products

Extract public SACO Saudi product prices in SAR, stock status, brands, images and SKU identifiers. Useful for small catalog checks, price comparisons and inventory snapshots. Supply English product URLs or opt in to bounded discovery through SACO's English sitemap.

### Quick start

Direct product input:

```json
{"productUrls":["https://www.saco.sa/en/product/tactix-270003-3-in-1-block-plane-2-inch-p-000000000000000515"],"maxProducts":1}
```

Discovery input (works when the Console omits an empty productUrls array):

```json
{"discoverFromSitemaps":true,"maxProducts":1,"maxSitemaps":1}
```

Export the default dataset as JSON or CSV. SKU and productId are strings: preserve their leading zeros when importing into spreadsheets. ProductId is the public product identifier encoded in the URL, which must match the JSON-LD SKU; it is not an internal database ID.

### Output

Each row contains source, url, productId, sku, title, description, brand, currency, price, regularPrice, salePrice, availability, inStock, images, attributes, rating, ratingCount, reviewCount and collectedAt (UTC).

The current price comes from the matching public Product JSON-LD offer and must be SAR. regularPrice and salePrice are null because the verified source does not distinguish these. Unknown ratings/counts remain null. Attributes are populated only from explicit Product properties. No reviews are invented or separately fetched. Missing products or mismatching identity/currency produce errors rather than guessed rows. Empty runs fail clearly.

The verified example is Tactix 270003 3-in-1 Block Plane 2-inch, SKU 000000000000000515, SAR 29.95, InStock. This is a dated observation, not a price guarantee. Prices and online availability may differ from checkout or a particular store.

### Inputs and limits

- productUrls: up to 100 clean HTTPS www.saco.sa/en/product/...-p-SKU URLs; no query strings or external hosts.
- discoverFromSitemaps: false by default. When true, fill remaining product capacity from sitemap_en.xml. Explicit URLs come first. No example URL is ever injected.
- maxProducts: 1–100, default 10; bounds product attempts.
- maxSitemaps: exactly 1; the verified English sitemap is a flat file.
- requestDelaySeconds: 1–30, default 2; robots crawl-delay/request-rate can increase it.

The large sitemap is parsed incrementally in 64 KiB chunks. Discovery closes the HTTP response immediately when the product limit is reached; it does not load the full catalog. Sitemap decompressed responses are capped at 24 MB, other responses at 8 MB. Each fetch permits at most three requests including redirects; each run is capped at 3 × (1 + maxProducts + maxSitemaps) HTTP requests. Only exact allowed hosts and paths can be followed, including redirects. Image URLs are returned as data and never fetched.

### Source access and limitations

The Actor fetches robots.txt before products or sitemap, honors the rules for its descriptive SacoProductActor/1.0 user agent, and stops on unavailable/invalid robots, explicit disallow, HTTP 401/403/429 or detected challenges. No login, proxy, CAPTCHA solving, search route, account route or /api/ endpoint is used. There are no retries after blocking signals. A malformed sitemap or network failure aborts rather than falling back to another access route. Product pages that fail ordinary extraction are counted in RUN_SUMMARY. Partial rows can be returned only for ordinary per-product errors, never by continuing past a blocking signal.

Supports the observed English product-page JSON-LD format. Arabic pages, category pagination, store-level inventory, historical prices, authenticated prices and reviews are outside this version. Site markup changes may require maintenance. Use modest limits and ensure your use of retrieved data meets applicable obligations.

### Validation

Python 3.12; exact requirements pin Apify 4.0.2, Crawlee 1.10.4 and Pydantic 2.13.5.

```sh
python -m venv .venv
.venv/bin/pip install -r requirements.txt jsonschema
.venv/bin/python -m unittest discover -s tests -v
```

Tests cover genuine sanitized source data, SAR/identity validation, missing prices, leading-zero preservation, input defaults/limits, robots behavior, redirect restrictions, request caps, blocking signals, streaming discovery and official Apify input/dataset metaschemas. The checked-in fixtures retain only public product JSON-LD, robots text and a short genuine sitemap excerpt. Metaschema snapshots were obtained from the official Apify schemas; attribution is included alongside them.

Source HTTP 200 evidence was collected on 2026-10-07. The local shell's direct network integration attempt was blocked by unavailable DNS; live-check.json distinguishes source evidence from recorded-response replay. Hosted execution must be verified separately before claiming hosted success. No deployment, publication or schedules are performed by this package.

# Actor input Schema

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

Clean https://www.saco.sa/en/product/...-p-SKU URLs. Maximum 100. Leading zeros are preserved. The editable example is not an API default.

## `discoverFromSitemaps` (type: `boolean`):

Opt in to the verified English sitemap_en.xml. Only product URLs are followed, with no search or API access.

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

Maximum product URLs attempted, including explicit URLs and discovered URLs. Range 1–100.

## `maxSitemaps` (type: `integer`):

The verified English sitemap is a single flat file; maximum one sitemap request (redirects separately bounded).

## `requestDelaySeconds` (type: `integer`):

Minimum delay between requests. Robots crawl-delay, when longer, takes precedence. Range 1–30.

## Actor input object example

```json
{
  "productUrls": [
    "https://www.saco.sa/en/product/tactix-270003-3-in-1-block-plane-2-inch-p-000000000000000515"
  ],
  "discoverFromSitemaps": false,
  "maxProducts": 1,
  "maxSitemaps": 1,
  "requestDelaySeconds": 2
}
```

# 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 = {
    "productUrls": [
        "https://www.saco.sa/en/product/tactix-270003-3-in-1-block-plane-2-inch-p-000000000000000515"
    ],
    "maxProducts": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("vixendata/saco-saudi-products").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 = {
    "productUrls": ["https://www.saco.sa/en/product/tactix-270003-3-in-1-block-plane-2-inch-p-000000000000000515"],
    "maxProducts": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("vixendata/saco-saudi-products").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 '{
  "productUrls": [
    "https://www.saco.sa/en/product/tactix-270003-3-in-1-block-plane-2-inch-p-000000000000000515"
  ],
  "maxProducts": 1
}' |
apify call vixendata/saco-saudi-products --silent --output-dataset

```

## MCP server setup

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

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/2GMoOGaYJPHVGr9sn/builds/zw6hOaGOXGBZ0BTZg/openapi.json
