CVS Scraper - Products, Prices, Ratings & Availability avatar

CVS Scraper - Products, Prices, Ratings & Availability

Pricing

from $1.60 / 1,000 product records

Go to Apify Store
CVS Scraper - Products, Prices, Ratings & Availability

CVS Scraper - Products, Prices, Ratings & Availability

Scrape CVS.com pharmacy, beauty, health and personal care products by keyword search. Extract product names, brands, prices, descriptions, ratings, customer reviews and availability. Collect structured product data for price comparison, competitor analysis, market research and stock monitoring.

Pricing

from $1.60 / 1,000 product records

Rating

0.0

(0)

Developer

Abot API

Abot API

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

2

Monthly active users

8 days ago

Last modified

Categories

Security

Limited permissions

Share

CVS Product Scraper

Scrape CVS.com product listings: keyword search, product details, prices, ratings and availability. CVS Pharmacy is the largest US drugstore chain (health, beauty, personal care, household essentials), and this actor turns its catalog into clean structured rows for price monitoring, assortment analytics and competitor research.

Why This Scraper?

  • Two ways in. Point it at keyword searches, or paste storefront links - search pages and product pages both walk with the same grammar.
  • Retail-ready product records. Every row carries title, brand, price with was-price and per-unit price, rating, review count, sponsored flag and badges; detail enrichment adds the description, availability and image gallery straight from the product page. Individual review texts are not collected; each row carries the rating and the review count.
  • Scheduled-run friendly. Incremental mode classifies every product NEW / UPDATED / UNCHANGED / EXPIRED across runs, so a daily monitor pushes only what moved, and resume mode continues an interrupted walk exactly where it stopped.
  • Fast and light. A 10-product run with details finishes in about a minute on a small memory size. The storefront geo-blocks non-US lines, so every connection exits in the US (automatic Apify proxy selection by default), and a denied exit is swapped for a fresh one automatically.
  • Connector-ready. Push records into Notion, Sheets or any MCP-capable app as they land.
  • Honest runs. Refused pages rotate and retry; a fully closed window fails loudly with a connection-problem message instead of shipping an empty success.

Use Cases

  • Who: pricing teams tracking CVS promos on health-and-beauty assortments - price, was-price and per-unit price land per row, updated per scheduled run.
  • Who: brands auditing their drugstore shelf - sponsored flags, badges, ratings and review velocity per product.
  • Who: analysts building retail datasets - walk any keyword search with the same grammar and export straight to CSV/Excel or a warehouse.

Data You Get

Sample shape: values are illustrative placeholders, not from a live record.

FieldExample
titleL.A. Girl Shimmer Spray, Gold
brandL.A. Girl
price / wasPrice7.99 / 12.99
pricePerUnit$1.62/oz.
rating4.6
reviewsCount21
sponsoredfalse
badges["Best Seller"]
availabilityInStock
descriptionFull product description text
imageUrlsFull image gallery
skuId186492
currencyUSD
availabilityInStock
position3 (grid slot)
scrapedAt2026-10-28T12:00:00Z
changeType / changedFieldsNEW / UPDATED / [price] (incremental mode)

How to Use

Run it from your code

Python:

from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("abotapi/cvs-product-scraper").call(run_input={
"mode": "search",
"searchTerms": ["shampoo"],
"maxItems": 50,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["title"], item["price"])

JavaScript:

import { Actor } from 'apify';
const run = await Actor.call('abotapi/cvs-product-scraper', {
mode: 'urls',
urls: ['https://www.cvs.com/search?q=shampoo'],
maxItems: 50,
});
const { default: dataset } = await Actor.openDataset(run.defaultDatasetId);
await dataset.forEach((item) => console.log(item.title, item.price));

Search by keyword:

{
"mode": "search",
"searchTerms": ["shampoo", "vitamin c"],
"maxItems": 50
}

Running URL mode with the urls field untouched scrapes one example of each supported shape: one product page first, then the shampoo search page (the default Max items fills with the search results after the product row). Search pages work in both forms the site produces (search?q=shampoo and search?searchTerm=shampoo). Category, brand and other browse pages are not supported yet; they are skipped with a warning naming the supported shapes, and a run with no usable link fails loudly. If an API call sends only searchTerms (your own terms) and leaves urls at the shipped examples, the run switches to search mode for your terms and does not scrape the example links.

Walk a search page:

{
"mode": "urls",
"urls": ["https://www.cvs.com/search?q=shampoo"],
"maxItems": 100
}

One product, details on:

{
"mode": "urls",
"urls": ["https://www.cvs.com/shop/l-a-girl-shimmer-spray-gold-prodid-186492"],
"maxItems": 1
}

Scheduled price monitor:

{
"mode": "search",
"searchTerms": ["shampoo"],
"maxItems": 200,
"fetchDetails": false,
"incrementalMode": true
}

Tips

  • The storefront re-ranks results between runs - incremental mode treats a moving position as noise and never re-bills it.
  • Scheduled incremental runs keep the walk cheap: unchanged rows are suppressed before any product page is re-read and bill nothing.
  • Sale prices arrive as price + was-price; per-unit prices ride the storefront's own unit-price text.

Input Parameters

ParameterTypeDefaultDescription
modeselecturlssearch walks keyword searches; urls walks pasted storefront links.
searchTermsstring listshampooKeyword searches to walk (search mode).
urlsURL list2 example linksSearch pages and product pages (links mode).
maxItemsinteger20Maximum records per run. 0 means no limit.
fetchDetailscheckboxtrueRead each product's detail page (description, availability, gallery).
incrementalModecheckboxfalseClassify rows NEW/UPDATED/UNCHANGED/EXPIRED across scheduled runs.
emitUnchangedcheckboxfalseAlso emit rows whose tracked fields did not change.
emitExpiredcheckboxfalseEmit EXPIRED rows for products that left a fully-scanned scope.
proxyConfigurationproxyAutomatic (US-pinned)Automatic Apify proxy selection, pinned to US exits because the storefront geo-blocks non-US lines. A residential pick is honoured as chosen and uses more of your residential proxy traffic; you can also supply your own US proxy URLs.
resumeFromRunIdstring(hidden)Internal: continues an interrupted run; collected records are skipped.
stateKeystring(hidden)Internal: names the incremental baseline; empty derives it from the scope.
ignoreFieldsForChangesstring list(hidden)Internal: extra fields excluded from the incremental change fingerprint.
mcpConnectorsstring list(hidden)Internal: connector ids that receive records as they push.
notionParentPageUrlstring(hidden)Internal: parent page for the Notion connector.
maxNotifyListingsinteger50 (hidden)Internal: cap on records sent to connectors per run.

All fields are also accepted through the API exactly as documented, including the hidden internal ones.

Output Example

Sample shape: values are illustrative placeholders, not from a live record.

{
"recordId": "cvs:186492",
"productId": "186492",
"title": "L.A. Girl Shimmer Spray, Gold",
"brand": "L.A. Girl",
"price": 7.99,
"wasPrice": 12.99,
"currency": "USD",
"rating": 4.6,
"reviewsCount": 21,
"availability": "InStock",
"url": "https://www.cvs.com/shop/l-a-girl-shimmer-spray-gold-prodid-186492"
}

Product rows with the detail enrichment on, from a live search walk.

Plan Requirement

This actor needs an Apify paid plan. The storefront geo-blocks non-US lines, so every page is read over US exits, chosen by the automatic Apify proxy selection by default. The Apify Free plan does not include Apify Proxy, and residential proxies are not included in it either.

  • If the selected connection is unavailable, the run logs: "The selected connection is unavailable to this run. Apify free plans do not include residential proxies. Continuing on a backup connection; some pages may still be unavailable." Pages that the backup connection serves are noted with "Served over the backup connection (the default connection was unavailable)."
  • If nothing can be read, the run fails with one of these messages: "The proxy connection was refused for this account, so no page could be read. Your Apify plan does not include proxy access; a paid plan is required." or "No results: residential proxies are unavailable on this run (Apify free plans do not include them), and the backup connection could not load the requested CVS pages." or "Could not load any results page (the requests failed), so nothing was collected. This is a connection problem, not an empty search."
  • You can supply your own US proxy URLs in the Proxy configuration field instead.

FAQ

The actor collects publicly available product data from the storefront's own pages, at a polite pace, without logging into any account. Review your use case and the site's terms before running at scale.

Why does a run fail with a connection problem?

The storefront denies individual proxy exits intermittently. The actor swaps a denied exit for a fresh one (at most five tries per page) and stops early when exit after exit is refused, so a closed window fails within minutes instead of running to the time limit; retry later, the same input usually succeeds on the next run.

How much does a run cost?

You pay the actor's per-event pricing (see the Pricing tab) plus your Apify plan's proxy usage. The billed events are the actor start, each product record pushed to the dataset, and the detail enrichment, charged once per product whose detail page was read and delivered. Unchanged rows in incremental mode, card-only rows (details off) and rows whose detail page could not be read bill no enrichment fee.

Can I track price changes daily?

Yes - schedule the run with incrementalMode on and the same inputs. Only NEW and UPDATED rows push, each with changedFields naming what moved.

Send results into your apps (MCP connectors)

Connect the actor to Notion, Google Sheets and other MCP-capable apps from the Apify Console: records this run pushed are delivered as they land, capped per run. Connector settings ship hidden in the input and need no attention unless you use connectors.

🔗 More scrapers you might like

Pair this actor with these related scrapers from the same team:

🛒 Drogasil Brazil Pharmacy, Health & Beauty Scraper
Scrape Drogasil (drogasil.com.br) medicines, health and beauty products by keyword...
🧩 JB Hi-Fi AU Scraper
Scrape JB Hi-Fi Australia (jbhifi.com.au) products from the public storefront. Extract...
🧩 Falabella Scraper
Scrape Falabella, Latin America's leading retail marketplace: products with local prices...
🛒 ROSSMANN.de Scraper
Scrape ROSSMANN (rossmann.de) drugstore products: current + original strike-through price...
🧩 Walmart Scraper
Scrape Walmart.com by keyword, URL, or item ID. Extract prices, was-prices, sellers...
🛒 dm.de
Scrape dm-drogerie markt (dm.de) products: current + strike-through Ausverkauf price with...

👉 Browse all abotapi scrapers

💬 Support

  • 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
  • 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
  • ⭐ Enjoying it? A quick review on the actor page helps other users find it.