# Cloud Ninjas RAM Price & Stock Monitor (`maydit/ram-price-stock-monitor`) Actor

Track Cloud Ninjas RAM offers, restocks and price changes. Normalize DDR4/DDR5, ECC, kit capacity and cost per GB with explicit source-conflict warnings.

- **URL**: https://apify.com/maydit/ram-price-stock-monitor.md
- **Developed by:** [Brandt May](https://apify.com/maydit) (community)
- **Categories:** E-commerce
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 saved observations

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

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

## Cloud Ninjas RAM Price & Stock Monitor

Turn Cloud Ninjas RAM listings into a sourcing sheet with advertised price, ordering availability, SKU, DDR generation, ECC type, module construction, kit capacity and cost per GB. Repeat the same watchlist to record price changes and restocks.

Built for server refurbishers, IT asset resellers, repair shops and purchasing teams. A RAM kit is only a useful comparison when you know what is inside it: 768 GB across 24 modules is different from one 768 GB module. This Actor separates those fields and preserves contradictory seller specifications as visible warnings.

### Start with a useful result

Click **Start** with the default input. The Actor reads the first three products in Cloud Ninjas' branded-memory collection, checks storefront currency, and saves their RAM variants. No API key, login, cookies or proxy is required.

Open **Output → Saved observations** for JSON, export CSV from the dataset, or open the readable run report for coverage and history status. Supply exact product URLs for a stable purchasing watchlist; use collection URLs for bounded discovery.

```json
{
  "startUrls": ["https://cloudninjas.com/collections/branded-memory-ram-modules-collection"],
  "productsPerSource": 3,
  "maxResults": 5
}
```

### Three practical recipes

#### 1. DDR5 module sourcing

Read the first three branded products and compare their prices, module types and merchant-supplied specifications. Out-of-stock variants are retained so you can see what is unavailable.

```json
{"startUrls":["https://cloudninjas.com/collections/branded-memory-ram-modules-collection"],"productsPerSource":3,"maxResults":5}
```

#### 2. Currently orderable DDR5 offers

Inspect five products, then retain matching in-stock DDR5 variants. Filtering happens inside the selected window; the Actor does not scan the entire catalog to fill the result cap.

```json
{"startUrls":["https://cloudninjas.com/collections/branded-memory-ram-modules-collection"],"productsPerSource":5,"availability":"in_stock","memoryGeneration":"DDR5","maxResults":5}
```

#### 3. Refurbished server kit pricing

Observe an exact DDR4 kit listing. Use `comparePrevious: true` on repeated runs to see price changes. Keep `availability: "all"` when you need restock and sold-out transitions; filtering out one state prevents that state from entering your baseline.

```json
{"startUrls":["https://cloudninjas.com/products/dell-768gb-24x32gb-ddr4-pc4-3200aa-pc4-25600-ecc-registered-server-memory-upgrade-kit"],"maxResults":5}
```

### What each result means

| Field | Meaning |
| --- | --- |
| `recordId`, `productId`, `variantId`, `sku` | Stable merchant product/variant identity and the supplied SKU. One saved row represents one variant observation. |
| `price`, `currency`, `compareAtPrice` | Public advertised USD offer and optional merchant comparison price. Currency must be explicitly verified from the same storefront; an unlabeled price is rejected. |
| `inStock`, `stockQuantity` | Merchant ordering availability. Physical quantity remains `null`; this Actor does not query carts or infer units on hand. |
| `memoryGeneration`, `moduleType`, `eccType` | DDR3/DDR4/DDR5; RDIMM/LRDIMM/UDIMM/SODIMM; ECC, non-ECC or on-die ECC only. Missing or conflicting values remain `null`. |
| `totalCapacityGB`, `moduleCount`, `moduleCapacityGB` | Total kit capacity, module count and capacity per module. TB labels use 1,024 GB for normalization. Unknown counts are not guessed. |
| `pricePerGB`, `pricePerModule` | Advertised offer divided by unambiguous total capacity or module count, rounded to six decimals. These exclude shipping, tax and resale costs. |
| `speedMTs`, `rank`, `condition` | Normalized merchant speed label, rank and stated condition. The merchant sometimes labels DDR transfer rates “MHz”; this is stored as the advertised effective rate, not measured clock speed. |
| `manufacturer`, `manufacturerPartNumber`, `vendor` | Explicit brand/MPN specification fields, separate from the Shopify vendor. A vendor named Cloud Ninjas is not assumed to manufacture the module. |
| `specWarnings`, `specEvidence` | Conflicts and supporting title, variant, specification lines and relevant tags. Review warnings before purchasing. |
| `priceChange`, `priceChangePercent`, `restocked`, `soldOut` | Changes against this workflow's retained prior observation, not market-wide or historical sales data. |
| `comparisonStatus`, `changedFields`, `previous` | `not_requested`, `new`, `changed` or `unchanged`, with prior values when available. |
| `observedAt`, `sourceUpdatedAt`, `sourceUrl`, `sourcePage`, `url` | Observation timestamp, optional merchant update timestamp and exact public evidence links. Merchant update time is not proof that its inventory was physically counted then. |

The storefront currently contains real inconsistencies. For example, a module's title/tags can say 5600 while its description says 4800. This Actor reports `speedMTs: null` with `conflicting_speedMTs`. It does not choose the most attractive specification or invent a compatibility verdict.

### Inputs and coverage

Accepts up to three plain Cloud Ninjas `/products/handle` or `/collections/handle` URLs. Other stores, query parameters, tagged collection paths, private pages and authentication are unsupported. `productsPerSource` selects 1–25 products from the first collection page in source order; it is ignored for exact product URLs. All variants returned for those products are inspected. A response at the public 250-variant ceiling is rejected because completeness cannot be established.

`maxResults` caps saved observations at 1–100. Default 5; the default source window currently produces three rows. `availability`, `memoryGeneration` and `moduleType` filter inside the chosen product window. A valid filter may produce zero results. Unknown normalized specifications do not match a specific filter. Non-RAM products are excluded and counted in `SUMMARY`.

Requests are spaced at least one second apart with a shared 30–120 second time budget. Public robots rules are checked, redirects are rejected, and HTTP 401, 403 or 429 stops subsequent requests. There are no proxies, block workarounds, account sessions or cart requests.

`SUMMARY` records requested sources, selected products, observed variants, filters, saved rows, unreached sources, failures, limits and whether comparison history was updated. Collection windows are never labeled complete catalog coverage. A successful platform run can still have partial coverage; inspect `SUMMARY.incomplete`.

### Reliable comparison history

Enable `comparePrevious` to retain complete observations, or `onlyChanges` to save only new or changed variants. A complete unchanged repeat may save zero rows and incur no dataset-item charge. History is separated by `snapshotName`, URLs, product-window size and filters.

History updates only after every selected source and matching variant has been handled successfully. Failed sources, a time limit, charge limit, an output cap that drops selected rows, or unreached URLs leave the prior baseline intact. An output cap exactly equal to the number of matching rows is complete. Concurrent writers that alter the same history are detected before overwrite.

Rows no longer present in a limited source window are not declared deleted, sold out or removed. Prior identities are retained, up to 20,000 per history scope. Use a new snapshot name or narrower watchlist if that limit is reached. Do not overlap runs that write the same history scope.

### Pricing

**$0.002 per saved variant observation, plus $0.00005 per run start.** A three-row run costs $0.00605 in these events. A five-row run costs $0.01005. Billing follows saved dataset rows, not downloaded pages; duplicate observations and unchanged rows skipped by `onlyChanges` create no dataset-item charge. The run-start event still applies. Configure **Max total charge** in Apify run options for an additional cap. Your platform plan can have separate usage charges.

### Scope and buying decisions

This independent Actor currently covers **Cloud Ninjas only**. It is not affiliated with Cloud Ninjas or RAM manufacturers. Public offers are not completed sales, guaranteed fulfillment, wholesale quotes, guaranteed compatibility or resale-profit predictions. Merchant descriptions can contain mistakes. Check your exact motherboard/server requirements and confirm seller details before purchasing. Combine these observations with your own selling prices, condition, shipping, tax, warranty, return and platform-fee assumptions.

Sources: [Cloud Ninjas branded RAM collection](https://cloudninjas.com/collections/branded-memory-ram-modules-collection), [Cloud Ninjas terms](https://cloudninjas.com/policies/terms-of-service), [public robots rules](https://cloudninjas.com/robots.txt), [Shopify public Product API](https://shopify.dev/docs/api/ajax/reference/product). Product/collection responses can change; unsupported or malformed responses fail explicitly instead of generating substitute data.

For source changes or additional memory fields, open an issue on the Actor page with the affected public URL and a non-secret example input. [Explore Mayd It data workflows](https://mayd-it.com/data-tools/workflows/).

# Actor input Schema

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

Up to 3 plain https://cloudninjas.com/collections/handle or /products/handle URLs. Empty uses the branded RAM collection. No query strings, tagged collection paths, other shops, credentials or proxies.

## `productsPerSource` (type: `integer`):

Read this many products from the first public collection page, then inspect all returned variants. Filters apply inside this window; they do not scan the whole catalog. Product URLs ignore this field.

## `availability` (type: `string`):

Merchant availability is an ordering flag, not a physical inventory count.

## `memoryGeneration` (type: `string`):

Filter normalized generation inside the selected window. Ambiguous values do not match a specific generation.

## `moduleType` (type: `string`):

Keep registered, load-reduced, unbuffered and small-outline DIMMs distinct. This is not a system-compatibility check.

## `maxResults` (type: `integer`):

Hard cap on saved billable rows. A partially saved window does not advance history.

## `comparePrevious` (type: `boolean`):

Save a baseline only after all selected source windows succeed and all matching variants are handled. Produce price changes, restocked/soldOut and previous values. Missing listings are never inferred removed.

## `onlyChanges` (type: `boolean`):

Enables comparison. An unchanged complete repeat can save zero rows with no dataset-item charge.

## `snapshotName` (type: `string`):

1–80 letters, numbers, underscores or hyphens. History is also separated by the URL list, selected window and filters.

## `maxRunSeconds` (type: `integer`):

Bounded public requests, at least one second apart. Access refusal or rate limiting stops requests without bypass. Use fewer source URLs for faster runs.

## Actor input object example

```json
{
  "startUrls": [
    "https://cloudninjas.com/collections/branded-memory-ram-modules-collection"
  ],
  "productsPerSource": 3,
  "availability": "all",
  "memoryGeneration": "all",
  "moduleType": "all",
  "maxResults": 5,
  "comparePrevious": false,
  "onlyChanges": false,
  "snapshotName": "default",
  "maxRunSeconds": 90
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `summary` (type: `string`):

No description

## `report` (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://cloudninjas.com/collections/branded-memory-ram-modules-collection"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maydit/ram-price-stock-monitor").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://cloudninjas.com/collections/branded-memory-ram-modules-collection"] }

# Run the Actor and wait for it to finish
run = client.actor("maydit/ram-price-stock-monitor").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://cloudninjas.com/collections/branded-memory-ram-modules-collection"
  ]
}' |
apify call maydit/ram-price-stock-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maydit/ram-price-stock-monitor"
        }
    }
}
```

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/nBTypFU71H434goMi/builds/tej7UGxMNOiXQpYrz/openapi.json
