Watchlists

Hand us up to 50,000 item ids (ASINs, Walmart item ids, TCINs or Best Buy SKUs) and a cadence; we run the cheapest call ladder that catches every change and push the changes to your webhook.

Hand us up to 50,000 item ids (ASINs, Walmart item ids, TCINs or Best Buy SKUs) and a cadence; we run the cheapest call ladder that catches every change and push the changes to your webhook.

Overview

A watchlist is the recommended call strategy, run for you. On Amazon: a cheap pulse sweep of every ASIN on each run, standard only for the ASINs that moved, and full once per new ASIN. On Walmart, Target and Best Buy (one tier each): every item fetched and compared on each run. Changes arrive as signed webhooks, batched per run; the same data is always available to pull.

A list holds the item ids of one platform and marketplace: ASINs on Amazon, item ids on Walmart, TCINs on Target, SKUs on Best Buy. Send them in ids; every item the API returns, and every webhook entry, carries id, and the list carries platform and marketplace.

cURL

curl -X POST "https://api.scrapely.dev/v1/watchlists" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"Repricer","platform":"amazon","marketplace":"us","ids":["B0009KNLV6","0060094664","B074PVTPBW"],"every":"15m","webhook_url":"https://example.com/hooks/scrapely"}'

Python

import requests

r = requests.post(
    "https://api.scrapely.dev/v1/watchlists",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={"name":"Repricer","platform":"amazon","marketplace":"us","ids":["B0009KNLV6","0060094664","B074PVTPBW"],"every":"15m","webhook_url":"https://example.com/hooks/scrapely"},
)
wl = r.json()
print(wl["id"], wl["next_run_at"], wl["estimated_credits"])

Parameter

Required

Meaning

name

yes

Up to 100 characters.

ids

yes

1 to 50,000 item ids of platform (de-duplicated; ASINs are case-insensitive). A newline- or comma-separated string also works.

asins

no

Amazon only, legacy: the same as ids, from before watchlists covered other platforms. Send ids or asins, not both.

platform

no

Default amazon. Any platform whose products are live: amazon, walmart, target, bestbuy.

marketplace

no

Default us. Every run fetches the list in this marketplace; it must be live (a coming-soon one is a 501). The list, its snapshots and webhooks carry marketplace and currency.

every

yes

Cadence, start to start: 5m, 15m, 1h, 6h, 24h or any whole minutes/hours/days from 5 minutes to 7 days.

webhook_url

no

HTTPS endpoint for this list's events. Omitted: your account webhook (Dashboard → Webhooks).

refresh_every_runs

no

Amazon: every Nth run sweeps at standard instead of pulse. Default 8; 0 = never; at most 1,000.

Creating, editing and deleting lists is free; at most 100 lists per account. The first run starts right away.

What each run does

  1. New ASINs: full, once. Their first snapshot is the baseline; it is not reported as a change.

  2. Every other ASIN: a pulse sweep (new-offer count and lowest new price, the cheapest tier).

  3. Escalate to standard the ASINs whose offer count or lowest new price moved, plus the ones whose buy box cost more than the lowest new offer at their last fetch (pulse tracks the lowest offer, so it cannot see that buy box move: ~8% of a typical catalogue), plus any retry still owed.

  4. Every 8th run (refresh_every_runs) sweeps every ASIN at standard instead of pulse: it sees a buy-box seller change at the same price and refreshes offer listing ids. It replaces that run's pulse, so you never pay for both.

  5. Compare each new standard/full snapshot with the last one and report what moved.

Result

What happens

ok

New snapshot; any tracked field that moved is a change.

not_found

The item is delisted: reported once, kept on the list as delisted, never fetched again. Add it again to give it another chance.

partial / unavailable

Nothing changes and nothing is charged; the item is retried next run. Never a change, never a delisting.

Tracked fields: price, inStock, availabilityType, isFba, offerListingId, merchant, condition, newCount, newMinPrice, usedCount, usedMinPrice. merchant is compared by seller id; prices within half a cent are equal.

Billing

A run costs exactly what the /v1/products calls it makes would: ASINs go in calls of up to 10,000 (pulse) or 2,000 (standard, full), each call is rounded up once, and partial/unavailable ASINs are refunded. Per ASIN: pulse 0.25, standard 3, full 10 credits.

So a 10,000-ASIN list on 15m with ~0.1% movement costs about 2,500 credits per pulse run plus 3 per moved ASIN, and 30,000 on each refresh run. GET /v1/watchlists/{id} shows estimated_credits for your list.

When the balance runs out: A run reserves its sweep (and any full fetches) up front. If your balance cannot cover it, the run is skipped: nothing is fetched or charged, and watchlist.run.completed arrives with "status": "insufficient_credits". After 3 skips in a row the list is paused (paused_reason: "insufficient_credits"); top up and PATCH it to "status": "active". If only the escalations cannot be covered, they are deferred to the next run (stats.deferred).

Webhooks

Each run sends, in order: watchlist.product.changed (if anything changed), watchlist.product.delisted (if anything was delisted), then watchlist.run.completed. Changes and delistings are batched, at most 500 products per delivery (part of parts). Same envelope, signature and retries as job webhooks.

Body (Amazon)

{
  "id": "evt_1a2b3c4d5e6f7a8b9c0d",
  "type": "watchlist.product.changed",
  "created_at": "2026-10-16T09:15:04.120Z",
  "data": {
    "watchlist_id": "wl_3f1c2a9e8d4b4c61",
    "watchlist_name": "Repricer",
    "platform": "amazon",
    "marketplace": "us",
    "currency": "USD",
    "run_id": "wlrun_5f0c8a1b2d3e4f56",
    "run_number": 42,
    "part": 1,
    "parts": 1,
    "changes": [
      {
        "id": "0060094664",
        "asin": "0060094664",
        "changed_at": "2026-10-16T09:15:02.871Z",
        "tier": "standard",
        "changes": {
          "price": {
            "before": 4.74,
            "after": 4.29
          },
          "newMinPrice": {
            "before": 3.45,
            "after": 3.2
          },
          "newCount": {
            "before": 9,
            "after": 10
          }
        },
        "snapshot": {
          "price": 4.29,
          "inStock": true,
          "availabilityType": "IN_STOCK",
          "isFba": true,
          "fulfillerName": "Amazon.com",
          "offerListingId": "kJfX%2B…",
          "merchant": {
            "id": "ATVPDKIKX0DER",
            "name": "Amazon.com"
          },
          "condition": "NEW",
          "newCount": 10,
          "newMinPrice": 3.2,
          "usedCount": 31,
          "usedMinPrice": 1.02,
          "title": "Example Book"
        }
      }
    ]
  }
}

data (Walmart)

{
  "watchlist_id": "wl_9a8b7c6d5e4f3a21",
  "watchlist_name": "Walmart MAP",
  "platform": "walmart",
  "marketplace": "us",
  "currency": "USD",
  "run_id": "wlrun_0d1e2f3a4b5c6d7e",
  "run_number": 7,
  "part": 1,
  "parts": 1,
  "changes": [
    {
      "id": "5129928603",
      "changed_at": "2026-10-16T09:15:02.871Z",
      "tier": "standard",
      "changes": {
        "price": {
          "before": 24.97,
          "after": 21.48
        }
      },
      "snapshot": {
        "price": 21.48,
        "…": "…"
      }
    }
  ]
}

id is the item id on the list's platform. Amazon entries also carry asin, the same value, for receivers written before watchlists covered other platforms; Walmart, Target and Best Buy entries never have an asin, so read id. changes holds only the fields that moved, each as { "before", "after" }; snapshot is every tracked field after the change. Field names match /v1/products results.

{
  "type": "watchlist.product.delisted",
  "data": {
    "watchlist_id": "wl_3f1c2a9e8d4b4c61",
    "watchlist_name": "Repricer",
    "platform": "amazon",
    "marketplace": "us",
    "currency": "USD",
    "run_id": "wlrun_5f0c8a1b2d3e4f56",
    "run_number": 42,
    "part": 1,
    "parts": 1,
    "products": [
      {
        "id": "B000000DED",
        "asin": "B000000DED",
        "delisted_at": "2026-10-16T09:15:01.002Z",
        "detected_by": "pulse",
        "last_snapshot": {
          "price": 12.99,
          "inStock": true,
          "…": "…"
        }
      }
    ]
  }
}
{
  "type": "watchlist.run.completed",
  "data": {
    "id": "wlrun_5f0c8a1b2d3e4f56",
    "object": "watchlist_run",
    "watchlist_id": "wl_3f1c2a9e8d4b4c61",
    "run_number": 42,
    "trigger": "schedule",
    "kind": "pulse",
    "status": "completed",
    "credits": 2516,
    "stats": {
      "tracked": 10000,
      "sweep_tier": "pulse",
      "calls": {
        "pulse": {
          "requested": 10000,
          "ok": 9978,
          "partial": 0,
          "not_found": 1,
          "unavailable": 21,
          "credits": 2495
        },
        "standard": {
          "requested": 7,
          "ok": 7,
          "partial": 0,
          "not_found": 0,
          "unavailable": 0,
          "credits": 21
        },
        "full": {
          "requested": 0,
          "ok": 0,
          "partial": 0,
          "not_found": 0,
          "unavailable": 0,
          "credits": 0
        }
      },
      "escalated": {
        "changed": 4,
        "buybox_above_lowest": 3,
        "pending": 0
      },
      "deferred": 0,
      "changed": 2,
      "delisted": 1,
      "retry_next_run": 21
    },
    "error": null,
    "started_at": "2026-10-16T09:15:00.004Z",
    "finished_at": "2026-10-16T09:15:04.101Z",
    "watchlist": {
      "id": "wl_3f1c2a9e8d4b4c61",
      "name": "Repricer",
      "platform": "amazon",
      "marketplace": "us",
      "status": "active",
      "paused_reason": null,
      "next_run_at": "2026-10-16T09:30:00.004Z"
    }
  }
}

Missed a delivery?: GET /v1/watchlists/{id}/items?changed_since=<ISO time> returns every item that changed or was delisted since then, with its current snapshot and last_change.

Endpoints

Method

Path

Does

POST

/v1/watchlists

Create (supports Idempotency-Key).

GET

/v1/watchlists

List yours.

GET

/v1/watchlists/{id}

One list, with item_count, items_by_state and estimated_credits (asin_count / asins_by_state are deprecated aliases with the same values).

PATCH

/v1/watchlists/{id}

name, every, webhook_url (null clears), refresh_every_runs, status (active / paused).

DELETE

/v1/watchlists/{id}

Delete with its items and runs.

POST

/v1/watchlists/{id}/items

{ "add": […], "remove": […] }: item ids of the list's platform. Added items get a baseline fetch next run (Amazon: full). Answers item_count.

POST

/v1/watchlists/{id}/asins

The legacy path of the call above: same body and behaviour.

POST

/v1/watchlists/{id}/run

Run now (202). Replaces the next scheduled run; 409 if one is in progress.

GET

/v1/watchlists/{id}/runs

Run history with per-tier counts and credits.

GET

/v1/watchlists/{id}/items

Items with id (plus asin on Amazon) and their last snapshot. ?changed_since=, ?state=, ?limit= (≤ 1,000), ?starting_after=.

Every schema is in the API reference.

Updated