Search Notion Workspace
Search for pages and databases in Notion workspace by title or content
Source Code
import fs from "fs";
const [
query = "",
filter = "all",
maxResults = "20",
outputPath = "session/notion-search.json",
] = process.argv.slice(2);
const maxResultsNum = Math.min(parseInt(maxResults) || 20, 100);
console.log(
`Searching Notion for "${query || "(all)"}"${
filter !== "all" ? ` (${filter}s only)` : ""
}...`
);
const NOTION_API = "https://api.notion.com/v1";
const headers = {
Authorization: "Bearer PLACEHOLDER_TOKEN",
"Content-Type": "application/json",
"Notion-Version": "2022-06-28",
};
try {
const results = [];
let cursor = undefined;
let totalFetched = 0;
while (totalFetched < maxResultsNum) {
const pageSize = Math.min(maxResultsNum - totalFetched, 100);
const body = {
page_size: pageSize,
...(query && { query }),
...(cursor && { start_cursor: cursor }),
};
// Add filter if not "all"
if (filter === "page") {
body.filter = { property: "object", value: "page" };
} else if (filter === "database") {
body.filter = { property: "object", value: "database" };
}
const res = await fetch(`${NOTION_API}/search`, {
method: "POST",
headers,
body: JSON.stringify(body),
});
if (!res.ok) {
const errorText = await res.text();
console.error(`Notion API error: ${res.status}`);
console.error(errorText);
throw new Error(`Notion API failed: ${res.status}`);
}
const response = await res.json();
for (const item of response.results) {
if (totalFetched >= maxResultsNum) break;
const result = {
id: item.id,
type: item.object,
url: item.url,
lastEdited: item.last_edited_time,
createdTime: item.created_time,
};
if (item.object === "page") {
// Extract title from page properties
const titleProp = Object.values(item.properties || {}).find(
(p) => p.type === "title"
);
result.title =
titleProp?.title?.[0]?.plain_text ||
item.properties?.title?.title?.[0]?.plain_text ||
"Untitled";
// Get parent info
if (item.parent?.type === "database_id") {
result.parentType = "database";
result.parentId = item.parent.database_id;
} else if (item.parent?.type === "page_id") {
result.parentType = "page";
result.parentId = item.parent.page_id;
} else {
result.parentType = "workspace";
}
// Include icon if present
if (item.icon) {
result.icon =
item.icon.type === "emoji"
? item.icon.emoji
: item.icon.external?.url || item.icon.file?.url;
}
} else if (item.object === "database") {
result.title = item.title?.[0]?.plain_text || "Untitled Database";
result.description = item.description?.[0]?.plain_text || null;
// Get property schema summary
result.properties = Object.entries(item.properties || {}).map(
([name, prop]) => ({
name,
type: prop.type,
})
);
if (item.icon) {
result.icon =
item.icon.type === "emoji"
? item.icon.emoji
: item.icon.external?.url || item.icon.file?.url;
}
}
results.push(result);
totalFetched++;
}
if (!response.has_more || totalFetched >= maxResultsNum) break;
cursor = response.next_cursor;
}
// Ensure output directory exists
const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
if (dir) {
fs.mkdirSync(dir, { recursive: true });
}
const output = {
query: query || null,
filter,
searchedAt: new Date().toISOString(),
count: results.length,
results,
};
fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
// Log summary
console.log(`\nā Found ${results.length} results`);
console.log(` Written to: ${outputPath}`);
// Show top results
const pages = results.filter((r) => r.type === "page");
const databases = results.filter((r) => r.type === "database");
if (databases.length > 0) {
console.log(`\n Databases (${databases.length}):`);
for (const db of databases.slice(0, 5)) {
console.log(
` ${db.icon || "š"} ${db.title} (${
db.properties?.length || 0
} properties)`
);
}
}
if (pages.length > 0) {
console.log(`\n Pages (${pages.length}):`);
for (const page of pages.slice(0, 5)) {
console.log(` ${page.icon || "š"} ${page.title}`);
}
}
console.log(
JSON.stringify({
success: true,
outputPath,
count: results.length,
databases: databases.length,
pages: pages.length,
})
);
} catch (error) {
console.error("Error searching Notion:", error.message);
throw error;
} import fs from "fs";
const [
query = "",
filter = "all",
maxResults = "20",
outputPath = "session/notion-search.json",
] = process.argv.slice(2);
const maxResultsNum = Math.min(parseInt(maxResults) || 20, 100);
console.log(
`Searching Notion for "${query || "(all)"}"${
filter !== "all" ? ` (${filter}s only)` : ""
}...`
);
const NOTION_API = "https://api.notion.com/v1";
const headers = {
Authorization: "Bearer PLACEHOLDER_TOKEN",
"Content-Type": "application/json",
"Notion-Version": "2022-06-28",
};
try {
const results = [];
let cursor = undefined;
let totalFetched = 0;
while (totalFetched < maxResultsNum) {
const pageSize = Math.min(maxResultsNum - totalFetched, 100);
const body = {
page_size: pageSize,
...(query && { query }),
...(cursor && { start_cursor: cursor }),
};
// Add filter if not "all"
if (filter === "page") {
body.filter = { property: "object", value: "page" };
} else if (filter === "database") {
body.filter = { property: "object", value: "database" };
}
const res = await fetch(`${NOTION_API}/search`, {
method: "POST",
headers,
body: JSON.stringify(body),
});
if (!res.ok) {
const errorText = await res.text();
console.error(`Notion API error: ${res.status}`);
console.error(errorText);
throw new Error(`Notion API failed: ${res.status}`);
}
const response = await res.json();
for (const item of response.results) {
if (totalFetched >= maxResultsNum) break;
const result = {
id: item.id,
type: item.object,
url: item.url,
lastEdited: item.last_edited_time,
createdTime: item.created_time,
};
if (item.object === "page") {
// Extract title from page properties
const titleProp = Object.values(item.properties || {}).find(
(p) => p.type === "title"
);
result.title =
titleProp?.title?.[0]?.plain_text ||
item.properties?.title?.title?.[0]?.plain_text ||
"Untitled";
// Get parent info
if (item.parent?.type === "database_id") {
result.parentType = "database";
result.parentId = item.parent.database_id;
} else if (item.parent?.type === "page_id") {
result.parentType = "page";
result.parentId = item.parent.page_id;
} else {
result.parentType = "workspace";
}
// Include icon if present
if (item.icon) {
result.icon =
item.icon.type === "emoji"
? item.icon.emoji
: item.icon.external?.url || item.icon.file?.url;
}
} else if (item.object === "database") {
result.title = item.title?.[0]?.plain_text || "Untitled Database";
result.description = item.description?.[0]?.plain_text || null;
// Get property schema summary
result.properties = Object.entries(item.properties || {}).map(
([name, prop]) => ({
name,
type: prop.type,
})
);
if (item.icon) {
result.icon =
item.icon.type === "emoji"
? item.icon.emoji
: item.icon.external?.url || item.icon.file?.url;
}
}
results.push(result);
totalFetched++;
}
if (!response.has_more || totalFetched >= maxResultsNum) break;
cursor = response.next_cursor;
}
// Ensure output directory exists
const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
if (dir) {
fs.mkdirSync(dir, { recursive: true });
}
const output = {
query: query || null,
filter,
searchedAt: new Date().toISOString(),
count: results.length,
results,
};
fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
// Log summary
console.log(`\nā Found ${results.length} results`);
console.log(` Written to: ${outputPath}`);
// Show top results
const pages = results.filter((r) => r.type === "page");
const databases = results.filter((r) => r.type === "database");
if (databases.length > 0) {
console.log(`\n Databases (${databases.length}):`);
for (const db of databases.slice(0, 5)) {
console.log(
` ${db.icon || "š"} ${db.title} (${
db.properties?.length || 0
} properties)`
);
}
}
if (pages.length > 0) {
console.log(`\n Pages (${pages.length}):`);
for (const page of pages.slice(0, 5)) {
console.log(` ${page.icon || "š"} ${page.title}`);
}
}
console.log(
JSON.stringify({
success: true,
outputPath,
count: results.length,
databases: databases.length,
pages: pages.length,
})
);
} catch (error) {
console.error("Error searching Notion:", error.message);
throw error;
}