Tutorial

Screenshot API for Notion and Productivity Tools Integration

Add dynamic screenshots to Notion databases, Airtable bases, and productivity workflows.

Asad AliOctober 10, 20255 min read

Productivity tools like Notion and Airtable benefit enormously from visual content. This guide shows how to automatically capture and embed website screenshots.

Notion Integration

Via Make.com/Zapier

Create automated screenshot workflows:

  1. Trigger: New link added to Notion database
  2. Action: Capture screenshot via API
  3. Action: Upload to cloud storage
  4. Action: Update Notion page with image
// Make.com HTTP module config
{
  "url": "https://api.screenshotly.app/screenshot",
  "method": "POST",
  "headers": {
    "Authorization": "Bearer {{apiKey}}",
    "Content-Type": "application/json"
  },
  "body": {
    "url": "{{notionPageUrl}}",
    "device": "desktop",
    "format": "png"
  }
}

Notion API + Screenshots

const { Client } = require('@notionhq/client');

const notion = new Client({ auth: process.env.NOTION_KEY });

async function addScreenshotToPage(pageId, url) {
  // Capture screenshot
  const screenshot = await captureScreenshot(url);
  
  // Upload to S3/Cloudinary
  const imageUrl = await uploadImage(screenshot);
  
  // Add image block to Notion page
  await notion.blocks.children.append({
    block_id: pageId,
    children: [
      {
        type: 'image',
        image: {
          type: 'external',
          external: { url: imageUrl },
        },
      },
    ],
  });
}

// Monitor database for new URLs
async function watchDatabase(databaseId) {
  const response = await notion.databases.query({
    database_id: databaseId,
    filter: {
      property: 'Screenshot',
      files: { is_empty: true },
    },
  });
  
  for (const page of response.results) {
    const url = page.properties.URL?.url;
    if (url) {
      await addScreenshotToPage(page.id, url);
    }
  }
}

Airtable Integration

Scripting Block

// Airtable Scripting Block
const table = base.getTable('Links');
const query = await table.selectRecordsAsync();

for (const record of query.records) {
  const url = record.getCellValue('URL');
  const hasScreenshot = record.getCellValue('Screenshot');
  
  if (url && !hasScreenshot) {
    // Call screenshot API
    const response = await fetch('https://api.screenshotly.app/screenshot', {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer ' + input.config().apiKey,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ url }),
    });
    
    // Upload and update record
    const imageUrl = await uploadToStorage(response);
    await table.updateRecordAsync(record.id, {
      'Screenshot': [{ url: imageUrl }],
    });
  }
}

Airtable Automation

  1. Trigger: When record matches conditions
  2. Script: Run screenshot capture
  3. Update: Attach image to record

Coda Integration

// Coda Pack formula
const screenshotFormula = coda.makeFormula({
  name: 'Screenshot',
  description: 'Capture website screenshot',
  parameters: [
    coda.makeParameter({
      type: coda.ParameterType.String,
      name: 'url',
      description: 'Website URL',
    }),
  ],
  resultType: coda.ValueType.String,
  execute: async function([url], context) {
    const response = await context.fetcher.fetch({
      method: 'POST',
      url: 'https://api.screenshotly.app/screenshot',
      headers: {
        'Authorization': 'Bearer {{apiKey}}',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ url }),
    });
    
    // Return image URL
    return uploadAndGetUrl(response.body);
  },
});

Slack Integration

Auto-preview links shared in Slack:

app.event('link_shared', async ({ event, client }) => {
  const unfurls = {};
  
  for (const link of event.links) {
    const screenshot = await captureScreenshot(link.url, {
      viewport: { width: 1200, height: 630 },
    });
    const imageUrl = await uploadImage(screenshot);
    
    unfurls[link.url] = {
      blocks: [
        {
          type: 'image',
          image_url: imageUrl,
          alt_text: `Preview of ${link.url}`,
        },
      ],
    };
  }
  
  await client.chat.unfurl({
    channel: event.channel,
    ts: event.message_ts,
    unfurls,
  });
});

Common Workflows

Research Database

Automatically capture screenshots for bookmarked links:

Trigger: New bookmark added
→ Capture desktop screenshot
→ Capture mobile screenshot
→ Attach both to record
→ Extract page title/description

Competitor Monitoring

Track competitor website changes:

Schedule: Daily at 9am
→ Capture all competitor URLs
→ Compare with previous screenshots
→ Alert if changes detected
→ Store in archive

Documentation

Auto-document linked tools:

Trigger: New tool URL in database
→ Capture homepage screenshot
→ Capture pricing page
→ Create summary from metadata
→ Update documentation page

Best Practices for Productivity Tool Integrations

1. Batch Captures for Efficiency

When you have multiple URLs to capture—like importing a research list into Notion—batch them rather than making individual requests. This reduces API calls and keeps you within rate limits.

async function batchCapture(urls, concurrency = 3) {
  const results = [];
  for (let i = 0; i < urls.length; i += concurrency) {
    const batch = urls.slice(i, i + concurrency);
    const captures = await Promise.allSettled(
      batch.map(url => captureScreenshot(url))
    );
    results.push(...captures);
    if (i + concurrency < urls.length) {
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
  }
  return results;
}

2. Cache Screenshots Intelligently

Most productivity databases don't need real-time screenshots. A homepage captured yesterday looks the same today. Use the Screenshotly cache parameter to get cached results automatically:

{
  "url": "https://example.com",
  "cache": true,
  "cacheTtl": 86400
}

This avoids recapturing the same URL within the cache window, saving API credits and speeding up your workflows.

3. Handle Errors Gracefully

Not every URL will produce a clean screenshot. Some sites block bots, some have broken SSL, some time out. Your integration should handle these gracefully rather than breaking the entire workflow.

async function safeCapture(url) {
  try {
    const screenshot = await captureScreenshot(url);
    return { success: true, data: screenshot };
  } catch (error) {
    console.warn(`Failed for ${url}: ${error.message}`);
    return { success: false, placeholder: '/images/unavailable.png' };
  }
}

4. Choose the Right Viewport

Different tools display images at different sizes. Capture at the right viewport to prevent unnecessary resizing:

Tool Recommended Viewport Notes
Notion 1200×630 Gallery view thumbnails
Airtable 800×600 Attachment previews
Coda 1200×630 Embeds and cards
Slack 1200×630 Link unfurls

5. Storage and Cleanup

Screenshots take space. For a database with 500 URLs captured monthly, that's roughly 500MB–1GB of images. Plan your storage accordingly:

  • Cloud storage (R2, S3, Cloudinary) is best for production
  • Set retention policies — delete screenshots older than 90 days
  • Use WebP format for 30–50% smaller files with no visible quality loss

Troubleshooting Common Issues

"Notion API returns 409 Conflict" — This happens when you update a page being edited. Add retry logic with exponential backoff.

"Airtable rate limit exceeded" — Airtable limits API calls to 5 per second. Space your screenshot updates or use batch update endpoints.

"Screenshots look different than expected" — Enable stealth mode and blockAds to get screenshots that match what real users see:

{ "url": "https://example.com", "stealth": true, "blockAds": true, "delay": 2000 }

FAQ

How often should I refresh screenshots? For most use cases, weekly or monthly is sufficient. Set up a scheduled automation to recapture all URLs periodically. For competitor monitoring, daily captures are worthwhile.

What about sites that block screenshots? Enable stealth mode in your API call. This bypasses most bot detection. For login-protected sites, you can pass cookies to authenticate the session.

Is there a file size limit for Notion images? Notion accepts external image URLs of any size, but large images load slowly. Capture at 1200px width with WebP format for the best quality-to-size ratio.

Can I capture specific parts of a page? Yes — use the selector parameter to capture only a specific CSS element, or use clip to specify exact pixel coordinates.


Ready to enhance your productivity tools with automated screenshots?

Get your free API key → — 100 free screenshots to get started.

See also:

notion
airtable
automation
no-code

About the Author

Asad Ali

Asad Ali

Full-Stack Developer and Founder of ZTabs with 8+ years of experience building scalable web applications and APIs. Specializes in performance optimization, SaaS development, and modern web technologies.

Credentials: Founder & CEO at ZTabs, Full-Stack Developer, Expert in Next.js, React, Node.js, and API optimization

Ready to capture your first screenshot?

Get started with 100 free screenshots. No credit card required.

Related Articles