# Global Industrial Scraper (`crawlerbros/globalindustrial-scraper`) Actor

Scrape Global Industrial (globalindustrial.com) - US industrial & B2B supplies marketplace with 1M+ products. Search by keyword, browse by category, fetch by product ID or URL - prices, brands, availability, ratings, images, specs.

- **URL**: https://apify.com/crawlerbros/globalindustrial-scraper.md
- **Developed by:** [Crawler Bros](https://apify.com/crawlerbros) (community)
- **Categories:** Automation, E-commerce, Lead generation
- **Stats:** 4 total users, 3 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Global Industrial Scraper

Scrape **Global Industrial (globalindustrial.com)** — one of America's largest industrial & B2B supplies marketplaces (1M+ SKUs across tools, material handling, HVAC, safety, packaging, electrical, foodservice and more). Search by keyword, browse by department/category, fetch by item key or direct URL, and get product prices, brands, availability, ratings, images and technical specs. No login, no API key, no residential proxy — runs on the free Apify AUTO proxy tier with US egress.

### What this actor does

- **Four modes:** `search`, `byCategory`, `byProductIds`, `byUrls`
- **Full product data:** price + price breaks, MSR/sale/overstock prices, brand, SKU/MPN, availability & lead time, ratings & review counts, images, dimensions, specs, flags (best seller, premium, hazardous…)
- **Category browse:** every department (`/t/…`) and category (`/c/…`) with optional subcategory tree records
- **Filters:** price range, brand, keyword-in-title, minimum rating, in-stock only
- **Empty fields are omitted**

### Data source

| | |
|---|---|
| Domain | `www.globalindustrial.com` |
| Tech | Next.js server-side rendering — all data comes from the `__NEXT_DATA__` JSON embedded in each page (category products also under `categoryDetails.productByGroups`), plus the same-origin `catalogApis/catalog/product` lookup for item keys |
| Search | `https://www.globalindustrial.com/searchResult?q=<keyword>` (SSR; page 2+ adds `list=true&cp=<offset>`) |
| Category browse | `https://www.globalindustrial.com/c/<path>` and `/t/<department>` (SSR) |
| Product pages | `https://www.globalindustrial.com/p/<slug>` (SSR) |
| Images | `images.globalindustrial.com` CDN (`/images/{size}/{image}.webp`) — accessible from anywhere |
| Egress | The origin is behind **CloudFront with a US-only geo-restriction**: every request from a non-US IP gets `403 Request blocked`. The actor therefore defaults to **Apify AUTO proxy with country `US`** (free on all plans, no residential cost). AUTO rotates IPs automatically, and the actor retries with a fresh proxy exit on 403/429/5xx. |

**Replacement history (slot lineage).** This actor's marketplace slot was originally assigned to **Costco (costco.com)**, then **Zoro (zoro.com)** — both were proven hard-blocked from Apify cloud egress: direct requests and the AUTO, RESIDENTIAL and SHADER proxy tiers all returned 403 for every page type. Per the project's replacement policy, the slot was reassigned to a same-category platform that is verifiably reachable from Apify's US egress: **Global Industrial**, a major US wholesale/industrial supplies marketplace (same vertical as Costco Business and Zoro: MRO, warehouse, tools, janitorial, packaging, electrical, HVAC). Cloud egress probe: `/c/industrial-supplies` and `/c/tools` return 200 with SSR product data, and all scrape paths below were reverse-engineered from the live site's own client code and archived page captures.

### Output per product (recordType = `product`)

- `itemKey` — Global Industrial item ID
- `title`, `productUrl`, `brand`, `sku`, `legacyNumber`, `manufacturerPartNumber`, `unspscCode`
- `price`, `priceOriginal`, `priceCatalog`, `priceMsr`, `priceOverstock`, `priceSale`, `priceClearance`, `minSalePrice`, `savings`, `priceType`, `autoReOrderDiscount`
- `priceBreaks[]` — quantity-tier pricing (`qtyStart`, `qtyEnd`, `price`, `priceOriginal`, `priceType`)
- `availabilityStatus` (`InStock` / …), `shipTime`, `leadTime`, `freeShipping`, `inStock`
- `avgRating`, `numReviews`
- `imageUrl` (177×177), `imageUrlLarge` (500×500), `productImages[]` (product pages only)
- `categoryKey`, `categoryUrl`, `categoryPath`, `breadcrumbs[]` (`url`, `label`)
- Flags: `isBestSeller`, `isPremium`, `isNewArrival`, `isPrivateLabel`, `isHazardous`, `isNonReturnable`, `isNonCancellable`, `isCallForPrice`, `isMapPrice`, `isCustomizable`, `isFreightFixed`, `isNoAirShip`, `isLtl`, `isReorder`, `isPromotional`
- `dimensions` (`length`, `height`, `width`, `unit`), `qty`, `attributes[]` (`group`, `name`, `value`), `description`, `itemLaunchDate`, `timeStamp`
- `recordType: "product"`, `sourceUrl`, `scrapedAt`

### Output per category (recordType = `category`)

- `categoryKey`, `categoryCode`, `title`, `categoryPath`, `leafUrl`, `categoryLevel`, `parentCategoryKey`
- `totalItems`, `sort`, `showMore`, `startIndex`, `categoryUrl`, `heading`, `titleTag`
- `subcategories[]` — child categories with `categoryKey`, `title`, `url`, `totalItems` (when `includeSubcategories` is on)

### Output per failed lookup (recordType = `error`)

- `itemKey` and/or `sourceUrl`, `error` (short reason), `recordType: "error"`, `scrapedAt`

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `search` | `search` / `byCategory` / `byProductIds` / `byUrls` |
| `searchQuery` | string | `adjustable workbench` | Free-text keyword (mode=search) |
| `sortBy` | string | `relevance` | `relevance` / `price_low` / `price_high` / `best_selling` |
| `categoryPreset` | string | – | Popular departments & categories (mode=byCategory) |
| `categoryPath` | string | – | Any `/c/…` or `/t/…` path or URL; overrides preset (mode=byCategory) |
| `includeSubcategories` | boolean | `false` | Emit category tree records while browsing |
| `productIds` | array | – | Numeric item keys, or `/p/<slug>-<key>` URLs (mode=byProductIds) |
| `startUrls` | array | – | Product / category / search URLs (mode=byUrls) |
| `minPrice` / `maxPrice` | number | – | Listing-price bounds (USD) |
| `minRating` | integer | – | Minimum average rating (1–5) |
| `brand` | string | – | Brand substring filter |
| `containsKeyword` | string | – | Title substring filter |
| `inStockOnly` | boolean | `false` | Keep only InStock products |
| `maxItems` | integer | `50` | Hard cap on product records (1–1000) |
| `proxyConfiguration` | object | AUTO + US | Prefilled: `{"useApifyProxy": true, "apifyProxyCountry": "US"}` |

#### Example: keyword search with filters

```json
{
  "mode": "search",
  "searchQuery": "pallet jack",
  "sortBy": "price_low",
  "minPrice": 100,
  "maxPrice": 500,
  "inStockOnly": true,
  "maxItems": 100
}
```

#### Example: browse a department, include subcategories

```json
{
  "mode": "byCategory",
  "categoryPreset": "Power Tools",
  "includeSubcategories": true,
  "maxItems": 200
}
```

#### Example: exact category path with price filter

```json
{
  "mode": "byCategory",
  "categoryPath": "/c/tools/abrasives/sanding_discs",
  "brand": "3M",
  "maxItems": 50
}
```

#### Example: lookup by item key, or scrape by URL

```json
{
  "mode": "byProductIds",
  "productIds": ["33082504", "33201668"]
}
```

```json
{
  "mode": "byUrls",
  "startUrls": [
    { "url": "https://www.globalindustrial.com/p/variable-speed-control-switch-with-6-ft-plug" },
    { "url": "https://www.globalindustrial.com/c/tools/sockets_bits" },
    { "url": "https://www.globalindustrial.com/searchResult?q=shelving" }
  ]
}
```

### Use cases

- **MRO & procurement teams** — pull current pricing, availability and specs for industrial supplies
- **Resellers & distributors** — monitor competitor assortment and price breaks by brand or category
- **Category intelligence** — track SKU counts, best-seller flags and new-arrival flags across departments
- **E-commerce benchmarking** — compare list vs MSR vs clearance pricing across 1M+ SKUs
- **Supply-chain research** — lead-time and free-shipping signals for sourcing decisions

### FAQ

**What is the data source?** Global Industrial's own website — server-rendered Next.js pages (`__NEXT_DATA__`) and the site's public `catalogApis` product lookup. No third-party data.

**Why does it need a US proxy if no login is required?** The site's CloudFront distribution rejects every non-US IP with HTTP 403 ("Request blocked"). Apify's AUTO proxy with country `US` is included free on every Apify plan, so there is no extra cost. The actor auto-retries on 403/429 with a fresh proxy exit.

**Are prices real-time?** Yes — each run scrapes the live listing pages, including current sale/clearance/MAP flags and quantity price breaks.

**Why is `byCategory` sometimes limited?** Category pages render products server-side (flat list and/or `productByGroups`). If a category returns zero embedded products, the actor still emits the category + subcategory tree and reports a status message; this happens only for CMS-only landing pages without product grids. The number of products the site embeds per SSR page varies by page configuration (typically 18–54). Some deep categories (4+ path segments, e.g. `/c/tools/tool_storage/chests_roller_cabinets`) expose a `__1`-suffixed URL variant that renders the full product grid, so those browse fully. Other deep paths no longer exist as separate SSR pages — e.g. `/c/tools/power_tools/drills` and `/c/tools/abrasives/sanding_discs` now redirect to their parent category (or, for the `__1` variant, to `/t/tools`), so the actor emits what the parent page serves and reports the shortfall in its status message. Shallow categories (3 segments, e.g. `/c/tools/power_tools`) serve only their first SSR grid, and the actor reports the shortfall in its status message.

**How does pagination work?** Search and category SSRs only advance when the site's quirks are satisfied: search page 2+ needs `list=true&cp=<offset>` (`cp` is an item offset, not a page number) and deep category pages need `sort=most_relevant&cp=<offset>` on the plain URL. The actor appends these automatically and walks until `maxItems`, the site's last page, or the point where the SSR stops advancing (reported in the status message).

**Why do some searches return 0 records?** Queries that exactly match a brand (e.g. `dewalt`) get redirected by the site to a `/shopbybranditems` brand landing page whose products load entirely client-side — nothing is in the server-rendered HTML, so nothing can be scraped without a browser. Add a second keyword (e.g. `dewalt drill`) and the search stays on server-rendered results. The actor reports this case in its status message.

**Why do some `byProductIds` lookups fail?** The site's legacy `catalogApis` product endpoint now returns HTTP 403 for every egress IP, so the actor falls back to the site's own search index, which indexes most — but not all — item keys. Keys that resolve are emitted as full products; keys the index doesn't know produce a typed `error` record (visible in the dataset). For guaranteed lookups, pass a full product page URL (`/p/<slug>`) in `productIds` — those are fetched directly and always resolve.

**Why are some products missing images?** Products without an image key on the source simply omit the image fields — no placeholders are ever fabricated.

**Does this actor require affiliation with Global Industrial?** No. This is an independent third-party actor using the public website.

**What are `recordType: "error"` records?** Lookups that fail (invalid ID, not found, blocked page) are emitted as typed error records with `sourceUrl` and a short `error` reason so you can audit partial runs.

**Is this actor affiliated with Apify?** It is built on the Apify platform; the data source is a third-party website.

**Are there rate limits?** The site has no documented API limits. The actor uses polite delays and retries, and rotates proxy exits automatically.

# Actor input Schema

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

What to fetch.

## `searchQuery` (type: `string`):

Free-text keyword, e.g. `workbench`, `pallet jack`, `3m respirator`. (mode=search)

## `sortBy` (type: `string`):

Search result ordering. (mode=search)

## `categoryPreset` (type: `string`):

Popular department / category shortcuts. Overridden by `categoryPath` if both are set. (mode=byCategory)

## `categoryPath` (type: `string`):

Any category URL or path, e.g. `/c/tools/power_tools`, `/c/tools/abrasives/sanding_discs`, `https://www.globalindustrial.com/t/hvac`. Takes precedence over `categoryPreset`. (mode=byCategory)

## `includeSubcategories` (type: `boolean`):

Emit a `category` record with the subcategory tree when browsing a category. (mode=byCategory, byUrls)

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

Global Industrial item keys (numeric IDs), e.g. `33082504`. A `https://www.globalindustrial.com/p/<slug>-<key>` URL also works — the trailing key is extracted. (mode=byProductIds)

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

Product (`/p/...`), category (`/c/...`, `/t/...`) or search (`/searchResult?q=...`) URLs. (mode=byUrls)

## `minPrice` (type: `number`):

Drop products cheaper than this (uses the listing price).

## `maxPrice` (type: `number`):

Drop products more expensive than this (uses the listing price).

## `minRating` (type: `integer`):

Only products with at least this average rating.

## `brand` (type: `string`):

Only products whose brand contains this text, e.g. `3M`, `Metro`, `Vestil`.

## `containsKeyword` (type: `string`):

Only products whose title contains this text (case-insensitive).

## `inStockOnly` (type: `boolean`):

Only products whose availability status is InStock.

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

Hard cap on product records emitted per run (1–1000).

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

The site only serves US IPs (CloudFront geo-restriction). Apify AUTO proxy with country `US` is prefilled and free on every plan.

## Actor input object example

```json
{
  "mode": "search",
  "searchQuery": "milk",
  "sortBy": "relevance",
  "includeSubcategories": false,
  "inStockOnly": false,
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

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

Dataset containing all scraped Global Industrial products, categories, and lookup errors.

# 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 = {
    "mode": "search",
    "searchQuery": "milk",
    "sortBy": "relevance",
    "includeSubcategories": false,
    "inStockOnly": false,
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("crawlerbros/globalindustrial-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 = {
    "mode": "search",
    "searchQuery": "milk",
    "sortBy": "relevance",
    "includeSubcategories": False,
    "inStockOnly": False,
    "maxItems": 20,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("crawlerbros/globalindustrial-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 '{
  "mode": "search",
  "searchQuery": "milk",
  "sortBy": "relevance",
  "includeSubcategories": false,
  "inStockOnly": false,
  "maxItems": 20,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  }
}' |
apify call crawlerbros/globalindustrial-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,crawlerbros/globalindustrial-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/0ez4jC8hT2q5et1zl/builds/R2EBfxdY4VNcG4pjg/openapi.json
