# T-Soft Store Scraper | Products and Prices (`atesen-software/tsoft-store-scraper`) Actor

Give a T-Soft store domain and get the catalogue: prices, discounts, stock, SKUs, barcodes, VAT, brands and images. No browser. Changes-only mode returns new, changed and removed products for price monitoring.

- **URL**: https://apify.com/atesen-software/tsoft-store-scraper.md
- **Developed by:** [Atesen Software](https://apify.com/atesen-software) (community)
- **Categories:** E-commerce, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 products

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

Scrape **any online store built on [T-Soft](https://www.tsoft.com.tr)**, the Turkish e-commerce platform used by about 1,600 shops, many of them large brands. Give it a domain like `direnc.net` and get the whole catalogue as JSON, CSV or Excel: **prices, discounts, stock, SKUs, barcodes, brands, categories and images**. For **price & stock monitoring**, turn on *changes only* and schedule it to get just the new, changed and removed products.

- Works on every T-Soft storefront: products are found through the store's sitemap and read from the data T-Soft embeds in each product page.
- **No browser, no login, no proxy.** Fast and cheap (256 MB is enough).
- Checks that each site really runs on T-Soft. Other sites get a free `not_tsoft` status row.
- T-Soft pages also expose the **stock quantity, VAT rate, supplier code and barcode** of each product.

### Use cases

- **Competitor price monitoring** for brands and retailers on T-Soft (schedule it daily or weekly).
- **Stock, discount and assortment tracking**: new products, sold-out items, campaigns.
- **Catalogue export / migration** from a T-Soft store (with SKU and barcode).
- **Market research** across many Turkish online shops; MAP / price-policy checks for distributors.

### How do I know a store runs on T-Soft?

Usually pages carry T-Soft `PRODUCT_DATA` / `PAGE_TYPE` scripts. You don't have to check yourself: put any domain in and the scraper tells you.

### Input

| Field | Description |
|---|---|
| `stores` | Store domains or URLs (`direnc.net`, `https://www.motorobit.com`) |
| `productUrls` | Individual product URLs, to track specific products |
| `maxProductsPerStore` | Default 100; `0` = whole catalogue |
| `includeDescription` | Add the product description (plain text) |
| `maxConcurrency` | Parallel page loads per store (default 3) |
| `changesOnly` | Off by default. On: save only new, changed and removed products since the previous run; unchanged ones are free (see [Price & stock monitoring](#price--stock-monitoring)) |
| `monitorKey` | Optional name of the watch list; empty = derived from the input |

```json
{
  "stores": ["direnc.net", "motorobit.com"],
  "maxProductsPerStore": 100
}
```

### Output

One row per product (`type: "product"`):

| Field | Description |
|---|---|
| `store`, `platform`, `url` | Store domain, `T-Soft`, product page |
| `productId`, `sku`, `barcode` | Identifiers |
| `name`, `brand`, `categories` | Product info (category path from root to leaf) |
| `price` | Current selling price, VAT included (discounted price when there is one) |
| `originalPrice`, `discountPercent` | Crossed-out list price and discount, when the shop shows one |
| `currency` | Usually `TRY` |
| `inStock`, `totalStock` | Availability and stock quantity when the store publishes it |
| `imageUrl`, `imageUrls` | Product images |
| `variantCount`, `variants` | Variants (options, SKU, barcode, price, stock) where the platform exposes them |
| `description` | Only with `includeDescription` |

Prices are what a visitor of the store sees. Some shops show different currencies to visitors from abroad; check `currency`.

#### Status rows

The run doesn't fail on bad input. Problems become rows with `type: "status"` and are **never charged**:

| status | meaning |
|---|---|
| `invalid_input` | No stores or product URLs given |
| `invalid_url` | Not a valid domain or URL |
| `unreachable` / `failed` | The site could not be loaded |
| `not_tsoft` | The site or page does not run on T-Soft |
| `no_products` | The store's sitemap has no live product pages |
| `not_found` | The product URL returns 404 |
| `not_a_product` | A T-Soft page that is not a product page |
| `blocked` | The shop showed a bot challenge; try again later |

Use the **Products** and **Status rows** views in the dataset tab. `RUN_SUMMARY` in the key-value store has the counts.

### Price & stock monitoring

Turn on **Changes only** (`changesOnly: true`) and schedule the Actor. Each run is compared with the previous run of the same input (or the same **Monitor name**, `monitorKey`), and only what changed is saved. Unchanged products are **not saved and not charged**; new and changed products cost the normal per-product price.

| `changeType` | Meaning | Charged |
| --- | --- | --- |
| `new` | first time this product is seen (every product on the first run) | yes, as a normal product |
| `price_changed` | price went up or down, also per variant (`previousPrice`, `priceChange`, `priceChangePercent`) | yes |
| `unavailable` | still listed but sold out — temporarily unavailable | yes |
| `back_in_stock` | sold out last time, buyable again | yes |
| `stock_changed` | stock quantity changed, or a variant sold out / came back | yes |
| `variants_changed` | a variant (size, colour) was added or removed | yes |
| `removed` | product is gone: its page returns 404 (`removedReason: not_found`), or it was missing from the store's sitemap or a fully crawled store two runs in a row (`not_listed`) | **free** |
| unchanged | not saved | **free** |

Every changed row keeps all the normal product fields and adds `changeType`, a `changes` list (e.g. `{"field": "price", "from": 1299, "to": 1149}`), `previousPrice`, `priceChange`, `priceChangePercent`, `previousInStock`, `previousSeenAt` and `firstSeenAt`. Products are matched by store + the platform's stable product ID (`productId`; variants by `variantId`), so a renamed product is not reported as new. A product that could not be loaded in one run (timeout, block) is never reported as removed. The **Changes** view in the Output tab shows just these columns; the run summary (`RUN_SUMMARY`) counts new, changed, unchanged and removed products.

Schedule example — every morning, get only the price and stock changes:

```json
{
  "stores": ["fantomshop.com"],
  "maxProductsPerStore": 0,
  "changesOnly": true,
  "monitorKey": "fantomshop-daily"
}
```

Save the input as a task, then in Apify Console open **Schedules → Create new**, set cron `0 7 * * *` (07:00 every day) and add the task. Connect a webhook, Google Sheets or Slack integration to receive only the changes. Use `maxProductsPerStore: 0` (whole store) for complete removal detection; with a limit, the same first products of the sitemap are compared. Keep the input the same between runs: a different input or monitor name starts a new watch list (all products `new` again).

### Pricing

Pay per event: **$0.001 per product** saved (that's $1 per 1,000 products), plus a tiny actor-start fee. Status rows are free.

### Tips

- Start with `maxProductsPerStore: 20` to check a store, then raise it or set `0` for the full catalogue.
- To monitor prices and stock, schedule the actor with **Changes only** on: you get (and pay for) only what changed.
- Keep `maxConcurrency` low (2–4) for small shops.

### Related actors

- [ikas Store Scraper](https://apify.com/atesen-software/ikas-store-scraper), [Ticimax Store Scraper](https://apify.com/atesen-software/ticimax-store-scraper), [IdeaSoft Store Scraper](https://apify.com/atesen-software/ideasoft-store-scraper), [T-Soft Store Scraper](https://apify.com/atesen-software/tsoft-store-scraper)
- [Trendyol Scraper](https://apify.com/atesen-software/trendyol-scraper), [Hepsiburada Scraper](https://apify.com/atesen-software/hepsiburada-scraper)

This actor reads only public product pages and sitemaps, like a regular visitor. Please respect each shop's terms and scrape at a reasonable pace.

### FAQ

**Which stores work?** Public T-Soft storefronts. Paste the domain. A store that is not T-Soft becomes a free status row.

**How do I monitor price and stock?** Turn on `changesOnly` and reuse the same `monitorKey`.

**What does the first run return?** The saved example reads two real T-Soft stores and returns product rows with price, stock, SKU and URL.

# Actor input Schema

## `stores` (type: `array`):

Domains or URLs of stores running on T-Soft, e.g. "direnc.net" or "https://www.motorobit.com". The scraper checks that the site runs on T-Soft, reads its product sitemap and scrapes the product pages.

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

Individual product page URLs from any T-Soft store. Use this to track prices of specific products.

## `maxProductsPerStore` (type: `integer`):

Stop after this many products for each store. 0 = the whole catalogue.

## `includeDescription` (type: `boolean`):

Add the product description as plain text.

## `maxConcurrency` (type: `integer`):

How many product pages are loaded at the same time. Keep it low to be gentle with small shops.

## `changesOnly` (type: `boolean`):

Compare every product with the previous run that used the same input (or the same monitor name) and write only **new** products and products whose **price or stock changed** (each row gets a changeType: new, price_changed, stock_changed, variants_changed, unavailable, back_in_stock). Products that disappeared are written as free 'removed' rows. Unchanged products are skipped and not charged. Off (default) = normal full scrape. Schedule the Actor with this on to get a daily change feed.

## `monitorKey` (type: `string`):

Optional. Runs with the same name share one snapshot. Leave empty to derive it automatically from your store domains and product URLs, so the same input always continues the same watch list.

## Actor input object example

```json
{
  "stores": [
    "direnc.net",
    "motorobit.com"
  ],
  "maxProductsPerStore": 100,
  "includeDescription": false,
  "maxConcurrency": 3,
  "changesOnly": false
}
```

# Actor output Schema

## `products` (type: `string`):

No description

## `changes` (type: `string`):

No description

## `allFields` (type: `string`):

No description

## `summary` (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 = {
    "stores": [
        "direnc.net",
        "motorobit.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("atesen-software/tsoft-store-scraper").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 = { "stores": [
        "direnc.net",
        "motorobit.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("atesen-software/tsoft-store-scraper").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 '{
  "stores": [
    "direnc.net",
    "motorobit.com"
  ]
}' |
apify call atesen-software/tsoft-store-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,atesen-software/tsoft-store-scraper"
        }
    }
}
```

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/clqXJn7SgI7u2hz8K/builds/0C9LjOiq8osJltiSn/openapi.json
