A comprehensive API for enhancing Notion pages with web content utilities. This API provides several endpoints to extract and update information from web pages to Notion, including favicons, page titles, domain information, and embeds. The system includes automated cron jobs for periodic data synchronization and blob storage for efficient caching.
This API serves as a bridge between web content and Notion pages, allowing you to:
- Extract favicons from websites and set them as Notion page icons
- Extract page titles from websites and update Notion page titles
- Extract domain information from URLs in Notion pages
- Add URL embeds to Notion pages
- Generate showcases of web content
- Cache Notion database data for improved performance
- Automatically publish content based on status changes
The API is built using Hono, a lightweight web framework, with modular routes for better organization and maintainability. It includes automated background processes and intelligent caching to optimize performance and reduce API calls.
/
├── index.tsx # Main Hono app entry point with route mounting
├── cache.tsx (cron) # Cron job for updating cached Notion data
├── publish.tsx (cron) # Cron job for auto-publishing content
├── routes/ # Directory for API route modules
│ ├── cache.tsx # Cache management endpoints
│ ├── domain.tsx # Domain extraction endpoint
│ ├── embed.tsx # URL embed endpoint
│ ├── favicon.tsx # Favicon extraction endpoint
│ ├── showcase.tsx # Content showcase endpoint
│ ├── showcaseHTML.tsx # HTML showcase endpoint
│ ├── test-domain.tsx # Domain testing endpoint
│ └── title.tsx # Title extraction endpoint
├── utils/ # Utility functions
│ ├── extractValuesFromNotionProperties.tsx # Notion property parsers
│ ├── getDomain.tsx # Function to extract domain from URL
│ ├── getTitle.tsx # Function to extract title from URL
│ └── HTMLhelpers.tsx # HTML processing utilities
├── frontend/ # Frontend assets
│ └── index.html # Main HTML template
└── README.md # This documentation file
The application uses Hono as its web framework, providing a lightweight and fast HTTP server. The main entry point (index.tsx
) creates a Hono app instance and mounts modular route handlers:
// Main app setup
const app = new Hono();
app.use(trimTrailingSlash());
// Mount route modules
app.route("/cache", cache);
app.route("/domain", domain);
app.route("/embed", embed);
app.route("/title", title);
app.route("/favicon", favicon);
// ... other routes
// Error handling for debugging
app.onError((err, c) => {
console.error("Error:", err);
throw err;
});
export default app.fetch; // Entry point for HTTP vals
Each route is implemented as a separate Hono router module in the /routes
directory, allowing for:
- Modular organization: Each endpoint has its own file with focused functionality
- Independent testing: Routes can be tested in isolation
- Easy maintenance: Changes to one endpoint don't affect others
- Clear separation of concerns: Each route handles specific web utility functions
The system includes two automated cron jobs that run periodically to manage Notion data:
- Purpose: Periodically refreshes cached Notion database data
- Frequency: Runs on a scheduled interval (configurable in Val Town UI)
- Process:
- Finds cached database blob using key pattern
cache--db--{databaseId}
- Queries Notion database for pages with "Published" status
- Extracts relevant properties (Name, URL, Publication status)
- Updates the blob storage with fresh data
- Maintains cache consistency without manual intervention
- Finds cached database blob using key pattern
// Key pattern for finding cached databases
const blobKey = ["cache", "db"].join("--");
const blobKeys = await blob.list(blobKey);
const databaseId = blobKeys[0].key.split("--").pop();
// Query Notion with filter
const response = await notion.databases.query({
database_id: databaseId,
filter: {
property: "Publication status",
select: { equals: "Published" }
}
});
- Purpose: Automatically publishes content marked as "Ready to publish"
- Frequency: Runs on a scheduled interval
- Process:
- Finds pages with "Ready to publish" status
- Updates their status to "Published"
- Enables automated content workflow without manual intervention
The application uses Val Town's blob storage as an intelligent caching layer to optimize performance and reduce Notion API calls:
Blob Key Structure: cache--db--{databaseId}
cache
: Identifies the blob's purposedb
: Indicates it's a database cache{databaseId}
: The specific Notion database being cached
-
Cache Initialization (
/cache
POST endpoint):// Extract database ID from a page const page = await notion.pages.retrieve({ page_id: pageId }); const parentId = page.parent.database_id; // Create structured cache key const blobKey = ["cache", "db", parentId].join("--"); // Initialize cache if it doesn't exist if (!await blob.getJSON(blobKey)) { await blob.setJSON(blobKey, { ok: true }); } -
Cache Updates (via cron job):
- Automatically refreshes cached data every interval
- Maintains data freshness without manual intervention
- Reduces load on Notion API by batching updates
-
Cache Retrieval (
/cache
GET endpoint):// Find cache using key fragment const blobKeys = await blob.list("cache--db"); const cachedData = await blob.getJSON(blobKeys[0].key); return c.json(cachedData);
- Performance: Instant data retrieval from blob storage vs. API calls
- Rate Limiting: Reduces Notion API usage, avoiding rate limits
- Reliability: Cached data available even if Notion API is temporarily unavailable
- Cost Efficiency: Fewer API calls reduce potential costs
- Scalability: Can serve multiple requests from cache without additional API load
Returns a list of available endpoints.
Example Response:
API Gateway - Available endpoints: /cache, /domain, /title, /favicon, /test-domain, /embed, /showcase, /showcase-html
Initializes caching for a Notion database by creating a blob storage entry.
Request Body:
{ "data": { "id": "notion-page-id" } }
Process:
- Retrieves the page from Notion
- Extracts the parent database ID
- Creates a cache blob with key pattern:
cache--db--{databaseId}
- Returns the cache status
Success Response:
{ "ok": true }
Retrieves cached database data from blob storage.
Success Response:
{ "success": true, "count": 25, "pages": [ { "id": "page-id", "name": "Page Title", "url": "https://example.com", "status": "Published" } ] }
Retrieves cached data for a specific database ID.
Parameters:
id
: The database ID to retrieve cache for
Extracts a favicon from a URL in a Notion page and updates the page icon.
Request Body:
{ "data": { "id": "notion-page-id" } }
Success Response:
{ "success": true, "message": "Favicon found and Notion page updated", "pageId": "notion-page-id", "url": "https://example.com", "faviconUrl": "https://example.com/favicon.ico" }
Error Response:
{ "success": false, "error": "Failed to retrieve Notion page", "message": "Error details", "pageId": "notion-page-id" }
Handles Notion webhooks to extract a favicon from a URL and update the corresponding Notion page with the favicon.
Request Body:
{ "pageId": "notion-page-id" }
Success Response:
{ "success": true, "message": "Favicon found and Notion page updated", "pageId": "notion-page-id", "url": "https://example.com", "faviconUrl": "https://example.com/favicon.ico" }
Extracts the domain from a URL property in a Notion page and updates the Domain property.
Request Body:
{ "data": { "id": "notion-page-id" } }
Success Response: Returns the updated Notion page object.
Test endpoint to verify the domain extraction API is working.
Response:
Hello World!
Extracts the title from a URL in a Notion page and updates the page title.
Request Body:
{ "data": { "id": "notion-page-id" } }
Success Response: Returns the updated Notion page object.
Error Response:
{ "error": "Error message", "stack": "Error stack trace" }
Test endpoint to verify the title extraction API is working.
Response:
Title Extractor API is running!
Test endpoint for the getTitle function.
Query Parameters:
url
: The URL to extract the title from
Success Response:
{ "url": "https://example.com", "title": "Example Domain" }
Adds an embed block with the provided URL to a Notion page.
Request Body:
{ "pageId": "notion-page-id", "url": "https://example.com" }
Endpoints for showcasing content, likely for preview purposes.
The favicon endpoint uses multiple strategies to extract a favicon:
- First tries Google's Favicon API for reliable results
- If that fails, tries the standard
/favicon.ico
path - If that fails, it parses the HTML to find favicon link tags
- Returns the favicon URL and updates the Notion page icon
- Falls back to a globe emoji (🌐) if no favicon can be found
The title extraction is sophisticated and handles various edge cases:
- Special handling for YouTube URLs using their oEmbed API
- Special handling for news sites with anti-scraping measures
- Multiple fallback strategies including:
- OpenGraph meta tags
- Twitter card meta tags
- Standard title tags
- Google search results
- URL slug parsing
Extracts the domain (including protocol) from a URL:
- Handles URLs without protocol by adding https:// as default
- Parses the URL to extract the origin (protocol + hostname)
- Updates the Domain property in the Notion page
This API requires the following environment variable:
NOTION_API_KEY
: Your Notion API integration token
You'll need to create an integration in the Notion workspace and grant it access to the relevant pages or databases.
curl -X POST https://your-api-url/favicon \ -H "Content-Type: application/json" \ -d '{"data":{"id":"your-notion-page-id"}}'
curl -X GET "https://your-api-url/title/test?url=https://example.com"
curl -X POST https://your-api-url/title \ -H "Content-Type: application/json" \ -d '{"data":{"id":"your-notion-page-id"}}'
To add a new route:
- Create a new file in the
/routes
directory - Export a Hono router with your endpoint handlers
- Import and mount the router in
index.tsx
Example:
// In /routes/newEndpoint.ts
import { Hono } from "npm:hono@3";
const router = new Hono();
router.get("/", (c) => {
return c.json({ message: "New endpoint" });
});
export default router;
// In index.tsx
import newEndpoint from "./routes/newEndpoint.tsx";
// Mount the new router
app.route("/new-endpoint", newEndpoint);
The API includes comprehensive error handling:
- Detailed error messages for debugging
- Fallback strategies for common failure scenarios
- Error unwrapping to see original error details
This API integrates with the Notion API to:
- Retrieve page data including properties like URL and Domain
- Update page properties with extracted information
- Set page icons using extracted favicons
- Add embed blocks to pages
The integration requires a Notion API key and appropriate permissions to access and modify the pages.