/

Create Notion Blog Post with Native Cover Images

Copy script

Copied

Copy script

Copied

Create Notion Blog Post with Native Cover Images

Created by

andrew van beek - Keyboard Team

Automatically creates professional blog posts in your Notion database with native cover images, SEO optimization, and rich content blocks.

**Required Database Schema:**

Your Notion database should have these properties (with exact names):

- **Title** (Title property) - Main blog post title

- **Status** (Status property) - Publication status (Draft, In Review, Published)

- **Category** (Select property) - Blog categories (Technology, Health & Wellness, Business, Lifestyle, Travel, Personal Development, Food & Recipes, Education)

- **Tags** (Multi-select property) - Blog tags (Technology, Personal, Tutorial, News, Opinion, Review, Travel, Health)

- **Publish Date** (Date property) - Publication date

- **Author** (People property) - Blog post author

- **Framer Slug** (Rich text property) - SEO-friendly URL slug

- **SEO Description** (Rich text property) - Meta description for search engines

- **Content** (Rich text property) - Brief content description

- **Featured Image** (URL property) - Optional, but script uses native cover images instead

**Features:**

- Auto-finds any database with "Blog" in the title

- Uses Notion's native cover image API (displays properly in gallery view)

- 15 curated high-quality cover images (1500px optimized)

- Generates SEO-friendly slugs automatically

- Creates rich content blocks with proper formatting

- Full verification of created blog posts

- Works with any blog database schema matching the above structure

Requirements

notion.com logo

KEYBOARD_NOTION_BLOG_INTERNAL_SECRET

Script

Copy script

Copied

Copy script

Copied

const axios = require('axios');

// Configuration
const NOTION_API_URL = 'https://api.notion.com/v1';
const NOTION_TOKEN = process.env.KEYBOARD_NOTION_BLOG_INTERNAL_SECRET;

console.log('šŸš€ Creating new blog post in Notion...');

// High-quality cover images that work well for blog posts
const randomCovers = [
  'https://images.unsplash.com/photo-1486312338219-ce68d2c6f44d?w=1500&q=80', // Laptop on desk
  'https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=1500&q=80', // Code on screen
  'https://images.unsplash.com/photo-1461749280684-dccba630e2f6?w=1500&q=80', // Programming setup
  'https://images.unsplash.com/photo-1498050108023-c5249f4df085?w=1500&q=80', // Computer and coffee
  'https://images.unsplash.com/photo-1504639725590-34d0984388bd?w=1500&q=80', // Minimal workspace
  'https://images.unsplash.com/photo-1611224923853-80b023f02d71?w=1500&q=80', // Notion-style workspace
  'https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=1500&q=80', // Technology abstract
  'https://images.unsplash.com/photo-1517077304055-6e89abbf09b0?w=1500&q=80', // Modern office
  'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=1500&q=80', // Creative workspace
  'https://images.unsplash.com/photo-1434030216411-0b793f4b4173?w=1500&q=80', // Writing and planning
  'https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=1500&q=80', // Data visualization
  'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=1500&q=80', // Analytics dashboard
  'https://images.unsplash.com/photo-1555421689-491a97ff2040?w=1500&q=80', // Mobile and laptop
  'https://images.unsplash.com/photo-1581291518857-4e27b48ff24e?w=1500&q=80', // Clean desk setup
  'https://images.unsplash.com/photo-1531297484001-80022131f5a1?w=1500&q=80'  // Tech workspace
];

// Function to find the Blog Posts database
async function findBlogDatabase() {
  try {
    const response = await axios({
      method: 'POST',
      url: `${NOTION_API_URL}/search`,
      headers: {
        'Authorization': `Bearer ${NOTION_TOKEN}`,
        'Notion-Version': '2022-06-28',
        'Content-Type': 'application/json'
      },
      data: {
        filter: {
          value: 'database',
          property: 'object'
        }
      }
    });
    
    // Find the first database with "Blog" in the title
    const blogDb = response.data.results.find(db => 
      db.title?.[0]?.plain_text?.toLowerCase().includes('blog')
    );
    
    if (blogDb) {
      console.log(`āœ… Found blog database: ${blogDb.title[0].plain_text} (ID: ${blogDb.id})`);
      return blogDb;
    } else {
      throw new Error('No database with "Blog" in the title found');
    }
  } catch (error) {
    console.error('āŒ Error finding blog database:', error.response?.data || error.message);
    throw error;
  }
}

// Function to get database schema
async function getDatabaseSchema(databaseId) {
  try {
    const response = await axios({
      method: 'GET',
      url: `${NOTION_API_URL}/databases/${databaseId}`,
      headers: {
        'Authorization': `Bearer ${NOTION_TOKEN}`,
        'Notion-Version': '2022-06-28'
      }
    });
    
    return response.data;
  } catch (error) {
    console.error('āŒ Error getting database schema:', error.response?.data || error.message);
    throw error;
  }
}

// Function to create blog post with proper cover image
async function createBlogPost(databaseId, blogData) {
  try {
    // Select random cover image if requested
    const coverImage = blogData.useRandomCover 
      ? randomCovers[Math.floor(Math.random() * randomCovers.length)]
      : null;
    
    if (coverImage) {
      console.log('šŸ–¼ļø Selected random cover image');
    }
    
    // Generate SEO description if not provided
    const seoDescription = blogData.seoDescription || 
      `Discover insights about ${blogData.title.toLowerCase()}. ${blogData.content.substring(0, 100)}...`;
    
    // Generate slug from title
    const slug = blogData.title.toLowerCase()
      .replace(/[^a-z0-9\s-]/g, '')
      .replace(/\s+/g, '-')
      .replace(/-+/g, '-')
      .trim('-');
    
    // Create the page data with cover at page level
    const pageData = {
      parent: {
        database_id: databaseId
      },
      properties: {
        'Title': {
          title: [{ text: { content: blogData.title } }]
        },
        'Status': {
          status: { name: blogData.status }
        },
        'Category': {
          select: { name: blogData.category }
        },
        'Tags': {
          multi_select: Array.isArray(blogData.tags) 
            ? blogData.tags.map(tag => ({ name: tag.trim() }))
            : blogData.tags.split(',').map(tag => ({ name: tag.trim() }))
        },
        'Publish Date': {
          date: { start: new Date().toISOString().split('T')[0] }
        },
        'Framer Slug': {
          rich_text: [{ text: { content: slug } }]
        },
        'SEO Description': {
          rich_text: [{ text: { content: seoDescription } }]
        },
        'Content': {
          rich_text: [{ text: { content: blogData.content } }]
        }
      },
      children: [
        {
          object: 'block',
          type: 'paragraph',
          paragraph: {
            rich_text: [{
              type: 'text',
              text: { content: 'šŸš€ ' + blogData.content },
              annotations: { bold: true }
            }]
          }
        },
        {
          object: 'block',
          type: 'divider',
          divider: {}
        },
        {
          object: 'block',
          type: 'heading_2',
          heading_2: {
            rich_text: [{ text: { content: 'Introduction' } }]
          }
        },
        {
          object: 'block',
          type: 'paragraph',
          paragraph: {
            rich_text: [{
              text: { content: `Welcome to this post about ${blogData.title.toLowerCase()}. This content was created automatically and is ready for you to customize and expand upon.` }
            }]
          }
        },
        {
          object: 'block',
          type: 'callout',
          callout: {
            rich_text: [{
              text: { content: '✨ This blog post was created using automation! Edit and customize as needed.' }
            }],
            icon: { emoji: '✨' }
          }
        },
        {
          object: 'block',
          type: 'heading_3',
          heading_3: {
            rich_text: [{ text: { content: 'Key Points' } }]
          }
        },
        {
          object: 'block',
          type: 'bulleted_list_item',
          bulleted_list_item: {
            rich_text: [{ text: { content: 'Automatically generated content structure' } }]
          }
        },
        {
          object: 'block',
          type: 'bulleted_list_item',
          bulleted_list_item: {
            rich_text: [{ text: { content: 'SEO-optimized slug and description' } }]
          }
        },
        {
          object: 'block',
          type: 'bulleted_list_item',
          bulleted_list_item: {
            rich_text: [{ text: { content: 'Professional cover image automatically selected' } }]
          }
        },
        {
          object: 'block',
          type: 'paragraph',
          paragraph: {
            rich_text: [{
              text: { content: 'Add your own content here and customize this post to make it uniquely yours!' }
            }]
          }
        }
      ]
    };
    
    // Add cover image at page level if requested
    if (coverImage) {
      pageData.cover = {
        type: 'external',
        external: {
          url: coverImage
        }
      };
    }
    
    const response = await axios({
      method: 'POST',
      url: `${NOTION_API_URL}/pages`,
      headers: {
        'Authorization': `Bearer ${NOTION_TOKEN}`,
        'Notion-Version': '2022-06-28',
        'Content-Type': 'application/json'
      },
      data: pageData
    });
    
    return response.data;
  } catch (error) {
    console.error('āŒ Error creating blog post:', error.response?.data || error.message);
    throw error;
  }
}

// Main execution
async function main() {
  try {
    // Parse input data
    const blogData = {
      title: '{{title}}',
      status: '{{status}}',
      category: '{{category}}',
      tags: {{tags}},
      author: '{{author}}',
      content: '{{content}}',
      seoDescription: '{{seoDescription}}' || null,
      useRandomCover: {{useRandomCover}}
    };
    
    console.log(`šŸ“ Creating blog post: "${blogData.title}"`);
    
    // Find the blog database
    const blogDb = await findBlogDatabase();
    
    // Get database schema to ensure compatibility
    await getDatabaseSchema(blogDb.id);
    
    // Create the blog post
    const newPost = await createBlogPost(blogDb.id, blogData);
    
    // Verify creation
    const verifyResponse = await axios({
      method: 'GET',
      url: `${NOTION_API_URL}/pages/${newPost.id}`,
      headers: {
        'Authorization': `Bearer ${NOTION_TOKEN}`,
        'Notion-Version': '2022-06-28'
      }
    });
    
    console.log('\nāœ… SUCCESS! Blog post created and verified!');
    console.log('šŸ“‹ Details:');
    console.log(`   šŸ“ Title: ${verifyResponse.data.properties.Title?.title?.[0]?.text?.content}`);
    console.log(`   šŸ“Š Status: ${verifyResponse.data.properties.Status?.status?.name}`);
    console.log(`   šŸ·ļø Category: ${verifyResponse.data.properties.Category?.select?.name}`);
    console.log(`   šŸ·ļø Tags: ${verifyResponse.data.properties.Tags?.multi_select?.map(tag => tag.name).join(', ')}`);
    console.log(`   šŸ–¼ļø Cover: ${verifyResponse.data.cover ? 'Notion cover image set' : 'No cover'}`);
    console.log(`   šŸ”— URL: ${verifyResponse.data.url}`);
    console.log(`   šŸ“„ Page ID: ${newPost.id}`);
    
  } catch (error) {
    console.error('āŒ Script failed:', error.message);
  }
}

main();