Back to skills
SKILL.md
Notion Search Retrieve
ASecurity'Search Notion workspaces and retrieve pages, databases, and block content
- 2 stars
- 0 votes
- 0 copies
- 0 views
- Added November 11, 2026
Works with
Security analysis
92/100- Installs packages at runtime which could introduce malicious dependencies
Pro scans all 3 files and shows the line behind each finding
npx -y skills add pauldx/claudifying --skill notion-search-retrieve --agent claude-codeAre you the author of Notion Search Retrieve?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/pauldx-notion-search-retrieve)---
name: notion-search-retrieve
description: 'Search Notion workspaces and retrieve pages, databases, and block content
using the Notion API. Use when querying databases with filters, searching
across a workspace, paginating large result sets, or extracting page content.
Trigger with phrases like "notion search", "query notion database",
"notion retrieve page", "notion pagination", "notion filter",
"notion blocks", "notion get content".
'
allowed-tools: Read, Write, Edit, Bash(npm:*), Grep
version: 1.0.0
license: MIT
tags:
- saas
- productivity
- notion
compatibility: Designed for Claude Code
---
# Notion Search & Data Retrieval
## Overview
Search across a Notion workspace, query databases with compound filters, retrieve individual pages, and extract nested block content. Covers the full read path: workspace-level search, database queries with filter/sort/pagination, page retrieval, and recursive block tree traversal.
## Prerequisites
- `@notionhq/client` installed (`npm install @notionhq/client`)
- Notion integration token with read access to target pages/databases
- Integration added to target pages via the Share menu in Notion
- Completed `notion-install-auth` setup
## Instructions
### Step 1: Search the Workspace
Call `notion.search()` to find pages and databases. The integration only sees content explicitly shared with it.
```typescript
import { Client } from '@notionhq/client';
const notion = new Client({ auth: process.env.NOTION_TOKEN });
// Search for pages matching a query
const searchResults = await notion.search({
query: 'meeting notes',
filter: {
property: 'object',
value: 'page', // 'page' or 'database'
},
sort: {
direction: 'descending',
timestamp: 'last_edited_time',
},
page_size: 20,
});
for (const result of searchResults.results) {
if (result.object === 'page' && 'properties' in result) {
const titleProp = Object.values(result.properties)
.find(p => p.type === 'title');
const title = titleProp?.type === 'title'
? titleProp.title.map(t => t.plain_text).join('')
: 'Untitled';
console.log(`${title} (${result.id})`);
}
}
```
An empty `query` string returns all shared content. Results are eventually consistent — newly shared pages may take a few seconds to appear in the index.
### Step 2: Query Databases with Filters
Call `notion.databases.query()` for structured queries. Filters support compound `and`/`or` logic. See [filter-operators.md](references/filter-operators.md) for every property type and operator.
```typescript
// Single filter
const activeItems = await notion.databases.query({
database_id: 'your-database-id',
filter: {
property: 'Status',
select: { equals: 'Active' },
},
sorts: [
{ property: 'Priority', direction: 'descending' },
],
page_size: 50,
});
// Compound filter with AND
const highPriorityActive = await notion.databases.query({
database_id: 'your-database-id',
filter: {
and: [
{ property: 'Status', select: { equals: 'Active' } },
{ property: 'Priority', number: { greater_than: 3 } },
],
},
});
```
### Step 3: Paginate, Retrieve Pages, and Extract Content
Notion uses cursor-based pagination. All list endpoints return `has_more` and `next_cursor`. Call `notion.pages.retrieve()` for a single page, then `notion.blocks.children.list()` to read its content recursively.
```typescript
import type {
PageObjectResponse,
BlockObjectResponse,
} from '@notionhq/client/build/src/api-endpoints';
// Paginate through all database results
async function queryAllPages(databaseId: string): Promise<PageObjectResponse[]> {
const pages: PageObjectResponse[] = [];
let cursor: string | undefined = undefined;
do {
const response = await notion.databases.query({
database_id: databaseId,
start_cursor: cursor,
page_size: 100,
});
for (const page of response.results) {
if ('properties' in page) {
pages.push(page as PageObjectResponse);
}
}
cursor = response.has_more ? response.next_cursor! : undefined;
} while (cursor);
return pages;
}
// Retrieve a single page and extract typed property values
async function getPage(pageId: string) {
const page = await notion.pages.retrieve({ page_id: pageId });
if (!('properties' in page)) throw new Error('Partial page object');
return page as PageObjectResponse;
}
function extractProperties(page: PageObjectResponse) {
const result: Record<string, any> = {};
for (const [name, prop] of Object.entries(page.properties)) {
switch (prop.type) {
case 'title':
result[name] = prop.title.map(t => t.plain_text).join(''); break;
case 'rich_text':
result[name] = prop.rich_text.map(t => t.plain_text).join(''); break;
case 'number': result[name] = prop.number; break;
case 'select': result[name] = prop.select?.name ?? null; break;
case 'multi_select':
result[name] = prop.multi_select.map(s => s.name); break;
case 'date':
result[name] = prop.date ? { start: prop.date.start, end: prop.date.end } : null; break;
case 'people':
result[name] = prop.people.map(p => ('name' in p ? p.name : p.id)); break;
case 'checkbox': result[name] = prop.checkbox; break;
case 'url': result[name] = prop.url; break;
case 'email': result[name] = prop.email; break;
case 'phone_number': result[name] = prop.phone_number; break;
case 'status': result[name] = prop.status?.name ?? null; break;
case 'relation': result[name] = prop.relation.map(r => r.id); break;
case 'formula': result[name] = prop.formula; break;
case 'rollup': result[name] = prop.rollup; break;
default: result[name] = `[${prop.type}]`;
}
}
return result;
}
// Recursively fetch all blocks (page content)
async function getPageContent(
blockId: string, depth = 0, maxDepth = 3
): Promise<BlockObjectResponse[]> {
const blocks: BlockObjectResponse[] = [];
let cursor: string | undefined = undefined;
do {
const response = await notion.blocks.children.list({
block_id: blockId,
start_cursor: cursor,
page_size: 100,
});
for (const block of response.results) {
if (!('type' in block)) continue;
const b = block as BlockObjectResponse;
blocks.push(b);
if (b.has_children && depth < maxDepth) {
blocks.push(...await getPageContent(b.id, depth + 1, maxDepth));
}
}
cursor = response.has_more ? response.next_cursor! : undefined;
} while (cursor);
return blocks;
}
function blockToText(block: BlockObjectResponse): string {
const content = (block as any)[block.type];
if (!content?.rich_text) return '';
return content.rich_text.map((t: any) => t.plain_text).join('');
}
```
## Output
- Workspace-wide search returning pages and databases sorted by recency
- Database queries with compound filters across all property types
- Full pagination collecting every matching result
- Typed property extraction for all 15 Notion property types
- Recursive block tree traversal yielding full page content
## Error Handling
| Issue | Cause | Solution |
|-------|-------|----------|
| Could not find database | Database not shared with integration | Open database in Notion, click Share, add the integration |
| Could not find page | Page not shared or deleted | Verify page is shared; check `archived` status |
| Empty search results | Integration not connected | Share parent page/database with integration; wait for indexing |
| validation_error on filter | Wrong operator for property type | Check [filter-operators.md](references/filter-operators.md) |
| HTTP 429 rate_limited | Too many requests | Back off using `Retry-After` header; use `page_size: 100` |
| Missing properties | Partial page object | Check `'properties' in page` before casting to `PageObjectResponse` |
| Incomplete page content | Not recursing child blocks | Check `has_children` and recurse; increase `maxDepth` |
## Examples
See [examples.md](references/examples.md) for complete patterns including database export, full-text page dump, and compound filter variations.
## Resources
- [Notion Search API](https://developers.notion.com/reference/post-search)
- [Query a Database](https://developers.notion.com/reference/post-database-query)
- [Retrieve a Page](https://developers.notion.com/reference/retrieve-a-page)
- [List Block Children](https://developers.notion.com/reference/get-block-children)
- [Filter Reference](https://developers.notion.com/reference/post-database-query-filter)
- [Property Values](https://developers.notion.com/reference/page-property-values)
- [@notionhq/client npm](https://www.npmjs.com/package/@notionhq/client)
## Next Steps
For creating and updating pages, see `notion-core-workflow-a`. For PII handling and GDPR compliance, see `notion-data-handling`. For real-time sync via webhooks, see `notion-webhooks-events`.
Files in this skill
- SKILL.md
- references/examples.md
- references/filter-operators.md
Attribution
Related skills
View all in tools →Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.