agentsclimarketplace

Notion cost tuning

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/notion-pack/skills/notion-cost-tuning

425 plugins, 2,810 skills, 200 agents for Claude Code. Open-source marketplace at tonsofskills.com with the ccpi CLI package manager.

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill notion-cost-tuning

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

Optimize Notion API usage to minimize rate-limit pressure, reduce engineering overhead, and maximize throughput. Use when auditing request volume, eliminating redundant API calls, implementing caching, or restructuring queries for efficiency. Trigger with "notion cost", "notion optimize", "notion API usage", "reduce notion requests", "notion rate limit budget", "notion efficient", "notion caching".

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

8.3 KB, as published. Nobody here has run it

Notion Cost Tuning

Overview

The Notion API is free with every workspace plan — there is no per-call pricing. The real "cost" is the 3 requests/second rate limit (per integration token) plus the engineering time wasted on inefficient patterns. Apply the six strategies below to reduce request volume by 80-95%.

Notion workspace pricing (for context — API access is included at every tier):

PlanPriceAPI AccessRate Limit
Free$0Full API3 req/sec
Plus$12/user/moFull API3 req/sec
Business$28/user/moFull API3 req/sec
EnterpriseCustomFull API3 req/sec

The rate limit is identical across all plans. Optimization is about staying within 3 req/sec, not reducing a bill.

Prerequisites

  • @notionhq/client v2.x installed (npm install @notionhq/client)
  • Integration token from notion.so/my-integrations
  • Token shared with target pages/databases via the Connections menu in Notion
  • For queue patterns: p-queue v8+ (npm install p-queue)
  • For caching: node-cache or lru-cache (npm install lru-cache)

Authentication

Every technique here uses the same auth as any Notion integration: a single bearer token passed to the client constructor. Store it in an environment variable (NOTION_TOKEN) — never hardcode it — and share it with each target page/database through the Connections menu. One token = one 3 req/sec budget; provision a separate integration token per service when you need independent budgets.

const notion = new Client({ auth: process.env.NOTION_TOKEN });

Instructions

Work the three steps in order. Each links to the full, copy-paste-ready code in references/implementation.md; the essentials are inlined below so you can follow the flow from here.

Step 1: Audit current request volume

Measure before optimizing. Wrap every Notion call in a tracker that records method, endpoint, timestamp, and duration, then print a per-minute report to find the operations eating > 50% of your budget (usually polling loops, redundant retrieves, and full-table scans).

async function tracked<T>(method: string, fn: () => Promise<T>): Promise<T> {
  const start = Date.now();
  try {
    return await fn();
  } finally {
    requestLog.push({ method, timestamp: start, durationMs: Date.now() - start });
  }
}

Full tracker + auditReport(): references/implementation.md, § Step 1.

Step 2: Eliminate redundant reads and shrink payloads

Three patterns cut reads immediately: (A) stop calling pages.retrieve on pages you already got from databases.query — the properties are already there; (B) pass filter_properties (property IDs, not names) to shrink responses 60-90%; (C) filter on last_edited_time to fetch only what changed since the last sync instead of re-scanning the whole database.

// Redundant: query already returned every property — skip the retrieve
const { results } = await notion.databases.query({ database_id: dbId });
for (const page of results) processPage(page); // 1 request total, not 1 + N

All three patterns with runnable code: references/implementation.md, § Step 2.

Step 3: Cache, batch, and replace polling

Cache read-heavy data with a TTL'd LRU; throttle writes through a p-queue sized to the 3 req/sec limit; batch up to 100 blocks per blocks.children.append; and replace polling loops with webhooks (a 10-second poll costs 360 requests/hour per database — webhooks cost zero).

// Rate-limited write queue — never exceeds 3 req/sec
const queue = new PQueue({ concurrency: 3, interval: 1000, intervalCap: 3 });

Caching, batching, and the webhook handler: references/implementation.md, § Step 3.

Output

After applying these optimizations:

  • Audit report showing request volume baseline and hotspots
  • Redundant reads eliminated — no duplicate pages.retrieve after databases.query
  • Payload sizes reduced 60-90% via filter_properties
  • Incremental sync via last_edited_time filter replacing full-table scans
  • Cache layer with TTL-based invalidation for reads
  • Write throughput maximized via queue-based throttling and block batching
  • Polling eliminated where webhooks are available (360 req/hr per database saved)

Typical impact: integrations drop from 500+ requests/hour to under 200 requests/hour (80-95% reduction), staying well within the 3 req/sec limit even with multiple databases.

Error Handling

IssueCauseSolution
429 Too Many Requests despite optimizationShared token across multiple servicesUse separate integration tokens per service; each gets its own 3 req/sec budget
Stale cached data causing bugsCache TTL too long for the use caseShorten TTL to 30-60s for volatile data, or use webhook-based cache invalidation
Webhook not triggeringIntegration not connected to the page/databaseShare via Connections menu in Notion; verify webhook URL is publicly accessible
filter_properties returning empty resultsUsing property names instead of IDsRun databases.retrieve to get property IDs (short strings like abc1), not display names
Incremental sync missing updatesClock skew between client and Notion serverSubtract 5-second buffer from lastSync timestamp to create overlap window
blocks.children.append failing with 400More than 100 children in single callChunk arrays into groups of 100 before appending

Examples

Two complete worked examples live in references/examples.md:

  • Request Reduction Calculator — estimate before/after request volume and rate-limit headroom for your own database count, page count, and poll interval before you commit to changes.
  • Full Optimization Wrapper — a drop-in createOptimizedClient(token) that bundles caching, request tracking, and a rate-limited write queue into one client.

Skeleton of the wrapper (full body in the reference):

export function createOptimizedClient(token: string) {
  const notion = new Client({ auth: token });
  const cache = new LRUCache<string, any>({ max: 1000, ttl: 5 * 60 * 1000 });
  const writeQueue = new PQueue({ concurrency: 3, interval: 1000, intervalCap: 3 });
  // getPage / queryDatabase / createPage / invalidate / stats — see references/examples.md
}

Resources

Next Steps

For rate-limit retry patterns, see notion-rate-limits. For query and search patterns, see notion-search-retrieve. For overall architecture guidance, see notion-reference-architecture.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.