# MarketMage Notebook: Saved Ideas & Seller Profile (`cleanpull/marketmage-notebook`) Actor

The memory of the MarketMage plugin for Claude, kept in your own Apify account: the product ideas you checked with their verdicts and key numbers, versions tried, decisions, linked watchlists, and your seller profile as bands only. Read, export or delete it any time.

- **URL**: https://apify.com/cleanpull/marketmage-notebook.md
- **Developed by:** [CleanPull](https://apify.com/cleanpull) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.20 / 1,000 notebook reads

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

## MarketMage Notebook: Saved Ideas & Seller Profile

The memory of the **MarketMage plugin for Claude**, kept in **your own Apify account**. With it, Claude remembers across chats the **product ideas you checked** (verdict, confidence, key numbers, date), the **versions you tried**, the **decisions you made**, the **watchlists linked to an idea**, and your **seller profile as bands only** (budget band, how you fulfil, experience, channels, goal, country). You can read it, export it and delete it at any time. Reading or writing it costs a fraction of a cent.

### What you can do with it

- **"Remember my situation."** Claude saves your profile as bands ("$1k–5k · own stock · first time · US"), so the next chat's "For you" advice starts from it without asking again.
- **"What did I check last month?"** Claude reads the ideas with their verdicts and numbers, newest first, or only those matching a word.
- **"I tried the steel version; I'll test it."** A variation and a decision are linked to the idea they belong to.
- **"Which watchlist was for the garlic press?"** Watch links keep the task and schedule ids next to the idea.
- **"Forget all of it."** One run deletes the whole notebook and its store.

### What makes it safe to keep

- **In your account, nobody else's.** The notebook lives in a key-value store named `marketmage-notebook` in your own Apify account (Storage → Key-value stores): one record per entry, one for the profile, and a small `meta-v2` record. CleanPull never sees it. The actor runs with limited permissions, so it can open only that store and its own run storages.
- **Bands, never amounts.** The profile takes bands only. A budget given as an amount ("about $2,000") is turned into its band (`1k-5k`) and the amount is not stored. Any other profile field holding an amount is refused.
- **No personal or secret data.** Every field is checked: email addresses, phone numbers, card and bank numbers, API keys, tokens, passwords, and amounts of your own money in free text ("my budget is $3k", "I have 2k to spend") are refused, while market figures ("leaders sell at $19.99") are kept, and the refusal names the field without repeating the value.
- **Only the schema.** A field outside the schema below is refused, not silently stored. Free text is capped (a note at 300 characters, an idea label at 80), and the whole notebook at 500 entries and 200 KB.
- **Nothing half-saved.** A refused input saves nothing and says why in the result row. Every write is checked first, then saved in one piece.
- **Versioned.** The `meta-v2` record names the schema version (now 2). Later builds read older notebooks, serving anything readable and naming what is not; a build never writes to a notebook from a newer one.
- **Only on request.** The notebook is created only by a write with `startNotebook: true`, which Claude sends once you ask it to keep a notebook. Any other write on an account without one saves nothing, so a deleted notebook is not brought back by a late call (a run may leave an empty store, which holds nothing).

### Input

| Field                               | What it does                                                                                               |
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------- |
| `operation`                         | `read` (default), `upsertProfile`, `addEntry`, `updateEntry`, `deleteEntry` or `deleteAll`                 |
| `profile`                           | For `upsertProfile`: the fields to set, as bands; `null` clears one                                        |
| `entry`                             | For `addEntry` (a whole entry) and `updateEntry` (only the fields to change; `null` clears one)            |
| `entryId`, `deleteLinked`           | For `updateEntry` and `deleteEntry`; deleting an idea also deletes its linked entries unless turned off    |
| `kinds`, `search`, `since`, `limit` | For `read`: only some kinds, entries mentioning a word (with their linked entries), from a date, at most N |
| `detail`                            | For `read`: `compact` (default) or `full` (with created and changed times, for an export)                  |
| `confirmDeleteAll`                  | For `deleteAll`: must be `true`                                                                            |

#### The profile

| Field           | Values                                                                             |
| --------------- | ---------------------------------------------------------------------------------- |
| `budgetBand`    | `under-1k`, `1k-5k`, `5k-25k`, `25k-plus` (an amount is turned into its band)      |
| `fulfilment`    | `dropship`, `own-stock`, `fba`, `print-on-demand`                                  |
| `experience`    | `none`, `some`, `brand`                                                            |
| `channels`      | any of `none`, `store`, `amazon`, `tiktok`, `etsy`, `ebay`, `email-list`, `social` |
| `goal`          | `side-income`, `brand`                                                             |
| `marketCountry` | a two-letter country code: `US`, `GB`, `DE`…                                       |
| `skills`        | optional: any of `ads`, `content`, `sourcing`, `design`                            |
| `timePerWeek`   | optional: `under-5h`, `5-15h`, `15-30h`, `30h-plus` (hours are turned into a band) |

#### Entries

| Field                   | On                                             | What it holds                                                                                                      |
| ----------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `kind`                  | all                                            | `idea`, `variation`, `decision` or `watch`                                                                         |
| `idea`                  | all                                            | A short label, up to 80 characters; linked entries take their parent's                                             |
| `parentId`              | variation, watch (needed); decision (optional) | The idea (or variation) it belongs to                                                                              |
| `date`                  | all                                            | `YYYY-MM-DD`, default today                                                                                        |
| `marketplace`           | all                                            | For example `amazon.com`                                                                                           |
| `verdict`, `confidence` | idea, variation                                | `go`, `maybe`, `no`; `high`, `medium`, `low`                                                                       |
| `signals`               | idea, variation                                | Counts: `{ "favours", "against", "neutral", "unknown" }`                                                           |
| `keyNumbers`            | idea, variation                                | Up to 10 market metrics, numbers only, from a fixed list (`priceMedian`, `medianReviews`, `moq`, `marginPercent`…) |
| `change`                | variation (needed)                             | What differs from the idea, up to 120 characters                                                                   |
| `decision`              | decision (needed)                              | `pursue`, `test`, `park` or `drop`                                                                                 |
| `watch`                 | watch (needed)                                 | `{ "actor": "cleanpull/…", "taskId", "scheduleId", "label" }`                                                      |
| `note`                  | all                                            | Up to 300 characters                                                                                               |

The same idea noted twice on the same day (a retried call, or a recheck) updates the first entry instead of adding a second.

#### Example inputs

Save the profile:

```json
{
  "operation": "upsertProfile",
  "profile": {
    "budgetBand": "1k-5k",
    "fulfilment": "own-stock",
    "experience": "none",
    "channels": ["none"],
    "goal": "side-income",
    "marketCountry": "US"
  }
}
```

Note an idea checked:

```json
{
  "operation": "addEntry",
  "entry": {
    "kind": "idea",
    "idea": "Silicone garlic press",
    "marketplace": "amazon.com",
    "verdict": "maybe",
    "confidence": "high",
    "signals": { "favours": 3, "against": 2, "neutral": 1, "unknown": 0 },
    "keyNumbers": { "priceMedian": 14.99, "medianReviews": 1850 },
    "note": "Paid pressure is the main risk."
  }
}
```

Read what was noted about it:

```json
{ "operation": "read", "search": "garlic" }
```

### Output

One row, with the answer first:

```json
{
  "operation": "addEntry",
  "ok": true,
  "message": "Added idea i-yysyr5.",
  "error": null,
  "schemaVersion": 2,
  "counts": { "idea": 1, "variation": 0, "decision": 0, "watch": 0 },
  "entry": {
    "id": "i-yysyr5",
    "kind": "idea",
    "idea": "Silicone garlic press",
    "verdict": "maybe",
    "date": "2026-10-23"
  },
  "sizeBytes": 683,
  "sizeLimitBytes": 200000,
  "store": {
    "name": "marketmage-notebook",
    "id": "…",
    "consoleUrl": "https://console.apify.com/storage/key-value-stores/…"
  }
}
```

A read adds `profile`, `entries` (newest first), `matched`, `returned` and `more`. A refused input has `"ok": false` and the reason in `error`, for example `entry.note looks like it holds an email address`. To export everything, read with `detail: "full"` and a high `limit`, or download the records from the store.

### Pricing

| Event                                                              | Price           |
| ------------------------------------------------------------------ | --------------- |
| Notebook read (a read of a non-empty notebook)                     | $0.10 per 1,000 |
| Entries read (each entry record read beyond the first 20 of a run) | $0.01 per 1,000 |
| Notebook write (a profile or entry saved, changed or deleted)      | $0.30 per 1,000 |

Each run also has a $0.00005 start fee. Reading an empty notebook, deleting the whole notebook and refused inputs cost only the start. A write is charged only once it is saved. Most reads stay within the first 20 entries; a search of a full 500-entry notebook adds about half a cent.

### Limits

- **One notebook per Apify account.** Each entry is its own record, so changes from several chats at once never overwrite each other. Two changes to the same entry or to the profile at the same moment: the later one wins.
- **500 entries and 200 KB.** Delete old ideas to make room; a full notebook says so instead of dropping anything.
- **The notebook belongs to this actor.** Other actors (with limited permissions) cannot open it. You can always open, download or delete it yourself in Storage.
- **Nothing found**: a read of an empty or deleted notebook, or a refused input or operation, still succeeds: the one row has `ok` and a `message` or `error` saying what happened, and nothing is charged.

### Changelog

- **0.1** (October 2026): profile in bands, ideas, variations, decisions and watch links; read with filters; delete one entry or everything; personal and secret data refused.

# Actor input Schema

## `operation` (type: `string`):

What to do. read returns the profile and the entries in one dataset row; every other operation returns the row with what changed. A refused input saves nothing and says why in the row's error.

## `profile` (type: `object`):

Bands only. budgetBand: under-1k, 1k-5k, 5k-25k or 25k-plus (an exact amount is turned into its band and never stored). fulfilment: dropship, own-stock, fba or print-on-demand. experience: none, some or brand. channels: any of none, store, amazon, tiktok, etsy, ebay, email-list, social. goal: side-income or brand. marketCountry: a two-letter code (US, GB, DE…). Optional: skills (any of ads, content, sourcing, design) and timePerWeek (under-5h, 5-15h, 15-30h, 30h-plus). Fields given replace the stored ones; null clears one. Any other field, an amount of money, an email, a phone number or a key is refused.

## `entry` (type: `object`):

kind: idea (a product idea checked), variation (a version of an idea tried: needs parentId and change), decision (what the user decided: decision is pursue, test, park or drop) or watch (a watchlist linked to an idea: needs parentId and watch {actor, taskId, scheduleId, label}). idea: a short label, up to 80 characters; a linked entry (with parentId) keeps none and shows its parent's. Optional: date (YYYY-MM-DD, default today; it cannot change later), marketplace (e.g. amazon.com), verdict (go, maybe, no), confidence (high, medium, low), signals {favours, against, neutral, unknown} as counts, keyNumbers (up to 10 market metrics, numbers only, from: priceMedian, priceP25, priceP75, priceMin, priceMax, medianReviews, under100Reviews, top3SellerShare, sellerCount, listingCount, sponsored, boughtPastMonth, unitsPerMonthMin, unitsPerMonthMax, revenuePerMonthMin, revenuePerMonthMax, bestSellersRank, unitPrice, landedCost, moq, marginPercent, feePercent, trendChangePercent, trendIndex, rating, oneStarShare, tiktokSalesPerDay, adCount; e.g. {"priceMedian": 24.99, "medianReviews": 1200}), note (up to 300 characters). For updateEntry give only the fields to change (null clears one). Fields outside this list, emails, phone numbers and keys are refused.

## `entryId` (type: `string`):

For updateEntry and deleteEntry: the entry's id as read returns it, for example i-3k9x2a.

## `deleteLinked` (type: `boolean`):

For deleteEntry: also delete the variations, decisions and watch links of the entry. Off: an entry with linked entries is not deleted.

## `kinds` (type: `array`):

For read: only these kinds of entry. Empty: all.

## `search` (type: `string`):

For read: only entries whose idea, change, note, marketplace or watch label contains this text (any case), with the entries linked to them.

## `since` (type: `string`):

For read: only entries dated on or after this day (YYYY-MM-DD).

## `limit` (type: `integer`):

For read: at most this many entries, newest first. The row says how many matched.

## `detail` (type: `string`):

For read: compact leaves out the createdAt and updatedAt times; full keeps them (for an export).

## `startNotebook` (type: `boolean`):

Set to true only on the first write after the user asked to keep a notebook: it creates the notebook in their account. Without it, a write to an account with no notebook (never started, or deleted) saves nothing, so a deleted notebook is never brought back by a late or retried call.

## `confirmDeleteAll` (type: `boolean`):

For deleteAll: must be true. Deletes the profile, every entry and the store itself, for good.

## Actor input object example

```json
{
  "operation": "read",
  "profile": {
    "budgetBand": "1k-5k",
    "fulfilment": "own-stock",
    "experience": "none",
    "channels": [
      "none"
    ],
    "goal": "side-income",
    "marketCountry": "US"
  },
  "deleteLinked": true,
  "kinds": [],
  "limit": 50,
  "detail": "compact",
  "startNotebook": false,
  "confirmDeleteAll": false
}
```

# Actor output Schema

## `results` (type: `string`):

One row: ok, the message or the reason it was refused, and the profile and entries read or changed.

## `summary` (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 = {
    "profile": {
        "budgetBand": "1k-5k",
        "fulfilment": "own-stock",
        "experience": "none",
        "channels": [
            "none"
        ],
        "goal": "side-income",
        "marketCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("cleanpull/marketmage-notebook").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 = { "profile": {
        "budgetBand": "1k-5k",
        "fulfilment": "own-stock",
        "experience": "none",
        "channels": ["none"],
        "goal": "side-income",
        "marketCountry": "US",
    } }

# Run the Actor and wait for it to finish
run = client.actor("cleanpull/marketmage-notebook").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 '{
  "profile": {
    "budgetBand": "1k-5k",
    "fulfilment": "own-stock",
    "experience": "none",
    "channels": [
      "none"
    ],
    "goal": "side-income",
    "marketCountry": "US"
  }
}' |
apify call cleanpull/marketmage-notebook --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cleanpull/marketmage-notebook"
        }
    }
}
```

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/3KgfI8xc1VamiD0Rk/builds/7FQK3Kefh3gwBSzvJ/openapi.json
