# CVS Scraper: Prices, ExtraCare Deals, Coupons & Store Stock (`vant47/cvs-pharmacy-scraper`) Actor

Get CVS Pharmacy advertised online/pickup prices and store stock by ZIP or store: sale prices, ExtraCare deals, ExtraBucks rewards, coupon dates, unit prices, UPC, size and aisle. Search, categories or product links. About $0.00249 per product. No login.

- **URL**: https://apify.com/vant47/cvs-pharmacy-scraper.md
- **Developed by:** [fortbrand](https://apify.com/vant47) (community)
- **Categories:** E-commerce, Automation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.99 / 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.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## CVS Scraper: Prices, ExtraCare Deals, Coupons & Store Stock

For retail price analysts, brands and comparison services monitoring CVS advertised prices, promotions and availability at selected US stores. Collect regular and sale prices, unit prices, explicit membership prices, UPCs, local inventory, pickup availability, aisles, ExtraBucks rewards and manufacturer coupon terms with their published dates.

About **$0.00249 per delivered product variant at one store**. A 20-observation run costs about **$0.05**, plus a minimum **$0.00005 start charge**. No CVS login, retailer API account or exported cookies are required.

### Understand prices and stock

The Actor requests CVS advertised online/pickup prices with the selected store number. The price channel is recorded as `ADVERTISED_ONLINE_PICKUP`. Keep that channel when comparing CVS observations with another retailer or your own store audit.

`price` is the supplied advertised sale price, or list price when there is no separate sale. `regularPrice` retains the source list price. `memberPrice` contains a positive, explicit CarePass/ExtraCare Plus price when supplied. Zero markers used by CVS for an unavailable membership price become null.

Coupons and ExtraBucks stay separate. An offer requiring a qualifying purchase of $20 to earn $10 in ExtraBucks does not turn a $4.99 toothpaste into a negative-priced item. Manufacturer offers can require multiple products, clipping, membership or a particular channel. Each offer retains its own conditions and dates.

`stockLevel` and `inStoreStock` contain the selected store's inventory quantity. `pickupStockLevel` is the separate pickup quantity. Chain-wide shipping inventory appears in `shippingStockLevel` and never replaces the local count. Aisle and shelf information also belongs to the selected store. When a listing omits stock for another size or pack, the Actor checks that exact variant separately. A capped pickup promise is kept apart from store inventory.

Signed-out cloud checks on 4 November 2026 found SKU `832677` with zero store units at CVS `2671` and six at CVS `2111`, with different aisle information. Its advertised price was $4.99, unit price $1.19/oz., and explicit UPC `827854025610`. These are dated observations, not promises about later availability.

### What you get

| Field | Meaning |
| --- | --- |
| `verdict` | Readable advertised price, local stock and store summary. |
| `retailer`, `country`, `currency` | CVS Pharmacy, US and USD. |
| `storeId`, `storeName`, `location` | Verified front store, address name and requested ZIP. |
| `productId`, `sku`, `gtin` | CVS product, exact variant and explicit source UPC. |
| `title`, `brand`, `size` | Published name, brand and package-size label. |
| `price`, `regularPrice`, `onSale` | Advertised amount, list price and numerical sale flag. |
| `promoText`, `promoType` | Promotion text and source offer type. |
| `promoStart`, `promoEnd` | Main price-promotion dates when supplied. |
| `memberPrice`, `memberProgram` | Explicit positive membership price and program. |
| `unitPrice`, `unitOfMeasure` | Numeric price per source unit, such as dollars per oz. |
| `inStock`, `stockLevel` | Selected-store availability and inventory quantity. |
| `rating`, `reviewCount` | Aggregate rating and review count. |
| `category`, `url`, `imageUrl` | Verified category context, product and image links. |
| `scrapedAt` | UTC observation timestamp. |

| Field | Meaning |
| --- | --- |
| `storeNumber`, `storeAddress`, `storeDistance` | Store number, public address and source distance. |
| `inStoreStock`, `pickupAvailable`, `pickupStockLevel` | Local inventory and separate pickup signals. |
| `shippingInStock`, `shippingStockLevel` | Chain-wide shipping availability. |
| `extraBucksText`, `couponText` | Readable reward and coupon conditions. |
| `offers` | Offer IDs, conditions, individual dates, clipping flags and channel information. |
| `isDeal`, `digitalCouponAvailable` | Combined deal flag and published coupon presence. |
| `priceScope`, `physicalShelfPrice` | Price channel and a separate null shelf-price field. |
| `priceStoreIdRequested`, `priceStoreIdReturned`, `priceZoneId` | Requested pricing store and returned pricing identifiers. |
| `unitPriceText` | Original unit-price label alongside its numeric amount. |
| `aisle`, `aisleInfo` | Aisle, section, shelf number and capacity. |
| `requestedLocations` | ZIP selections that resolved to one store. |
| `details` | Optional ingredients, directions, attributes and image references. |
| `sourceUrl`, `sourceJobId` | Public request route and an export audit identifier. |

The overview puts title, price, regular price, promotion, membership price, unit price, stock and store together. The deals view emphasizes offer fields. The `DEALS` JSON output contains only `isDeal: true` rows; large exports use smaller parts.

Every run saves `SUMMARY` with products, stores, deals, source outcomes, duplicate exclusions, free failures and spending-limit status. Product rows use `recordType: "PRODUCT"`. Free explanatory rows use `recordType: "NOTE"`.

Validation on 4 November 2026 covered 34 varied inputs and two 1,100-observation runs without duplicate store/SKU rows. The production prefill delivered 20 observations in 30.0 seconds. A real reboot finished with three unique rows and three product charges.

### Start in three steps

1. Enter ZIP codes or exact CVS store numbers.
2. Supply terms, category/product links or CVS product numbers and set your limits.
3. Run, check stores and coverage in SUMMARY, then export JSON, CSV or Excel.

The prefill searches toothpaste near ZIP `10001`, capped at 20 observations. Exact store numbers keep a repeated monitor on the same location.

### Inputs and examples

| Input | Example and behavior |
| --- | --- |
| `searchTerms` | `["toothpaste", "shampoo"]`, searched at each store. |
| `startUrls` | Optional CVS category, search, product or deals links. |
| `productIds` | `["832677", "789787"]`, CVS identifiers rather than UPCs. |
| `locations` | `["10001", "90012"]`, nearest front store per ZIP. ZIP+4 is accepted. |
| `storeIds` | `["2671", "2111"]`, exact stores, taking precedence over ZIPs. |
| `maxItemsPerSearch` | New observations per source/store; default 20, maximum 1,000. |
| `maxItems` | Global observation cap; default 20, maximum 10,000. |
| `dealsOnly` | Keep sales, explicit membership prices or published coupon/reward offers. |
| `includeDetails` | Add public ingredients, directions, attributes and image references. |
| `proxyConfiguration` | Automatic US routing or your own configuration. |

Empty ZIP and store arrays use the website default front store, recorded in SUMMARY. Pharmacy counters inside another retailer are excluded from nearest-front-store selection.

Two searches at two stores can produce up to 80 observations with a per-source cap of 20:

```json
{
  "searchTerms": ["toothpaste", "shampoo"],
  "locations": ["10001", "90012"],
  "maxItemsPerSearch": 20,
  "maxItems": 80
}
```

For a repeatable watchlist:

```json
{
  "productIds": ["832677", "789787"],
  "storeIds": ["2671", "2111"],
  "maxItemsPerSearch": 20,
  "maxItems": 10
}
```

A product with several variants can produce several rows. The combination `storeId + sku` identifies a charged observation, including when CVS uses another parent-product alias. A product found again through another source at that store is delivered and charged once within the run.

A category example:

```json
{
  "startUrls": ["https://www.cvs.com/shop/personal-care/oral-care/toothpaste"],
  "locations": ["10001"],
  "maxItems": 20
}
```

For published offers, add `"dealsOnly": true`. Both price reductions and conditional rewards can qualify; inspect `offers` and `onSale` according to your analysis. Pagination follows source totals and your limits. Paid placements are excluded. A cap is a maximum, rather than a guarantee that a store carries that many results.

### Output example

Selected fields from the signed-out 4 November 2026 source checks:

```json
{
  "retailer": "CVS Pharmacy",
  "country": "US",
  "currency": "USD",
  "storeId": "2671",
  "productId": "832677",
  "sku": "832677",
  "gtin": "827854025610",
  "price": 4.99,
  "regularPrice": 4.99,
  "unitPrice": 1.19,
  "unitOfMeasure": "oz.",
  "inStock": false,
  "stockLevel": 0,
  "priceScope": "ADVERTISED_ONLINE_PICKUP",
  "physicalShelfPrice": null
}
```

That check also returned a manufacturer offer with a two-item requirement and a published end of `2026-11-29T23:59:59-05:00`, plus an ExtraBucks offer ending `2026-11-07T23:59:59-05:00`. A separate manufacturer redemption-end timestamp is retained alongside the published end, rather than silently replacing one date.

### Pricing and spending limits

One charged product is one delivered variant at one store. The same SKU at two stores is two observations. Overlapping sources, retries, duplicates, notes and empty selections have no product charge. Optional details have no extra product fee, and there is no additional default dataset-row charge.

| Observations | Base | Bronze | Silver | Gold |
| --- | --- | --- | --- | --- |
| 20 | $0.0498 | $0.04731 | $0.04482 | $0.03984 |
| 100 | $0.249 | $0.23655 | $0.2241 | $0.1992 |
| 1,000 | $2.49 | $2.3655 | $2.241 | $1.992 |

Add the minimum $0.00005 start charge. Base price is $0.00249 per observation, Bronze $0.0023655, Silver $0.002241 and Gold $0.001992. Bigger jobs are controlled by your product cap and Apify maximum total charge.

The Actor checks remaining charge capacity before further delivery. At the limit, it stops cleanly and records the decision in SUMMARY. A saved product is the billing unit; failed requests and notes do not become paid empty results.

### Monitor prices every day

Save a task with stable store numbers and a watchlist or selected searches/categories. In Apify Console, create a schedule, choose the task and timezone, and connect the completed-run output to your database or workflow.

Join snapshots using `retailer + storeId + sku`, with `scrapedAt` as observation time. Preserve the price channel, membership fields and offer conditions. Store UPCs as text in spreadsheets so leading zeros survive.

Each scheduled run collects a fresh observation and charges for its delivered rows again. Compare snapshots in your database to identify price changes, inventory movements and promotion expiries. Check `freeFailures` and source outcomes before treating a snapshot as complete.

The retail family field names let you combine CVS with other store datasets. Preserve each source's price channel and membership conditions when comparing baskets.

### API and integrations

Use your own Apify token:

```bash
curl -X POST \
  'https://api.apify.com/v2/acts/vant47~cvs-pharmacy-scraper/runs' \
  -H 'Authorization: Bearer YOUR_APIFY_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"searchTerms":["toothpaste"],"locations":["10001"],"maxItems":20}'
```

```python
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("vant47/cvs-pharmacy-scraper").call(
    run_input={"productIds": ["832677"], "storeIds": ["2671", "2111"], "maxItems": 10}
)
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())
```

```javascript
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('vant47/cvs-pharmacy-scraper').call({
  searchTerms: ['shampoo'], locations: ['10001'], maxItems: 20
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Make, Zapier, n8n, Sheets and webhooks can read the dataset and SUMMARY. Filter `recordType` before treating rows as products.

### Use with AI agents (MCP)

Connect to `https://mcp.apify.com` and request CVS advertised prices, UPCs, unit prices, ExtraCare offers and stock by ZIP or store. Supply terms or product numbers and a cap. Ask the agent to inspect SUMMARY, preserve nulls, retain offer conditions and dates, and keep `priceScope` in comparisons.

### Reliability

Public calls run sequentially with ordinary pacing. Direct cloud datacenter HTTP is tried first. Refused calls retry on fresh US datacenter proxy sessions, honor Retry-After and can fall back to US residential routing. Default operation uses public data calls rather than a browser.

Saved page progress and the dataset ledger survive reboots. Delivery and charging are serial. Recovery reconciles saved rows, acknowledged charges and platform counts before further billing. Ambiguous recovery stops additional charging.

### Limits

- Prices are advertised online/pickup amounts requested for a verified store. Physical shelf prices, checkout taxes and delivery fees are not supplied; shelf prices can differ.
- Store quantities are CVS online inventory signals, not a shelf audit or reservation. Availability can change after collection.
- Prescription prices/stock, account offers, clipped-coupon state and personalized checkout eligibility are outside coverage.
- Coupons may require clipping, membership, quantities or a purchase channel. Raw timestamps without an offset retain their source local-time meaning; separate expiry fields can disagree.
- Not every variant supplies every field. Missing promotion starts, membership prices, identifiers and optional details remain null.
- Coverage follows the public catalog. Image-only weekly-ad panels are not extracted. The Actor does not sign in or solve CAPTCHAs.
- Source outages or refusals can prevent complete output. Failures are free and visible in SUMMARY.

### FAQ

**Can I choose an exact store?** Yes, use `storeIds` or use `locations` for nearest-front-store selection.

**Does a coupon lower price?** Conditions and reward values remain separate in `offers`, `couponText` and `extraBucksText`.

**Does includeDetails add a fee?** It adds product-page requests without another product fee.

**What happens with bad input?** Parseable invalid selections produce free notes and a summary. Apify can reject schema-invalid input before a run starts; neither path adds a product charge.

# Actor input Schema

## `searchTerms` (type: `array`):

Search the CVS catalog, such as toothpaste, shampoo or vitamins. Each term runs at every selected store. Leave empty when using only links or product IDs.

## `startUrls` (type: `array`):

Optional CVS category, search, deals or product links. Both www.cvs.com and es.cvs.com links are accepted. Store selection comes from locations or storeIds.

## `productIds` (type: `array`):

CVS product numbers from product links, such as 832677. These are retailer identifiers, not UPC barcodes. Multi-variant products can produce several SKU rows.

## `locations` (type: `array`):

Use US ZIP codes, such as 10001 or 90012. Chooses the nearest CVS front store per ZIP. Empty uses the website default store.

## `storeIds` (type: `array`):

Optional exact numeric CVS store numbers, such as 2671 or 2111. These take precedence over ZIP codes; every store is verified before product requests.

## `maxItemsPerSearch` (type: `integer`):

Maximum new product/variant rows from one term, category, product selection or deals source at one store. Duplicates are free.

## `maxItems` (type: `integer`):

Global limit across all stores and sources. About $0.00249 per product/store row: 20 observations cost about $0.05 plus the small start charge.

## `dealsOnly` (type: `boolean`):

Keep advertised sales, explicit membership prices or currently published coupon/ExtraBucks offers. Coupon conditions are retained and are not subtracted from the product price.

## `includeDetails` (type: `boolean`):

Adds public product-page ingredients, directions, attributes and image references. Slower, with no extra product fee. Missing optional details stay null.

## `proxyConfiguration` (type: `object`):

Default: direct cloud datacenter HTTP first, US datacenter proxy retries next, and US residential only after failures. Custom proxy settings are supported.

## Actor input object example

```json
{
  "searchTerms": [
    "toothpaste"
  ],
  "locations": [
    "10001"
  ],
  "maxItemsPerSearch": 20,
  "maxItems": 20,
  "dealsOnly": false,
  "includeDetails": false,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `dealsView` (type: `string`):

No description

## `dealsOnly` (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 = {
    "searchTerms": [
        "toothpaste"
    ],
    "locations": [
        "10001"
    ],
    "maxItemsPerSearch": 20,
    "maxItems": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("vant47/cvs-pharmacy-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 = {
    "searchTerms": ["toothpaste"],
    "locations": ["10001"],
    "maxItemsPerSearch": 20,
    "maxItems": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("vant47/cvs-pharmacy-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 '{
  "searchTerms": [
    "toothpaste"
  ],
  "locations": [
    "10001"
  ],
  "maxItemsPerSearch": 20,
  "maxItems": 20
}' |
apify call vant47/cvs-pharmacy-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vant47/cvs-pharmacy-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/OTsKUZFBk93yOztwM/builds/ezPbNaoLvSmFynHOM/openapi.json
