Salla Store Catalog & Price Scraper
Pricing
from $2.50 / 1,000 results
Salla Store Catalog & Price Scraper
Products, prices and stock from any Salla store. Give it a normal store link - it works out the API identifier itself. HTTP only, no browser, no key.
Pricing
from $2.50 / 1,000 results
Rating
0.0
(0)
Developer
ABDULWAHAB NASER RASHED ALQARAWI
Maintained by CommunityActor stats
0
Bookmarked
4
Total users
3
Monthly active users
3 days ago
Last modified
Categories
Share
Products, prices and stock from any Salla store — Saudi Arabia's largest e-commerce platform.
Give it a normal store link. No API key, no browser, no proxy.
The identifier problem, solved
Salla's API needs a Store-Identifier header that no shop owner has ever seen.
An Actor that asks you for it is asking for something you do not have.
This one works it out itself — and when a link's name is not the store's name, it reads the store's real identity from its own page. All of these work:
salla.sa/coffee_souqcoffee_souqotor200sa.comwww.otor200sa.comhttps://otor200sa.com/ar/xvXnyXKcoffeesouq1.com (a custom domain whose name differs from the store's)
What you get
One row per product, 35 fields, every one always present — missing values
come back as null, never dropped.
| Field | Example |
|---|---|
name | مجموعة أورموند جين فيستيف كاليندر |
price | 1150.0 |
regularPrice | 1955.0 |
currency | SAR |
isOnSale | true |
discountPct | 41.18 |
isOutOfStock | true |
category | للجنسين |
rating / ratingCount | 4.5 / 2 |
Plus productId, sku, gtin, mpn, salePrice, discountStarts,
discountEnds, isAvailable, quantity, unlimitedQuantity, brand,
productType, status, tags, weight, imageUrl, hasOptions,
productUrl, store, scrapedAt, and changeType / previousPrice /
changedFields / previousValues / discountEnded (filled only with
onlyChanges, see below).
A real discount, not a claimed one. Some stores set the on-sale flag with
both prices equal; discountPct is filled only when the regular price is
genuinely higher, and onlyOnSale returns only those products.
Stars with their count. rating alone would make five stars from two
buyers look like five stars from five hundred; ratingCount says which.
Use it for
- Competitor price monitoring — schedule it with
onlyChanges - Discount tracking — set
onlyOnSale, sort bydiscountPct - Stock-out alerts —
onlyChangesreturns a product whenisOutOfStockflips - Catalog and market research across Saudi and Gulf stores
Only what changed
Turn on onlyChanges and schedule the Actor. Each run returns only products
that are new, or whose price, regularPrice, isAvailable or
isOutOfStock changed since the last run. Every row says why:
changeType is NEW or UPDATED, and previousPrice holds the price it had
before (null for new products). The first run returns every product as NEW.
An UPDATED row also lists the watched fields that moved in changedFields
(for example ["price"]) and all four watched values from before in
previousValues; both are null on NEW rows.
Removed products (emitRemoved, off by default). With onlyChanges on,
also returns a REMOVED row — product id, URL, name and SKU as last seen, and
previousValues — for each known product that is no longer in the store.
A removal row is a delivered row and is charged like any other. Removals are
reported only after a read that is genuinely complete: the cursor walked to its
end, no request failed, no product skipped or missing an id, not stopped by
maxProductsPerStore, the page guard or the spending limit, and
onlyOnSale off. Any other run reports no removals and says why in the run
report (removalNote). A product is forgotten only once its removal row was
delivered; if the spending limit stops the run first, the rest are reported
next run. A state saved by a build before this option existed is first
re-recorded by one complete read, so turning emitRemoved on never reports
removals on its first run.
Ended discounts (includeEndedDiscounts, off by default). With both
onlyOnSale and onlyChanges on, a product whose discount ended is normally
filtered out, because it is no longer discounted. Turn this on to receive it
as an UPDATED row with discountEnded: true. Only a discount this build
recorded counts: a snapshot saved by an older build did not record discounts,
so an ending in the first run after upgrading is not recognised.
The last known state of each store is kept in a named key-value store in your
account (stateStoreName, default salla-catalog-state). Use a different name
for each separate monitor.
A store's state is saved only when its read finished cleanly. If a request fails partway, or the run stops at your spending limit, the old state is kept and those products are compared again on the next run — a change is never skipped, though a product delivered just before the stop can be delivered again.
With onlyChanges on, maxProductsPerStore caps the products checked, not
the rows returned; otherwise a store with nothing new would be read to its last
page on every run.
Speed
Measured 14 Sep 2026 on a local machine, default 200 ms delay between pages:
otor200sa 300 products 21 requests 15.6 secondscoffee_souq 300 products 21 requests 17.7 seconds
That is 0.75–0.85 s per page of 15, delay included, so a 1,000-product store takes roughly a minute. Speed on Apify's servers will differ.
Honest notes
Pagination is cursor-only. page= is ignored and per_page is capped at 15
by the API — not by this Actor. The cursor is walked for you.
A store is confirmed only when products come back. The API answers
"success" with zero products for a wrong identifier, so a success code alone is
never trusted. A store that cannot be confirmed is listed in the run report
under unresolved, and named in the run's status message.
One odd product never fails the run. A product whose data arrives in an
unexpected shape is skipped, counted under skippedProducts in the run report,
and every other product is delivered.
No personal data. The payload's availability_notify block holds booleans
about notification channels, not anyone's contact details. Nothing identifying
a person is collected.
robots.txt is honoured on both sites it reads. api.salla.dev's file is
read before any product request; it allows a normal agent on this path and
disallows a named list of AI crawlers, and this Actor ships its own identifier.
When a link's name is not the store's identifier, the store's own page is read
to find it — and that site's robots.txt is checked first. If it disallows the
page, the page is not read and the store is listed as unresolved.
maxProductsPerStore counts rows returned. Products filtered out by
onlyOnSale do not use it up (see onlyChanges above for the one exception).
Arabic links do not break the run. An Arabic domain is looked up in its DNS form; an Arabic slug cannot be a Salla API identifier, so the store's page is read instead.
Every run publishes a report to the key-value store under RUN_REPORT:
requests, rows, unresolved stores, skipped products, cost per 1,000 rows, and
for each store the identifier it resolved to, whether its read was cut short
(truncated), and a catalog summary of the products read — count, real
discounts, out of stock, and minimum, median and maximum price.
Questions, or a store that will not resolve?
Open an issue with the store link. Most failures are one store's theme, and they are usually quick to fix.