Notion SDK vs Airtable API vs Google Sheets API: Spreadsheet and Database APIs (2026)
TL;DR
Notion SDK provides programmatic access to Notion databases, pages, and blocks — query databases, create pages, manage content, build integrations with the Notion workspace. Airtable API turns Airtable bases into a backend — CRUD operations, formulas, attachments, views, webhook automations, relational data. Google Sheets API treats spreadsheets as a database — read/write cells, formatting, charts, widely used for quick data backends and reporting. In 2026: Notion SDK for content and knowledge base automation, Airtable API for relational data and app backends, Google Sheets API for spreadsheet automation and reporting.
Key Takeaways
- Notion: @notionhq/client ~300K weekly downloads — databases, pages, blocks, rich content
- Airtable: airtable ~200K weekly downloads — relational data, views, formulas, attachments
- Google Sheets: googleapis ~5M weekly downloads — cells, ranges, formatting, charts
- Notion excels at rich content (pages, blocks, embeds)
- Airtable excels at relational data (linked records, views)
- Google Sheets is the most accessible (everyone knows spreadsheets)
Notion SDK
Notion SDK — Notion API client:
Query database
import { Client } from "@notionhq/client"
const notion = new Client({ auth: process.env.NOTION_TOKEN })
// Query a database:
const response = await notion.databases.query({
database_id: "abc123...",
filter: {
and: [
{
property: "Status",
select: { equals: "Published" },
},
{
property: "Tags",
multi_select: { contains: "react" },
},
],
},
sorts: [
{ property: "Date", direction: "descending" },
],
page_size: 10,
})
// Parse results:
response.results.forEach((page: any) => {
const title = page.properties.Title.title[0]?.plain_text
const status = page.properties.Status.select?.name
const date = page.properties.Date.date?.start
console.log(`${title} — ${status} (${date})`)
})
Create page
import { Client } from "@notionhq/client"
const notion = new Client({ auth: process.env.NOTION_TOKEN })
// Create a page in a database:
const page = await notion.pages.create({
parent: { database_id: "abc123..." },
properties: {
Title: {
title: [{ text: { content: "React 19 Released" } }],
},
Status: {
select: { name: "Draft" },
},
Tags: {
multi_select: [
{ name: "react" },
{ name: "release" },
],
},
Date: {
date: { start: "2026-03-09" },
},
Downloads: {
number: 25000000,
},
},
// Page content (blocks):
children: [
{
object: "block",
type: "heading_2",
heading_2: {
rich_text: [{ text: { content: "Overview" } }],
},
},
{
object: "block",
type: "paragraph",
paragraph: {
rich_text: [{
text: { content: "React 19 introduces new features..." },
}],
},
},
{
object: "block",
type: "code",
code: {
rich_text: [{ text: { content: "npm install react@19" } }],
language: "bash",
},
},
],
})
Update and search
import { Client } from "@notionhq/client"
const notion = new Client({ auth: process.env.NOTION_TOKEN })
// Update page properties:
await notion.pages.update({
page_id: "page-123...",
properties: {
Status: { select: { name: "Published" } },
Date: { date: { start: new Date().toISOString().split("T")[0] } },
},
})
// Search across workspace:
const search = await notion.search({
query: "react",
filter: { property: "object", value: "page" },
sort: { direction: "descending", timestamp: "last_edited_time" },
})
search.results.forEach((result: any) => {
console.log(result.id, result.url)
})
// Append blocks to page:
await notion.blocks.children.append({
block_id: "page-123...",
children: [
{
object: "block",
type: "callout",
callout: {
rich_text: [{ text: { content: "Updated with latest data!" } }],
icon: { emoji: "📊" },
},
},
],
})
Airtable API
Airtable — relational database API:
Query records
import Airtable from "airtable"
const base = new Airtable({ apiKey: process.env.AIRTABLE_TOKEN })
.base(process.env.AIRTABLE_BASE_ID!)
// Query records:
const records = await base("Packages")
.select({
filterByFormula: "AND({Status} = 'Active', {Downloads} > 1000000)",
sort: [{ field: "Downloads", direction: "desc" }],
maxRecords: 10,
view: "Published",
})
.all()
records.forEach((record) => {
console.log({
id: record.id,
name: record.get("Name"),
downloads: record.get("Downloads"),
tags: record.get("Tags"),
status: record.get("Status"),
})
})
Create and update records
import Airtable from "airtable"
const base = new Airtable({ apiKey: process.env.AIRTABLE_TOKEN })
.base(process.env.AIRTABLE_BASE_ID!)
// Create records (batch up to 10):
const created = await base("Packages").create([
{
fields: {
Name: "react",
Description: "UI library for building interfaces",
Downloads: 25000000,
Tags: ["frontend", "ui"],
Status: "Active",
Website: "https://react.dev",
},
},
{
fields: {
Name: "vue",
Description: "Progressive JavaScript framework",
Downloads: 5000000,
Tags: ["frontend", "ui"],
Status: "Active",
},
},
])
// Update records:
await base("Packages").update([
{
id: "rec123...",
fields: {
Downloads: 26000000,
"Last Updated": new Date().toISOString(),
},
},
])
// Upsert (update or create):
await base("Packages").update(
[{ fields: { Name: "react", Downloads: 26000000 } }],
{ performUpsert: { fieldsToMergeOn: ["Name"] } }
)
Linked records and formulas
import Airtable from "airtable"
const base = new Airtable({ apiKey: process.env.AIRTABLE_TOKEN })
.base(process.env.AIRTABLE_BASE_ID!)
// Create with linked records:
await base("Versions").create([
{
fields: {
Version: "19.0.0",
Package: ["rec123..."], // Link to Packages table
"Release Date": "2026-03-09",
"Breaking Changes": true,
},
},
])
// Query with linked records expanded:
const packages = await base("Packages")
.select({
fields: ["Name", "Downloads", "Latest Version"],
// "Latest Version" is a lookup/rollup from Versions table
})
.all()
// Computed fields are read-only:
// - Formulas: calculated server-side
// - Rollups: aggregate linked records
// - Lookups: pull fields from linked records
Webhooks and automations
// Airtable webhook (receive changes):
// POST /api/webhooks/airtable
app.post("/api/webhooks/airtable", async (req, res) => {
const { base, table, record, action } = req.body
if (action === "create" && table === "Packages") {
// New package added — trigger analysis:
await analyzePackage(record.fields.Name)
}
if (action === "update" && record.fields.Status === "Published") {
// Published — deploy content:
await deployContent(record)
}
res.sendStatus(200)
})
Google Sheets API
Google Sheets API — spreadsheet access:
Read data
import { google } from "googleapis"
const auth = new google.auth.GoogleAuth({
keyFile: "service-account.json",
scopes: ["https://www.googleapis.com/auth/spreadsheets"],
})
const sheets = google.sheets({ version: "v4", auth })
// Read a range:
const response = await sheets.spreadsheets.values.get({
spreadsheetId: process.env.SHEET_ID!,
range: "Packages!A2:E", // Skip header row
})
const rows = response.data.values || []
rows.forEach(([name, downloads, version, tags, status]) => {
console.log({ name, downloads: Number(downloads), version, tags, status })
})
// Read multiple ranges:
const multi = await sheets.spreadsheets.values.batchGet({
spreadsheetId: process.env.SHEET_ID!,
ranges: ["Packages!A2:E", "Downloads!A2:C", "Metadata!A1:B10"],
})
multi.data.valueRanges?.forEach((range) => {
console.log(range.range, range.values?.length, "rows")
})
Write data
import { google } from "googleapis"
const sheets = google.sheets({ version: "v4", auth })
// Append rows:
await sheets.spreadsheets.values.append({
spreadsheetId: process.env.SHEET_ID!,
range: "Packages!A:E",
valueInputOption: "USER_ENTERED",
requestBody: {
values: [
["react", 25000000, "19.0.0", "frontend,ui", "Active"],
["vue", 5000000, "3.5.0", "frontend,ui", "Active"],
],
},
})
// Update specific cells:
await sheets.spreadsheets.values.update({
spreadsheetId: process.env.SHEET_ID!,
range: "Packages!B2", // Downloads column, row 2
valueInputOption: "RAW",
requestBody: {
values: [[26000000]],
},
})
// Batch update multiple ranges:
await sheets.spreadsheets.values.batchUpdate({
spreadsheetId: process.env.SHEET_ID!,
requestBody: {
valueInputOption: "USER_ENTERED",
data: [
{ range: "Packages!B2", values: [[26000000]] },
{ range: "Packages!B3", values: [[5500000]] },
{ range: "Metadata!B1", values: [[new Date().toISOString()]] },
],
},
})
Formatting and sheets management
import { google } from "googleapis"
const sheets = google.sheets({ version: "v4", auth })
// Create new sheet:
await sheets.spreadsheets.batchUpdate({
spreadsheetId: process.env.SHEET_ID!,
requestBody: {
requests: [
{
addSheet: {
properties: {
title: "March 2026 Report",
gridProperties: { rowCount: 1000, columnCount: 10 },
},
},
},
],
},
})
// Format cells (bold header, background color):
await sheets.spreadsheets.batchUpdate({
spreadsheetId: process.env.SHEET_ID!,
requestBody: {
requests: [
{
repeatCell: {
range: { sheetId: 0, startRowIndex: 0, endRowIndex: 1 },
cell: {
userEnteredFormat: {
backgroundColor: { red: 0.23, green: 0.51, blue: 0.96 },
textFormat: { bold: true, foregroundColor: { red: 1, green: 1, blue: 1 } },
},
},
fields: "userEnteredFormat(backgroundColor,textFormat)",
},
},
],
},
})
Feature Comparison
| Feature | Notion SDK | Airtable API | Google Sheets API |
|---|---|---|---|
| Data model | Pages + databases | Tables + records | Sheets + cells |
| Rich content | ✅ (blocks, embeds) | ❌ (fields only) | ❌ (cells only) |
| Relational data | ✅ (relations) | ✅ (linked records) | ❌ (manual) |
| Formulas | ✅ (database formulas) | ✅ (powerful) | ✅ (spreadsheet) |
| Attachments | ✅ (files, images) | ✅ (attachments) | ❌ (links only) |
| Webhooks | ❌ | ✅ (automations) | ✅ (Apps Script) |
| Real-time | ❌ | ❌ | ❌ |
| Rate limits | 3 req/s | 5 req/s | 60 req/min |
| Row limit | Unlimited | 50K/base (free) | 10M cells |
| TypeScript | ✅ | ✅ (@types) | ✅ |
| Auth | API token | API token | OAuth2/Service Account |
| Free tier | Generous | 1,200 records | 15 GB Drive |
| Pricing | Per-user | Per-seat | Free/Workspace |
When to Use Each
Use Notion SDK if:
- Building integrations with Notion workspaces
- Need rich content management (pages, blocks, embeds)
- Building knowledge bases or content pipelines
- Want to automate Notion databases programmatically
Use Airtable API if:
- Need a relational database with a visual UI
- Building app backends with linked records and views
- Want powerful formulas and automations
- Need attachments and file management
Use Google Sheets API if:
- Need spreadsheet automation and reporting
- Building data pipelines that output to spreadsheets
- Want the most familiar interface for non-technical users
- Need integration with the Google Workspace ecosystem
Methodology
Download data from npm registry (weekly average, February 2026). Feature comparison based on @notionhq/client v2.x, airtable v0.12.x, and googleapis v140.x.