# Mechanical Keyboard Deals (`shiftyllama/mk-deals-clean`) Actor

Daily deals on mechanical keyboards, keycaps, switches & accessories scraped from top stores worldwide.

- **URL**: https://apify.com/shiftyllama/mk-deals-clean.md
- **Developed by:** [Jason Remnant](https://apify.com/shiftyllama) (community)
- **Categories:** E-commerce, Other
- **Stats:** 2 total users, 0 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. 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?

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

## Mechanical Keyboard Deals Scraper

**Find clearance and sale deals across the best mechanical keyboard stores** — keyboards, keycaps, switches, and accessories at a discount. HTTP-only Shopify API scraping means it's fast, cheap, and doesn't need Playwright.

[![Apify Store](https://img.shields.io/badge/Apify-Store-blue)](https://apify.com/store)
[![Python](https://img.shields.io/badge/Python-3.11-blue)](https://python.org)

### Why this scraper?

Mechanical keyboards are expensive. Enthusiasts constantly hunt for deals across 10+ fragmented stores. No cross-store deal aggregator exists — **this is the first**.

#### Key advantages

- **HTTP-only API scraping** — Uses Shopify's native JSON API. Much faster and cheaper than Playwright-based scrapers.
- **6 stores out of the box** — NovelKeys (71 clearance), CannonKeys (21 clearance), Divinikey (246 BFCM sale), KBDfans (26 clearance), Daily Clack AU (16 clearance), SwitchKeys AU + more coming.
- **Real deals only** — Automatically filters out items with no actual discount (compare\_at\_price must exist and be higher than current price).
- **Sorted by discount** — Results are sorted with the biggest discounts first.
- **Australian stores included** — Daily Clack and SwitchKeys for AU-based deals (import savings).
- **Structured output** — Clean JSON with title, price, discount %, product type, inventory status, store, and image URL.
- **Summary report** — Aggregated breakdown by store and product category, plus average/max discount stats.

### Pricing

| Volume | Price |
|--------|-------|
| 500 deals | $1.00 |
| 1,000 deals | $2.00 |
| 5,000 deals | $5.00 |
| 10,000+ deals | Custom |

Pay only for what you scrape. No monthly subscription. Runs are fast (typically <30 seconds).

### How to use

#### Quick start (no coding needed)

1. Select which stores you want to scrape
2. Optionally set a minimum discount filter (e.g. 30%+ off)
3. Run the Actor
4. Download results as JSON, CSV, Excel, or XML

#### Input parameters

| Field | Type | Description | Default |
|-------|------|-------------|---------|
| `stores` | `array` | Which stores to scrape (multiselect) | All 6 Shopify stores |
| `include_mk_com` | `boolean` | Also scrape MechanicalKeyboards.com (HTML-based, slower) | `false` |
| `min_discount_percent` | `integer` | Min discount % to include (e.g. 30 = 30%+ off). 0 = all deals. | `0` |
| `max_results` | `integer` | Max deals to return. Sorted by biggest discount first. | `500` |
| `output_aggregated` | `boolean` | Include a summary report item with store/category breakdown | `true` |

#### Example input

```json
{
  "stores": ["NovelKeys", "CannonKeys", "KBDfans", "Daily Clack"],
  "min_discount_percent": 30,
  "max_results": 200,
  "output_aggregated": true
}
```

### Output fields

Each deal returns:

| Field | Description | Example |
|-------|-------------|---------|
| `store_name` | Source store | "NovelKeys" |
| `title` | Product title | "GMK Olivia++ Base Kit" |
| `variant` | Variant name (e.g. size/color) | "Standard Base" |
| `product_type` | Category: keycaps, switches, keyboards, accessories | "keycaps" |
| `brand` | Manufacturer/brand | "GMK" |
| `price` | Current sale price (USD) | 95.00 |
| `compare_at_price` | Original price (USD) | 180.00 |
| `discount_percent` | Discount percentage | 47.2 |
| `in_stock` | Whether the item is available | true |
| `currency` | Currency code | "USD" |
| `image_url` | Product image URL | "https://cdn.shopify.com/..." |
| `product_url` | Link to the product page | "https://novelkeys.com/products/..." |
| `scraped_at` | When the deal was scraped | "2026-07-05T..." |
| `tags` | Product tags from store | \["clearance", "nonreturnable"] |

#### Summary report item

A separate dataset item with `_type: "summary"` containing:

| Field | Description |
|-------|-------------|
| `total_deals` | Total number of deals found |
| `store_breakdown` | Count of deals per store |
| `category_breakdown` | Count of deals per product category |
| `avg_discount` | Average discount percentage across all deals |
| `max_discount` | Maximum discount percentage found |

### Sample output

```json
{
  "store_name": "NovelKeys",
  "title": "wilba.tech Salvation PCB",
  "variant": "WT60-D (Soldered)",
  "product_type": "keyboards",
  "brand": "NovelKeys, LLC",
  "price": 30.0,
  "compare_at_price": 60.0,
  "discount_percent": 50.0,
  "in_stock": true,
  "currency": "USD",
  "image_url": "https://cdn.shopify.com/...",
  "product_url": "https://novelkeys.com/products/salvation-pcb",
  "scraped_at": "2026-07-05T10:00:00+00:00",
  "tags": ["nonreturnable"]
}
```

### Use cases

- **Hobbyist deal hunting** — Get notified when your favourite switch or keycap set goes on sale across multiple stores
- **Price monitoring** — Track discount trends over time by running this Actor on a schedule
- **Discord/Telegram alerts** — Pipe deal data into a bot to alert your community about new markdowns
- **Market research** — Understand pricing patterns across the mechanical keyboard industry
- **Group buy leftovers** — Find unsold group buy extras being cleared at a discount

### Stores currently supported

| Store | Country | Method | Products |
|-------|---------|--------|----------|
| [NovelKeys](https://novelkeys.com/collections/clearance) | US | Shopify JSON | ~71 clearance |
| [CannonKeys](https://cannonkeys.com/collections/clearance) | US | Shopify JSON | ~21 clearance |
| [Divinikey](https://divinikey.com/collections/bfcm-sale) | US | Shopify JSON | ~246 BFCM sale |
| [KBDfans](https://kbd.fans/collections/clearance) | CN | Shopify JSON | ~26 clearance |
| [Daily Clack](https://dailyclack.com/collections/clearance-items) | AU | Shopify JSON | ~16 clearance |
| [SwitchKeys](https://www.switchkeys.com.au/) | AU | Shopify JSON | Discount detection |
| MechanicalKeyboards.com (optional) | US | HTML | HTML scraping |

### Roadmap

- \[ ] More AU stores (MechStock, Keebz N Cables, Keyboard Treehouse)
- \[ ] More US stores (Kinetic Labs, Drop, PrimeKB, ClickClack)
- \[ ] Price history tracking across runs
- \[ ] Email/Discord notification integration
- \[ ] Keycap profile + switch type detection from product descriptions
- \[ ] FILCO / Realforce / Topre deal detection
- \[ ] Amazon mechanical keyboard deals

### FAQ

**Does this need Apify proxy?**
No — Shopify's JSON API is public and doesn't block. MechanicalKeyboards.com (optional HTML mode) may need a residential proxy.

**How often should I run it?**
Daily is good. Clearance stock changes infrequently. During BFCM / holiday sales, run more often.

**Can I add my own store?**
Yes — the stores are defined in the `STORES` list in `src/main.py`. Most keyboard stores use Shopify and expose the same `/collections/{handle}/products.json` endpoint.

**I found a bug / a store changed its site!**
Open an issue on the Apify Store page. I check regularly and push fixes fast.

### Support

Open an issue on the Apify Store page, or contact me through the Apify developer profile.

***

*Built with Python + httpx for Apify. Not affiliated with any of the stores listed above.*

# Actor input Schema

## `stores` (type: `array`):

Select which keyboard stores to scrape for deals. All selected by default.

## `include_mk_com` (type: `boolean`):

Also scrape MechanicalKeyboards.com (custom site, slower — HTML-based scraping).

## `min_discount_percent` (type: `integer`):

Only show deals with at least this much discount (e.g. 30 = 30% off or more). Set to 0 for all deals.

## `max_results` (type: `integer`):

Maximum number of deals to return (sorted by discount descending). Leave empty for all.

## `output_aggregated` (type: `boolean`):

Include an aggregated summary as a separate dataset item showing store breakdown, category breakdown, and average discount.

## Actor input object example

```json
{
  "stores": [
    "NovelKeys",
    "CannonKeys",
    "Divinikey",
    "KBDfans",
    "Daily Clack",
    "SwitchKeys"
  ],
  "include_mk_com": false,
  "min_discount_percent": 0,
  "max_results": 500,
  "output_aggregated": true
}
```

# 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 = {
    "stores": [
        "NovelKeys",
        "CannonKeys",
        "Divinikey",
        "KBDfans",
        "Daily Clack",
        "SwitchKeys"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("shiftyllama/mk-deals-clean").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 = { "stores": [
        "NovelKeys",
        "CannonKeys",
        "Divinikey",
        "KBDfans",
        "Daily Clack",
        "SwitchKeys",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("shiftyllama/mk-deals-clean").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 '{
  "stores": [
    "NovelKeys",
    "CannonKeys",
    "Divinikey",
    "KBDfans",
    "Daily Clack",
    "SwitchKeys"
  ]
}' |
apify call shiftyllama/mk-deals-clean --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,shiftyllama/mk-deals-clean"
        }
    }
}

```

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/MsnWEjTfwD7tIGuph/builds/pMBzAfOh9dAycc0gd/openapi.json
