/

Create Notion Blog Post with Native Cover Images

Copy script

Copied

Copy script

Copied

Create Notion Blog Post with Native Cover Images

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

Created by

andrew van beek - Keyboard Team

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();