# iHerb Scraper: Search, Products, Reviews & Monitor (`vaunted/iherb-scraper`) Actor

The ultimate iHerb API. Supports 4 modes: Search, Product Details, Reviews, and Real-Time Price Monitoring. Extract ingredients, nutrition, and stock status for 188 countries. No monthly fees - pay only for what you scrape.

- **URL**: https://apify.com/vaunted/iherb-scraper.md
- **Developed by:** [Sage](https://apify.com/vaunted) (community)
- **Categories:** E-commerce, Lead generation, Automation
- **Features:** Limited permissions
- **Stats:** 346 total users, 44 monthly users, 99.6% runs succeeded, 5 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 items

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

**Extract iHerb product data, customer reviews, and price changes** from a single Actor. Search any category or keyword, pull 50+ fields per product including ingredients, nutritional facts and UPC codes, collect customer reviews, or monitor prices and stock across 188 countries.

No browser rendering, so runs finish in seconds rather than minutes. Pay only for the results you keep.

### What can iHerb Scraper do?

- 🔍 **Search** - scrape products from any iHerb category, search URL, or keyword
- 📦 **Product details** - 50+ fields per product: price, stock, ingredients, nutrition facts, UPC, rankings, images
- ⭐ **Reviews** - collect customer reviews with ratings, verified-buyer flags and reviewer location
- 🔔 **Price monitoring** - track price drops and restocks, with optional webhook alerts
- 🌍 **188 countries and 84 currencies** with localized pricing
- ⚡ **API-based extraction** - no headless browser, so it's fast and cheap to run

Running on Apify means you also get scheduling, run monitoring, a REST API, webhook and integration support (Zapier, Make, Slack, Google Drive), proxy rotation, and dataset exports to JSON, CSV or Excel - without building any of it yourself.

#### Smart auto-detection

Don't want to pick a mode? Paste **any** iHerb URL - product, category, or search - into `startUrls` and the Actor detects the page type and scrapes it accordingly.

### What data can you extract from iHerb?

| Mode | What you get |
| --- | --- |
| **Search** | Product ID, URL, title, brand, price, list price, rating, review count, stock status, image |
| **Details** | Everything above plus ingredients, nutritional facts, suggested use, warnings, allergen info, UPC, part number, serving size, package quantity, **expiry / best-by date**, physical dimensions, **units left when stock is low**, category rankings, all images |
| **Reviews** | Review ID, rating, title, text, reviewer name and country, verified-buyer flag, separate helpful up/down counts, review language, reward flag, attached images, date |
| **Monitor** | Change type, previous vs current price, percentage change, previous vs current stock status, timestamp |

### How much does it cost to scrape iHerb?

This Actor uses Apify's **pay-per-result** pricing: **$0.50 per 1,000 results**, or $0.0005 per item. You pay for the rows you actually receive - failed or unavailable products are never billed.

Platform usage (compute, proxies) is **free** on this Actor; you are not charged separately for it.

A few worked examples:

| What you scrape | Results | Cost |
| --- | --- | --- |
| 100 products from a category | 100 | $0.05 |
| 1,000 products | 1,000 | $0.50 |
| 500 reviews across 5 products | 500 | $0.25 |
| Daily price check on 200 products | 200/day | $0.10/day |

Apify's free plan includes monthly usage credits, so you can trial the Actor at no cost before committing.

> Set **Maximum Items** in the input to cap your spend on any run. You can also set a maximum cost per run in the Apify Console.

### How do I use the iHerb Scraper?

1. Click **Try for free** and sign in to Apify.
2. Paste an iHerb URL into **Start URLs**, or type a keyword into **Search Keywords**.
3. Pick a **Mode** - or leave it on `auto` to let the Actor decide.
4. Set **Maximum Items** to control how much you scrape.
5. Click **Start** and download results as JSON, CSV or Excel.

#### Scrape iHerb search results and categories

```json
{
    "mode": "search",
    "startUrls": [{ "url": "https://www.iherb.com/c/vitamins" }],
    "maxItems": 100
}
```

#### Extract full iHerb product details

```json
{
    "mode": "details",
    "startUrls": [{ "url": "https://www.iherb.com/pr/NOW-Foods-Vitamin-D-3/10421" }]
}
```

#### Scrape iHerb customer reviews

```json
{
    "mode": "reviews",
    "startUrls": [{ "url": "https://www.iherb.com/pr/NOW-Foods-Vitamin-D-3/10421" }],
    "maxReviewsPerProduct": 100
}
```

#### Monitor iHerb prices and stock

```json
{
    "mode": "monitor",
    "startUrls": [{ "url": "https://www.iherb.com/pr/Product-Name/12345" }],
    "webhookUrl": "https://your-webhook.com/alerts"
}
```

Monitor mode persists its baseline between runs, so there are no files to upload. Schedule it daily and it diffs against the previous check automatically. Baselines are kept separately for each country and currency, so a US task and a Kazakhstan task never compare against each other's prices.

Each row's `changeType` is one of `price_drop`, `price_increase`, `out_of_stock`, `back_in_stock`, `removed` (iHerb no longer serves the product in this market), `new_item` (first time seen) or `no_change`. A product running low (`limited`) still counts as in stock. Monitoring a category checks up to `maxItems` products from it, so set `maxItems` to `0` to watch a whole category.

Baselines saved before they were split by market are taken over by the first market that runs after the upgrade. If you run monitors for several markets, the first run of the others starts fresh and reports products as `new_item`. A carried-over price more than 2.5x away from the current one is treated as coming from another currency, and that product is reported as `new_item` rather than as a price change.

### Input

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `mode` | string | `auto` | Operation mode: `auto`, `search`, `details`, `reviews`, `monitor` |
| `startUrls` | array | required | Product, category, or search URLs to scrape |
| `searchKeywords` | string | - | Search keywords (alternative to URLs) |
| `countryCode` | string | `US` | Country code for localized pricing |
| `currencyCode` | string | `USD` | Currency for prices |
| `maxItems` | integer | 100 | Maximum dataset items to produce (0 = unlimited) |
| `maxReviewsPerProduct` | integer | 100 | Maximum reviews per product in Reviews mode |
| `reviewLanguage` | string | `all` | Only return reviews written in this language, e.g. `ru-RU`. Default is every language |
| `includeUpc` | boolean | true | Fetch UPC, SKU and refreshed ratings in Details mode. Costs one extra request per product - turn off to roughly halve requests |
| `webhookUrl` | string | - | Webhook URL for price monitoring alerts |
| `useSitemapForBulk` | boolean | false | Deprecated and ignored. Category extraction is already unlimited - see below |
| `filters.minPrice` | number | - | Minimum price filter |
| `filters.maxPrice` | number | - | Maximum price filter |
| `filters.minRating` | number | - | Minimum rating filter (1-5) |
| `filters.inStockOnly` | boolean | false | Only return in-stock items |

### Output examples

#### Product details

```json
{
    "productId": "10421",
    "url": "https://www.iherb.com/pr/now-foods-vitamin-d-3/10421",
    "title": "Vitamin D-3, High Potency, 5,000 IU, 120 Softgels",
    "brand": "NOW Foods",
    "price": 8.99,
    "listPrice": 12.99,
    "discountPercentage": 31,
    "currency": "USD",
    "stockStatus": "in_stock",
    "rating": 4.8,
    "reviewCount": 15432,
    "upc": "733739003676",
    "categories": ["Vitamins", "Vitamin D"],
    "ingredients": ["Vitamin D-3 (as Cholecalciferol)", "..."],
    "suggestedUse": "Take 1 softgel daily with a fat-containing meal.",
    "packageQuantity": "120 count",
    "expiryDate": "2030-03-01T00:00:00.000Z",
    "expiryDateFormatted": "03/2030",
    "dimensions": { "length": 3.5, "width": 1.8, "height": 1.8, "unit": "in" },
    "stockQuantity": null,
    "isAvailableToPurchase": true,
    "images": ["https://..."],
    "scrapedAt": "2024-01-15T10:30:00.000Z"
}
```

#### Review data

```json
{
    "reviewId": "abc123",
    "productId": "10421",
    "rating": 5,
    "title": "Great product!",
    "text": "I've been taking this for 6 months and my levels are perfect now.",
    "reviewerName": "John D.",
    "reviewerLocation": "United States",
    "isVerified": true,
    "helpfulYes": 12,
    "helpfulNo": 1,
    "languageCode": "en-US",
    "languageName": "English",
    "hasRewarded": false,
    "date": "2024-01-10T00:00:00.000Z"
}
```

#### Price alert (Monitor mode)

```json
{
    "productId": "10421",
    "productName": "Vitamin D-3, High Potency",
    "changeType": "price_drop",
    "previousPrice": 12.99,
    "currentPrice": 8.99,
    "priceChangePercent": -31,
    "currency": "USD",
    "timestamp": "2024-01-15T10:30:00.000Z",
    "lowestPrice": 8.99,
    "highestPrice": 12.99,
    "history": [
        { "price": 12.99, "stockStatus": "in_stock", "checkedAt": "2024-01-14T10:30:00.000Z" },
        { "price": 8.99,  "stockStatus": "in_stock", "checkedAt": "2024-01-15T10:30:00.000Z" }
    ]
}
```

Monitor mode keeps up to 30 observations per product, so each run reports the
recent price series along with the lowest and highest price seen - enough to
chart a trend or trigger on "cheapest in a month" without storing anything
yourself.

### Tips and best practices

- 💰 **Control cost** - set `maxItems` to cap results, or set a maximum cost per run in the Console.
- ⚡ **Filter early** - `filters.inStockOnly` avoids paying for out-of-stock products.
- 📦 **Bulk extraction** - just set `maxItems` (or `0` for unlimited). The catalog API paginates through an entire category, so no special flag is needed.
- 🏷️ **Skip barcodes for speed** - set `includeUpc: false` in Details mode to halve the requests when you don't need UPC or SKU.
- 🌐 **Proxies** - not needed for most runs. Turn on Apify Proxy only if a run logs rate-limited requests.
- ⏰ **Scheduling** - Monitor mode is designed to run on a schedule; set one up in the Console and it diffs automatically.

### Which countries and currencies does the iHerb scraper support?

**188 countries and 84 currencies**, every one checked against iHerb's own regional pricing. Pick them from the **Country/Region** and **Currency** dropdowns.

- **Country** selects the iHerb market, so it changes both the price and whether a product can be shipped there. The same product often costs more in Kazakhstan or Kyrgyzstan than in the US.
- **Currency** only changes how the price is displayed. Leave it on USD to compare markets directly, or pick the local currency (for example KZT for Kazakhstan) to see what shoppers there see.

### What can you do with iHerb data?

- 💲 **Price comparison** - track iHerb prices against other retailers
- 📉 **Inventory monitoring** - get alerted when products return to stock
- 📊 **Market research** - analyze categories, pricing trends and review sentiment
- 🛒 **Dropshipping and ecommerce** - populate a storefront with product data
- 🥗 **Nutrition research** - collect ingredients and nutritional facts at scale
- 🗣️ **Review analysis** - aggregate customer feedback for sentiment analysis

### Frequently asked questions

#### Is it legal to scrape iHerb?

This Actor collects only publicly available data - information anyone can see without logging in. Scraping public data is generally legal, but how you use it is your responsibility. Don't collect personal data without a lawful basis, and review iHerb's terms before large-scale commercial use. If in doubt, seek legal advice.

#### Do I need proxies?

Usually not. The Actor reads iHerb's data APIs rather than its web pages, and in testing about 9,800 requests at up to 58 per second, including runs of nearly 2,000 products and 26,000 search results, went through without a single block or rate limit. If a run's log ever reports rate-limited requests, turn on Apify Proxy in the input; the Actor already retries and backs off on its own.

#### How many results can I get?

There's no hard cap. Set `maxItems` to whatever you need, or `0` for unlimited. For very large category extractions, enable `useSitemapForBulk`.

#### Why did my run return fewer items than I asked for?

`maxItems` caps the number of dataset items, not the number of pages checked. Filters (`minPrice`, `minRating`, `inStockOnly`) are applied after fetching, so a strict filter can return fewer matches than the cap. Out-of-stock or discontinued products are skipped and never billed.

#### Does it return expiry or best-by dates?

Yes. Details mode returns `expiryDate` in ISO format plus `expiryDateFormatted`
exactly as iHerb displays it (e.g. `03/2030`). iHerb publishes this for most
supplements, which matters for resellers and anyone tracking shelf life.

#### What does a null stockQuantity mean?

That iHerb has not disclosed a unit count - not that stock is zero. iHerb only
publishes a number once stock runs low ("Only 6 left"). Use `stockStatus` for
availability and treat `stockQuantity` as a bonus signal when present.

#### Can I scrape reviews for many products at once?

Yes. Pass multiple product URLs in `startUrls`, or a category URL - the Actor discovers products first, then collects reviews. Note that `maxItems` caps total reviews across all products, while `maxReviewsPerProduct` caps them per product.

#### Can I get notified when a price drops?

Yes. Use `monitor` mode with a `webhookUrl` and schedule the Actor to run daily. It compares against the previous run's baseline and reports only what changed.

#### How do I scrape a whole category?

Point `startUrls` at the category URL and set `maxItems` to `0` for unlimited.
The catalog API pages through the full category, so there is no size limit and
no special flag. The old `useSitemapForBulk` option is deprecated and ignored -
it read a site-wide sitemap that could not filter by category.

#### How do I export the data?

Download from the Console as JSON, CSV, Excel, XML or RSS, or pull it from the API. You can also push results straight to Google Drive, Slack, or any HTTP endpoint using Apify integrations.

#### Can you build a custom version for me?

Yes - if you need extra fields, a different output shape, or another supplement retailer, get in touch through the Apify Store contact link on this page.

# Actor input Schema

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

Select the operation mode. 'auto' will detect URL type automatically.

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

Product URLs, Category URLs, or Search URLs to scrape. The actor will auto-detect the type if mode is 'auto'.

## `searchKeywords` (type: `string`):

Keywords to search for (alternative to providing search URLs). Leave empty if using startUrls.

## `countryCode` (type: `string`):

Target country for localized pricing and availability

## `currencyCode` (type: `string`):

Currency for prices

## `filters` (type: `object`):

Filter results by brand, price range, or rating (applies to Search mode). Example: {"brand": "California Gold", "minPrice": 10, "maxPrice": 50, "minRating": 4}

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

Maximum number of dataset items to produce (0 = unlimited). In Reviews mode this caps total reviews, not products.

## `maxReviewsPerProduct` (type: `integer`):

Maximum reviews to fetch per product in Reviews mode (0 = all). Total output is still capped by Maximum Items.

## `reviewLanguage` (type: `string`):

Only return reviews written in this language (Reviews mode). All languages is the default: English alone is a fraction of iHerb reviews, and Russian is often the largest share.

## `includeUpc` (type: `boolean`):

Fetch UPC barcode, SKU and refreshed ratings in Details mode. This costs one extra request per product (roughly doubling requests). Turn off for faster, cheaper runs when you do not need barcodes.

## `webhookUrl` (type: `string`):

URL to POST changes when price/stock changes are detected. Receives full JSON payload.

## `monitorChangesOnly` (type: `boolean`):

In Monitor mode, only output items that changed since last run

## `useSitemapForBulk` (type: `boolean`):

Deprecated and ignored for category URLs. The catalog API already paginates through an entire category, so bulk extraction needs no special flag.

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

Maximum parallel requests (adaptive throttling will adjust automatically)

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

Not needed for most runs: iHerb's data APIs have not rate-limited this Actor even at high volume. Turn on Apify Proxy only if a run reports blocked or rate-limited requests.

## Actor input object example

```json
{
  "mode": "auto",
  "startUrls": [
    {
      "url": "https://www.iherb.com/c/vitamins"
    }
  ],
  "countryCode": "US",
  "currencyCode": "USD",
  "maxItems": 100,
  "maxReviewsPerProduct": 100,
  "reviewLanguage": "all",
  "includeUpc": true,
  "monitorChangesOnly": true,
  "useSitemapForBulk": false,
  "maxConcurrency": 5,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One item per scraped record. The shape depends on the run mode: product summaries in Search mode, full product details in Details mode, individual customer reviews in Reviews mode, and price/stock change records in Monitor mode.

## `runStats` (type: `string`):

Counters for the run: mode, total and successful requests, failures, items scraped, duplicates skipped, rate-limit hits and average request time.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.iherb.com/c/vitamins"
        }
    ],
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("vaunted/iherb-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 = {
    "startUrls": [{ "url": "https://www.iherb.com/c/vitamins" }],
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("vaunted/iherb-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 '{
  "startUrls": [
    {
      "url": "https://www.iherb.com/c/vitamins"
    }
  ],
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call vaunted/iherb-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vaunted/iherb-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/77ucUPltttheQkuZl/builds/jRM5VFwfjjnNFntqa/openapi.json
