# Bank of Lamps Scraper (`memo23/bankoflamps-scraper`) Actor

Scrape Bank of Lamps wholesale electrical products: name, EAN, MPN, GTIN, brand, public list price, stock, technical specs, images, and datasheets. Paste any category, brand, or product URL on bankoflamps.com. One row per SKU. JSON or CSV out, billed per result. Caps via maxItems.

- **URL**: https://apify.com/memo23/bankoflamps-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** E-commerce
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 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.

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

## Bank of Lamps Scraper

**Turn bankoflamps.com into structured wholesale electrical product data.** Paste a category, brand, or product URL and get EAN, MPN, brand, public list price, stock, technical specs, images, and datasheets — one row per SKU. JSON or CSV out.

#### How it works

![How Bank of Lamps Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-bankoflamps.png)

#### ✨ Why use this scraper?

- **Three starting points.** Category (`/luminaires`), brand (`/philips`), or a direct product URL (`/{slug}-{ean}`) — classified automatically.
- **JSON-LD–backed rows.** Name, EAN/SKU, MPN, GTIN, list price, availability, brand, and the spec table come from the page's `Product` graph — the same block search engines read.
- **Specs stay structured.** Socket, IP rating, wattage, CRI, colour temperature, HS code, ETIM class, and the rest land in `specs[]` with units.
- **Public list price, labelled as such.** Guest price is in the HTML. Validated B2B net prices stay behind login and are not scraped. `priceNote` says so on every row.
- **Datasheets when the page has them.** Energy labels and product PDFs are collected as `documents[]`.
- **Hard cap on a 1.5M catalog.** Always set `maxItems`. One category can run to thousands of pages.

#### 🎯 Use cases

| Team | What they build |
|------|-----------------|
| **Electrical distributors** | Assortment overlays against Bank of Lamps stock and list price |
| **Procurement / PIM** | EAN-keyed product feeds with MPN, brand, and spec attributes |
| **Lighting project desks** | Spec sheets for luminaires, LED lamps, and installation gear |
| **Market researchers** | Brand coverage and public list-price snapshots across the EU B2B catalog |
| **Agencies** | Scheduled category dumps for a client PIM or price-watch sheet |

#### 📥 Supported inputs

Pass any mix of these in `startUrls`. Each URL is classified automatically.

| URL pattern | Behaviour |
|---|---|
| `https://bankoflamps.com/{category}` | **Listing** — walks `?page=N` (~60 products per page) |
| `https://bankoflamps.com/{brand}` | **Listing** — same pagination, brand-filtered |
| `https://bankoflamps.com/search/?q=…` | **Listing** — keyword search (the site is picky; EAN/MPN in the address bar works better than a vague word) |
| `https://bankoflamps.com/{slug}-{ean}` | **Product detail** — routed straight to the parser |
| `https://bankoflamps.com/{de\|fr\|ru\|lt\|lv\|et}/…` | Same patterns with a locale prefix |

Easiest workflow: open the category or brand you want on bankoflamps.com, copy the address bar, paste into `startUrls`, set `maxItems`.

**Not supported:**

- Login, cart, checkout, quote, or account pages
- Validated B2B net prices (those need a company account)
- Hosts outside `bankoflamps.com`

#### 🔄 How it works

1. **Classify each `startUrl`** as a listing or a product (`/{slug}-{ean}`).
2. **Fetch each listing** and collect product links. Category and brand pages paginate with `?page=N`.
3. **Fetch each product page** in parallel via a sliding window.
4. **Parse the JSON-LD `Product` + `Offer` graph**, plus MOQ, package qty, breadcrumbs, and document links from the HTML.
5. **Push one row per SKU** to the dataset.

#### ⚙️ Input parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| `startUrls` | array | `["https://bankoflamps.com/luminaires"]` | Category, brand, and/or product URLs. Mix freely. |
| `maxItems` | integer | `100` | Hard cap on products collected. The live catalog is 1.5M+ SKUs. |
| `monitoringMode` | boolean | `false` | Skip EANs already delivered to you on a previous run. First run after enabling still returns every match (it seeds the baseline). |
| `resetMonitoringState` | boolean | `false` | Clear the saved "already seen" EANs at the start of this run. |
| `maxConcurrency` / `minConcurrency` | integer | `10` / `1` | Parallel product-page limits. |
| `maxRequestRetries` | integer | `5` | Retries before a request is given up. |
| `proxy` | object | Apify residential | Proxy configuration. The site has no interactive challenge; a small pool is enough. |

#### 📊 Output overview

Each scraped SKU is one dataset row of `type: "product"`. Row count equals product count — no padding rows. Price is the **public list price (excl. VAT)**. Individual B2B prices are not on the public page.

#### 📦 Output sample

Real row from `/luminaires` (trimmed):

```json
{
    "type": "product",
    "productId": "118444",
    "ean": "4752280007892",
    "mpn": "03001G3",
    "gtin": "04752280007892",
    "sku": "4752280007892",
    "productUrl": "https://bankoflamps.com/microwave-led-sensor-lamp-9w-3000k-4000k-6000k-1100lm-4-16m-ip44-ik10-thorgeon-4752280007892",
    "name": "Microwave LED Sensor Lamp 9W 3000K/4000K/6000K 1100Lm 4-8m IP44 IK10 4752280007892 — 03001G3",
    "brand": "THORGEON",
    "brandUrl": "https://bankoflamps.com/thorgeon",
    "category": "Luminaires",
    "price": 35.43,
    "priceCurrency": "EUR",
    "priceNote": "List price. Register for individual B2B price.",
    "availability": "InStock",
    "inStock": true,
    "moq": 1,
    "packageQty": 1,
    "images": [
        "https://image-cdn.bankoflamps.com/1200x1200/7e/7e8b88397969752f67d0930edd6ebe7ae288bd3c.jpg"
    ],
    "specs": [
        { "name": "Degree of protection (IP)", "value": "IP44", "unit": null },
        { "name": "Impact strength", "value": "IK10", "unit": null },
        { "name": "Max. system power", "value": 9, "unit": "W" },
        { "name": "Rated luminous flux according to IEC 62722-2-1", "value": 1100, "unit": "lm" },
        { "name": "ETIM Class", "value": "EC002892", "unit": null }
    ],
    "documents": [
        { "title": "Energy label", "url": "https://blob-cdn.bankoflamps.com/76/761df299237aef93554391ad2a192ea3831fea97.pdf" }
    ],
    "scrapedAt": "2026-09-21T10:29:40.607Z"
}
```

#### 🗂 Key output fields

| Group | Fields |
|---|---|
| **Identity** | `productId`, `ean`, `mpn`, `gtin`, `sku`, `productUrl` |
| **Merchandising** | `name`, `description`, `brand`, `brandUrl`, `category`, `categories[]`, `keywords[]` |
| **Price & stock** | `price`, `priceCurrency`, `priceNote`, `availability`, `inStock`, `condition`, `moq`, `packageQty` |
| **Specs & media** | `specs[]` (`name`, `value`, `unit`), `images[]`, `documents[]` (`title`, `url`) |
| **Run** | `type` (`product`), `scrapedAt` |

#### ❓ FAQ

**Which URLs are supported?**
Category pages, brand pages, search URLs, and product URLs that end in an 8–14 digit EAN. Login, cart, checkout, and quote pages are skipped.

**Is the price the real B2B net?**
No. It is the public list price shown to guests. The site assigns individual net prices after a company account is validated. Every row sets `priceNote` so that is visible in the export.

**How big is the catalog?**
About 1.5–2.0 million products, ~1,000 categories, ~790 brands. Set `maxItems` on every run.

**Does keyword search work?**
The HTML search form is finicky from a plain GET. Prefer a category or brand URL, or a product URL / EAN you already have.

**Can I scrape prices behind login?**
No. The actor only reads public pages.

**How do I get only new products on a schedule?**
Turn on `monitoringMode`. The first run builds a per-user EAN baseline; later runs skip EANs you already received.

#### 💬 Support

- For issues or feature requests, use the **Issues** tab on the actor's Apify Console page.
- Author's website: <https://muhamed-didovic.github.io/>
- Email: <muhamed.didovic@gmail.com>

#### 🛠 Additional services

- Custom output shape, extra fields, or a one-off full-catalog dump: <muhamed.didovic@gmail.com>
- A similar scraper for another EU electrical wholesaler: same inbox.
- API access (no Apify fee, usage fee only): <muhamed.didovic@gmail.com>

#### 🔎 Explore more scrapers

If this was useful, see other scrapers at [memo23's Apify profile](https://apify.com/memo23).

***

### 🤖 For AI Agents & LLM Apps

Compact reference for AI agents calling this actor via the [Apify MCP server](https://mcp.apify.com) or the Apify API (actor: `memo23/bankoflamps-scraper`).

**Purpose:** Scrapes wholesale electrical products from bankoflamps.com — category, brand, or product URLs — one dataset row per SKU with public list price, EAN/MPN, specs, and images.

**Minimal input:**

```json
{
  "startUrls": ["https://bankoflamps.com/luminaires"],
  "maxItems": 50
}
```

**Output:** one row per product — type, productId, ean, mpn, gtin, sku, productUrl, name, description, brand, brandUrl, category, categories\[], price, priceCurrency, priceNote, availability, condition, inStock, moq, packageQty, images\[], specs\[] {name, value, unit}, documents\[] {title, url}, keywords\[], scrapedAt.

**Behaviors an agent should know:**

- Always set `maxItems`. The catalog is 1.5M+ SKUs; an uncapped category walk is huge.
- `startUrls` are auto-classified: `/{slug}-{ean}` is a product; everything else on the host is treated as a listing and paginated with `?page=N`.
- `price` is the public list price (EUR, excl. VAT), not the validated B2B net.
- `monitoringMode` skips EANs already delivered to that user; skipped products are not fetched and not charged.
- Only public pages. No login, cart, or quote endpoints.
- Pay-per-event billing — see the Pricing tab on the actor page.

***

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by Bank of Lamps, ATTA-1 SIA, or any of their subsidiaries or affiliates. All trademarks mentioned are the property of their respective owners.

The scraper accesses only publicly available product, category, and brand pages on bankoflamps.com — no authenticated endpoints, B2B-portal-only prices, or content behind a Bank of Lamps login. Users are responsible for ensuring their use complies with bankoflamps.com's Terms of Service, applicable data-protection law (GDPR, CCPA, etc.), and any contractual obligations of their own organisation.

***

### SEO Keywords

bankoflamps scraper, bank of lamps scraper, scrape bankoflamps, bankoflamps api, Apify bankoflamps, wholesale lighting scraper, electrical wholesale catalog scraper, EAN product scraper, MPN lighting data, EU electrical distributor scraper, LED lamp catalog scraper, luminaire specs scraper, OSRAM Philips ABB catalog data, B2B lighting product feed, electrical PIM scraper, industrial lighting wholesale data, bankoflamps.com products

# Actor input Schema

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

Full bankoflamps.com URLs to crawl. Mix category, brand, and product URLs in the same array if useful.

## `monitoringMode` (type: `boolean`):

When enabled, products whose EAN has already been delivered to you are skipped (no detail page is fetched and no charge applies for them). The first run after enabling monitoring returns every match — that builds your baseline. Every subsequent run returns only new products.

## `resetMonitoringState` (type: `boolean`):

One-shot toggle: clear the saved 'products already seen' record at the start of this run. Use this when you change start URLs, want to rebuild the baseline, or recover from a missed delivery. Has no effect when monitoring mode is off.

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

Hard cap on the number of products collected. The catalog has 1.5M+ SKUs; always set this unless you want a full dump.

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

Maximum number of product pages processed in parallel.

## `minConcurrency` (type: `integer`):

Minimum number of product pages processed in parallel.

## `maxRequestRetries` (type: `integer`):

Number of retries before a failed request is given up.

## `proxy` (type: `object`):

Select proxies to be used by your crawler.

## Actor input object example

```json
{
  "startUrls": [
    "https://bankoflamps.com/luminaires"
  ],
  "monitoringMode": false,
  "resetMonitoringState": false,
  "maxItems": 100,
  "maxConcurrency": 10,
  "minConcurrency": 1,
  "maxRequestRetries": 5,
  "proxy": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (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 = {
    "startUrls": [
        "https://bankoflamps.com/luminaires"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/bankoflamps-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": ["https://bankoflamps.com/luminaires"] }

# Run the Actor and wait for it to finish
run = client.actor("memo23/bankoflamps-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": [
    "https://bankoflamps.com/luminaires"
  ]
}' |
apify call memo23/bankoflamps-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,memo23/bankoflamps-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/N4xTagcpCrYncKekR/builds/83ee5a0aLngRIa0J1/openapi.json
