# Etsy Price & Promotion Tracker (`agency-shift/etsy-price-promotion-tracker`) Actor

Monitor public Etsy listing prices and promotions by keyword. Export displayed prices and compare saved observations to find new listings, title changes, and price or discount changes.

- **URL**: https://apify.com/agency-shift/etsy-price-promotion-tracker.md
- **Developed by:** [Valdeir Lima](https://apify.com/agency-shift) (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.18 / 1,000 listing records

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

### What does Etsy Price & Promotion Tracker do?

**Track displayed Etsy prices, promotions, and listing-title changes for product keywords.** This Actor reads the listing cards available in public [Etsy market pages](https://www.etsy.com/market/ceramic_mug), using US English and USD prices; it does **not** collect a complete marketplace inventory or all results loaded later by the website.

Start with a snapshot, or repeat a saved monitor to receive new and changed observations with before-and-after values. Apify provides API access, schedules, run monitoring, exports, and integrations; residential proxy access and compute are included in the record price.

### Why use Etsy Price & Promotion Tracker?

Watch publicly displayed competitor prices, inspect promotion changes, and keep a dated evidence trail for product research. Each record links back to its listing and source market page, making the observed values easy to check.

Prices are **asking prices**, potentially the starting price of a cheaper variant. They are not transaction prices or evidence of sales volume. Sponsored and organic cards can appear together; the Actor reports the source sponsorship signal when available. Observed card order is not verified organic search rank.

### How to use Etsy Price & Promotion Tracker

1. Enter one to five specific product keyword phrases in the Input tab.
2. Choose **Snapshot** for current observations or **New and changed listings** to maintain a monitor.
3. Set the record, listing, and page limits, then start the Actor.
4. Inspect the dataset and the **SUMMARY** key-value record for actual coverage, warnings, and errors.
5. To repeat the watch, save your input and configure an Apify Schedule with the same monitor name and queries. Use Apify webhooks or integrations to route results to your other tools.

The Actor does not create schedules or send alerts directly. No Etsy login or user-supplied proxy configuration is required.

### Input

```json
{
  "queries": ["ceramic mug"],
  "mode": "changes",
  "monitorName": "ceramic-mugs",
  "emitInitialSnapshot": true,
  "maxResults": 100,
  "maxListingsPerQuery": 100,
  "maxPagesPerQuery": 5
}
```

| Field | Meaning | Default |
| --- | --- | --- |
| `queries` | Required array of 1–5 product keyword phrases using English letters, numbers, spaces, underscores or hyphens | — |
| `mode` | `snapshot` or `changes` | `snapshot` |
| `maxResults` | Maximum paid output records across the run, 1–2,500 | `500` |
| `maxListingsPerQuery` | Maximum listings observed per query, including unchanged listings, 1–500 | `100` |
| `maxPagesPerQuery` | Maximum market pages attempted per query, 1–10 | `5` |
| `monitorName` | History name, 1–50 letters, digits, underscores or hyphens; start with a letter or digit | `default` |
| `emitInitialSnapshot` | Deliver paid initial records when creating a Changes baseline | `true` |

Limits are upper bounds, not promised record counts. Available cards and pages depend on Etsy. This version has a fixed **US / USD / en-US** scope; it does not offer currency conversion or alternative regional settings. See the Input tab for field validation.

### Output

The default dataset contains paid listing observations and change records. This simplified example is **illustrative**, not an actual Etsy listing:

```json
{
  "schemaVersion": 1,
  "listingId": "ILLUSTRATIVE_LISTING_ID",
  "query": "ceramic mug",
  "title": "Example handmade mug",
  "shopName": "Example shop",
  "price": 24,
  "currency": "USD",
  "originalPrice": 30,
  "discountPercent": 20,
  "onSale": true,
  "priceIsFrom": null,
  "isSponsored": false,
  "position": 3,
  "page": 1,
  "eventType": "changed",
  "changedFields": ["price"],
  "before": {"price": 25},
  "after": {"price": 24},
  "observedAt": "2026-09-24T12:00:00.000Z",
  "eventId": "ILLUSTRATIVE_EVENT_ID"
}
```

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel. **SUMMARY** reports coverage, limits, and errors without an additional record charge. A fatal error, when recorded, is available under **ERROR**.

### Data table

| Fields | Meaning |
| --- | --- |
| `listingId`, `url`, `title` | Listing identity, canonical link, and displayed title |
| `shopName` | Public shop name when available |
| `price`, `currency` | Displayed asking price in USD |
| `originalPrice`, `discountPercent`, `onSale` | Promotion information when the source supports it |
| `priceIsFrom` | Whether the source identifies a starting or minimum-variant price; null when unknown |
| `isSponsored` | Sponsorship indicator when established by the source; null when unknown |
| `imageUrl` | Listing image when available |
| `query`, `sourceUrl`, `page`, `position` | Query and source context; position is card order on that page |
| `observedAt` | Time this Actor observed the listing |
| `eventType` | `snapshot`, `initial`, `new`, or `changed` |
| `changedFields`, `before`, `after` | Changed field names and their previous/current values |
| `eventId` | Key downstream consumers can use to deduplicate repeated deliveries |

Unavailable source values remain null unless Changes mode preserves previously observed comparison values. A null promotion or variant flag means unknown, not false. Before and after are empty objects for records that are not `changed` events.

### How much does it cost to track Etsy prices?

The price is **$1.18 per 1,000 delivered listing records** (`$0.00118` each), plus a **$0.00005 startup event**. Apify bills startup events per GB of Actor memory, with a minimum of one event. Compute and the Actor's residential proxy access are included. The minimum maximum-charge budget is **$0.01**.

At the default memory allocation, 100 delivered records cost approximately **$0.11805** and 1,000 cost **$1.18005**. A budget limits charges; it does not guarantee that many listings are available.

Snapshots charge for delivered records each time. Changes mode charges for initial, new, and changed records. Turn `emitInitialSnapshot` off to seed the first baseline without initial dataset records. Unchanged observations, SUMMARY, and monitor history have no extra record event charge; startup still applies when a run returns no changes.

### Tips and monitor behavior

Use specific product phrases and inspect a small snapshot before scheduling a watch. The Actor collects available server-rendered market cards. Etsy may expose a limited sample, mix sponsored and organic cards, load additional listings only in the browser, repeat cards across pages, or block requests. SUMMARY makes partial coverage visible; larger limits do not guarantee deeper coverage. Cards whose visible prices or identities disagree with structured listing data are skipped and reported in SUMMARY. Verified cards remain usable; a page with no valid cards fails. Global currency, page-identity, or access failures stop pagination with a warning when earlier verified pages are available. A first-page failure adds no newly fetched records for that query; previously pending records and successful other queries can still be delivered and charged.

Changes mode tracks title, shop name, displayed price, original price, promotion status, and starting-price status. **Changes in card position alone do not generate paid events.** A newly encountered listing is `new` to this monitor, not necessarily newly published. Missing listings never become sold, retired, or disappeared events.

An explicit `onSale: false` clears previously known discount information. An unavailable source value does not invent a promotion ending or erase a known comparison value. Inspect the source links when a change needs confirmation.

Reuse the same monitor name and queries to continue the history. A different monitor name starts a separate baseline. History is isolated by Apify user, Actor, monitor, query, and USD scope; a lock prevents overlapping runs from writing conflicting state. Each query can retain up to 10,000 historical listings, with a 16 MB checkpoint limit. Exceeding a limit produces an error instead of silently deleting history.

Prepared records resume when a later run has enough output or charge budget. Delivery is at least once: a rare interruption between dataset delivery and checkpointing can repeat an `eventId` and charge for that repeated record. Consumers should deduplicate using `eventId`.

### FAQ, limitations, and support

**Does this measure sales, demand, or organic search rank?** No. It observes public listing cards, asking prices, and page order. It does not verify purchases, revenue, conversion rates, or organic rank.

**Does a lower price mean the same variant is cheaper?** Not necessarily. Listings can advertise minimum variant prices or change their variants. Use `priceIsFrom`, the listing URL, and the original displayed values when comparing offers.

**Why did I receive fewer records than requested?** Limits are caps. Etsy controls the available cards and pagination, and a Changes run may find few or no new observations. Consult SUMMARY before drawing conclusions from absence.

**Can I receive alerts?** Configure Apify schedules, webhooks, or integrations separately. This Actor does not send messages or create recurring tasks by itself.

This is an independent tool, not affiliated with Etsy or the shops returned. Use public data in accordance with applicable terms and laws. Report reproducible issues or request additional coverage and custom workflows through this Actor's Issues tab.

# Actor input Schema

## `queries` (type: `array`):

One to five product keyword phrases, using English letters, numbers, spaces, underscores, or hyphens; for example ceramic mug. Collects only the available server-rendered market cards in USD.

## `mode` (type: `string`):

Snapshot returns current listing observations. Changes remembers previous observations and outputs initial, new, or changed listings.

## `maxResults` (type: `integer`):

Maximum paid dataset records for the whole run. Your maximum-charge budget may stop delivery earlier.

## `maxListingsPerQuery` (type: `integer`):

Maximum listings examined for each query, including unchanged listings. Available coverage can be smaller.

## `maxPagesPerQuery` (type: `integer`):

Stop after this many market pages per query, or earlier when the listing limit or available results are reached.

## `monitorName` (type: `string`):

In Changes mode, reuse this name to compare with the same history. Choose a different name to start a separate baseline.

## `emitInitialSnapshot` (type: `boolean`):

In Changes mode, include paid initial records when creating a baseline. Turn off to remember the first observations without initial dataset records; later new and changed records are still charged.

## Actor input object example

```json
{
  "queries": [
    "ceramic mug"
  ],
  "mode": "snapshot",
  "maxResults": 500,
  "maxListingsPerQuery": 100,
  "maxPagesPerQuery": 5,
  "monitorName": "default",
  "emitInitialSnapshot": true
}
```

# Actor output Schema

## `records` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `error` (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 = {
    "queries": [
        "ceramic mug"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("agency-shift/etsy-price-promotion-tracker").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 = { "queries": ["ceramic mug"] }

# Run the Actor and wait for it to finish
run = client.actor("agency-shift/etsy-price-promotion-tracker").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 '{
  "queries": [
    "ceramic mug"
  ]
}' |
apify call agency-shift/etsy-price-promotion-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,agency-shift/etsy-price-promotion-tracker"
        }
    }
}
```

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/B0C31QgCboUndJWjm/builds/wtLoBJlGcLph8zYTS/openapi.json
