# Grainger Product Scraper (`axlymxp/grainger-product-scraper`) Actor

Scrape Grainger.com industrial and MRO products by keyword, category or item number: web price and list price, brand, manufacturer part number, stock status, full technical specs, images and category path. Built for price monitoring and catalog enrichment. Pay only for results.

- **URL**: https://apify.com/axlymxp/grainger-product-scraper.md
- **Developed by:** [axly](https://apify.com/axlymxp) (community)
- **Categories:** E-commerce, Business
- **Stats:** 3 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 dataset 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

## Grainger Product Scraper

**Get clean, structured product data from Grainger.com — the largest US industrial and MRO supplier — by keyword, category or item number.** Web price *and* list price, brand, manufacturer part number, stock status, the full technical spec sheet, images and category path for every product, as JSON, CSV or Excel.

No Grainger account, no browser automation and no proxy setup needed.

### Who uses it

- **Procurement & MRO buyers** — price-check a list of Grainger item numbers on a schedule and catch price changes before re-ordering.
- **Distributors & resellers** (Zoro, Amazon Business, eBay, your own store) — monitor Grainger prices and stock status across whole categories to stay competitive.
- **PIM / catalog teams & SaaS developers** — enrich product records with manufacturer part numbers, specs, dimensions, weights and images.
- **Analysts & researchers** — track industrial pricing, discounts (list vs web price) and assortment by category.

### What you get

| Field | Type | Example |
|---|---|---|
| `sku` | string | `52RW10` (Grainger item number) |
| `name` | string | `Disposable Gloves: Chemical-Resistant/Food-Grade/Gen Purpose, XS ( 6 ), 8 mil, Grain, 50 PK` |
| `description` | string | short description |
| `brand` | string | `ANSELL` |
| `manufacturer_part_number` | string | `93-260` |
| `price` | number | `49.99` — current web price |
| `list_price` | number | `62.5` — list price before discount |
| `currency` | string | `USD` |
| `price_type` | string | `WP` web price, `ZPR1` promotion, … |
| `unit` | string | `Package`, `Each`, … |
| `sales_status` / `sales_status_message` | string | `ST` / `Typically in Stock` |
| `stocked`, `discontinued`, `clearance`, `ready_to_ship` | boolean | stock & lifecycle flags |
| `online_availability` | string | `GENERALSALE` |
| `rating`, `review_count` | number | review summary |
| `image` | string | main product image URL |
| `url` | string | `https://www.grainger.com/product/52RW10` |
| `category_path` | string | `Safety > Hand & Arm Protection > Safety Gloves > …` |
| `specs` | object | `{"Material": "Neoprene/Nitrile", "Nominal Palm Thickness": "8 mil", …}` |
| `package` | object | sell-pack `height`, `length`, `width` (in), `weight` (lb), `quantity` |
| `has_alternates` | boolean | Grainger lists alternate products |
| `restricted_states` | array | US states where sale is restricted |
| `source`, `search_query`, `category_url` | string | how the row was found |
| `scraped_at` | string | ISO-8601 UTC timestamp |

### Use cases

1. **SKU price monitoring** — paste your Grainger item numbers into *Item numbers*, schedule a daily run and diff `price` / `sales_status` in Google Sheets or your ERP.
2. **Category price intelligence** — point *Category URLs* at e.g. `…/category/safety/hand-arm-protection` and collect every product in every sub-category with prices and specs.
3. **Competitive repricing** — search for the products you sell (`"dewalt drill"`, `"safety glasses"`) and compare Grainger's web price and discount against yours.
4. **Catalog enrichment** — fill missing manufacturer part numbers, specifications, dimensions and images in your PIM from a list of item numbers.

### Input

| Parameter | Description | Default |
|---|---|---|
| **Search queries** | Keywords, item numbers or part numbers, as typed into Grainger search. Each query expands into its best-matching categories with the query as a filter. | — |
| **Category URLs** | Grainger category pages. Parent categories are crawled into all sub-categories. | — |
| **Item numbers or product URLs** | e.g. `52RW10` or `https://www.grainger.com/product/…-52RW10`. Looked up 100 at a time. | — |
| **Max products (total)** | Stop after this many rows. | 100 |
| **Max products per query / category** | Cap per search query and per category URL. | 200 |
| **Max categories per search query** | How many matching categories a search collects from. | 5 |
| **Sort order** | Top sellers, price low→high / high→low, brand A→Z / Z→A (Grainger's own ordering). | Top sellers |
| **Include technical specifications** | Add the `specs` object. | on |

Provide at least one of search queries, category URLs or item numbers. They can be combined in one run; each product is returned once.

#### Example input

```json
{
    "searchQueries": ["nitrile gloves"],
    "categoryUrls": ["https://www.grainger.com/category/safety/hand-arm-protection/finger-protection"],
    "skus": ["52RW10", "https://www.grainger.com/product/ANSELL-Disposable-Gloves-Chemical-793JR4"],
    "maxItems": 60,
    "maxItemsPerQuery": 30,
    "sort": "price_asc"
}
```

#### Example output

```json
{
    "sku": "52RW10",
    "name": "Disposable Gloves: Chemical-Resistant/Food-Grade/Gen Purpose, XS ( 6 ), 8 mil, Grain, 50 PK",
    "description": "DisposableGloves, Green, XS, Size6, PK50",
    "brand": "ANSELL",
    "manufacturer_part_number": "93-260",
    "price": 49.99,
    "list_price": 62.5,
    "currency": "USD",
    "price_type": "WP",
    "unit": "Package",
    "sales_status": "ST",
    "sales_status_message": "Typically in Stock",
    "stocked": true,
    "discontinued": false,
    "clearance": false,
    "ready_to_ship": true,
    "online_availability": "GENERALSALE",
    "rating": 0.0,
    "review_count": 0,
    "image": "https://static.grainger.com/rp/s/is/image/Grainger/52RW10_GC01?$lgmain$",
    "url": "https://www.grainger.com/product/52RW10",
    "category_path": "Safety > Hand & Arm Protection > Safety Gloves > Disposable Gloves > General Purpose Disposable Gloves",
    "specs": {"Material": "Neoprene/Nitrile", "Nominal Palm Thickness": "8 mil", "Powdered/Powder-Free": "Powder-Free", "Color Family": "Green"},
    "package": {"height": 2.6, "length": 11.1, "width": 5.0, "weight": 1.3, "quantity": 1},
    "has_alternates": true,
    "restricted_states": [],
    "source": "sku",
    "search_query": null,
    "category_url": null,
    "scraped_at": "2026-10-02T06:02:33Z"
}
```

### Scheduling, webhooks and integrations

- **Schedule** the Actor (e.g. daily at 06:00) from the *Schedules* tab for ongoing price monitoring.
- **Export** results as JSON, CSV, Excel or XML, or pull them via the Apify API.
- **Integrate** with Google Sheets, Make, Zapier, Slack, Airbyte or a webhook that fires when a run finishes.

### Use with AI assistants (MCP)

This Actor works with the [Apify MCP server](https://mcp.apify.com), so Claude, ChatGPT, Cursor and other MCP clients can call it directly — e.g. *"Find the 20 cheapest nitrile gloves on Grainger and list brand, part number and price."*

### FAQ

**How many products can I get?** Grainger lists about 1.6 million items. Use category URLs (parents are crawled into every sub-category) or item-number lists for bulk jobs; `maxItems` caps the run.

**How fresh is the data?** Every run fetches live data from Grainger at run time — prices and stock status are what a visitor sees at that moment.

**Which price is returned?** The public (guest) web price and the list price. Account- or contract-specific pricing requires a Grainger login and is not included.

**Why does a search return products from several categories?** Grainger maps keywords to categories; the Actor collects the best-matching categories (`maxCategoriesPerQuery`) with your query applied as a filter. Grainger's matching can be broad — use category URLs or item numbers when you need an exact scope.

**Is "price low to high" strictly ordered?** The order is Grainger's own sort, which can differ slightly from the displayed web price for multi-pack items.

**What happens with an item number that doesn't exist?** It's skipped and logged; the run continues.

**Is scraping Grainger legal?** The Actor collects publicly available product information. You are responsible for using the data in line with Grainger's terms and applicable law.

**Something broke or a field is missing?** Open an issue on the Actor's *Issues* tab — reports are answered quickly.

# Actor input Schema

## `searchQueries` (type: `array`):

Keywords, Grainger item numbers or manufacturer part numbers, exactly as you would type them into the Grainger search box. Each query is expanded into its best-matching categories with the query applied as a filter.

## `categoryUrls` (type: `array`):

Grainger category pages, e.g. https://www.grainger.com/category/safety/hand-arm-protection. Parent categories are crawled down into all of their sub-categories.

## `skus` (type: `array`):

Grainger item numbers (e.g. 52RW10) or product URLs (https://www.grainger.com/product/...-52RW10). Looked up in batches of 100 — the fastest way to price-check a known SKU list.

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

Stop after this many product rows across all inputs.

## `maxItemsPerQuery` (type: `integer`):

Cap for each search query and each category URL (item-number lookups are only limited by Max products).

## `maxCategoriesPerQuery` (type: `integer`):

A search usually matches several categories (e.g. 'nitrile gloves' → disposable, coated, chemical-resistant). How many of the best matches to collect from.

## `sort` (type: `string`):

Order of products within each category.

## `includeSpecs` (type: `boolean`):

Add the full spec sheet (material, size, standards, …) as a `specs` object. Turn off for lighter rows.

## Actor input object example

```json
{
  "searchQueries": [
    "nitrile gloves",
    "dewalt drill",
    "safety glasses"
  ],
  "categoryUrls": [
    "https://www.grainger.com/category/safety/hand-arm-protection/safety-gloves/disposable-gloves/general-purpose-disposable-gloves"
  ],
  "skus": [
    "52RW10",
    "1FEW8",
    "https://www.grainger.com/product/ANSELL-Disposable-Gloves-Chemical-793JR4"
  ],
  "maxItems": 100,
  "maxItemsPerQuery": 200,
  "maxCategoriesPerQuery": 5,
  "sort": "relevance",
  "includeSpecs": true
}
```

# Actor output Schema

## `products` (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 = {
    "searchQueries": [
        "nitrile gloves"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("axlymxp/grainger-product-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 = { "searchQueries": ["nitrile gloves"] }

# Run the Actor and wait for it to finish
run = client.actor("axlymxp/grainger-product-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 '{
  "searchQueries": [
    "nitrile gloves"
  ]
}' |
apify call axlymxp/grainger-product-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,axlymxp/grainger-product-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/9ycGlksMn6GlGHbXT/builds/5qqGexVraalhoxkI7/openapi.json
