Почему Notion нуждается в автоматизации
Notion стал стандартом для управления знаниями, проектами и документацией в IT-компаниях и стартапах. Но чем больше
данных в Notion, тем больше ручной работы: обновление статусов, перенос данных из CRM, создание отчётов, актуализация
контент-плана.
Notion API, выпущенный в стабильной версии, открывает возможности для полной автоматизации. В связке с Python вы
получаете мощный инструмент, который может: автоматически создавать и обновлять страницы, синхронизировать данные между
Notion и другими системами, строить отчёты и дашборды.
В этом гайде мы разберём всё — от получения API-ключа до продакшн-деплоя. Если вас
интересует автоматизация бизнес-процессов в целом, Notion API — отличная точка входа.
Шаг 1: Настройка Notion API
Создание интеграции
Прежде чем писать код, нужно создать интеграцию в Notion:
- Перейдите на developers.notion.com
- Нажмите «New integration»
- Дайте имя (например, «Python Automation»)
- Выберите workspace, к которому будет доступ
- Настройте Capabilities — какие действия разрешены (Read content, Update content, Insert content)
- Скопируйте Internal Integration Token — это ваш API-ключ
Настройка доступа к базам данных
Важный момент, о котором многие забывают: интеграция не имеет доступа ни к чему по умолчанию. Нужно явно «расшарить»
каждую базу данных или страницу:
- Откройте нужную базу данных в Notion
- Нажмите «...» (меню) → «Add connections»
- Найдите вашу интеграцию и подключите её
Без этого шага API будет возвращать пустые результаты, и вы потратите часы на отладку несуществующей ошибки.
Получение ID базы данных
ID базы данных — это часть URL. Если URL выглядит так:
https://www.notion.so/workspace/abc123def456...?v=...
То ID — это abc123def456... (32 символа между последним / и ?).
Шаг 2: Настройка Python-окружения
Установка библиотек
Для работы с Notion API используем официальный SDK:
pip install notion-client python-dotenv
Базовая конфигурация
Создайте файл .env для безопасного хранения ключей:
NOTION_TOKEN=secret_abc123...
NOTION_DATABASE_ID=abc123def456...
И файл config.py:
import os
from dotenv import load_dotenv
from notion_client import Client
load_dotenv()
notion = Client(auth=os.environ["NOTION_TOKEN"])
DATABASE_ID = os.environ["NOTION_DATABASE_ID"]
Теперь у вас есть настроенный клиент, готовый к работе. Никогда не храните токены в коде — это базовое правило
безопасности при Python-разработке.
Шаг 3: Чтение данных из базы
Получение всех записей
def get_all_records(database_id: str) -> list:
"""Получает все записи из базы данных Notion."""
results = []
has_more = True
start_cursor = None
while has_more:
response = notion.databases.query(
database_id=database_id,
start_cursor=start_cursor
)
results.extend(response["results"])
has_more = response["has_more"]
start_cursor = response.get("next_cursor")
return results
Обратите внимание на пагинацию — Notion API возвращает максимум 100 записей за запрос. Для больших баз данных (1000+
записей) обязательно нужно обрабатывать has_more и next_cursor.
Фильтрация и сортировка
Notion API поддерживает мощные фильтры прямо на уровне запроса:
def get_active_tasks(database_id: str) -> list:
"""Получает активные задачи со статусом 'В работе'."""
response = notion.databases.query(
database_id=database_id,
filter={
"and": [
{
"property": "Статус",
"select": {"equals": "В работе"}
},
{
"property": "Приоритет",
"select": {"equals": "Высокий"}
}
]
},
sorts=[
{"property": "Дедлайн", "direction": "ascending"}
]
)
return response["results"]
Фильтрация на стороне API работает быстрее, чем загрузка всех данных и фильтрация в Python. Всегда используйте серверные
фильтры, когда это возможно.
Извлечение значений свойств
Данные в Notion API имеют вложенную структуру. Вот утилита для извлечения значений:
def extract_property(page: dict, prop_name: str) -> str:
"""Извлекает значение свойства из страницы Notion."""
prop = page["properties"].get(prop_name, {})
prop_type = prop.get("type", "")
if prop_type == "title":
return prop["title"][0]["plain_text"] if prop["title"] else ""
elif prop_type == "rich_text":
return prop["rich_text"][0]["plain_text"] if prop["rich_text"] else ""
elif prop_type == "select":
return prop["select"]["name"] if prop["select"] else ""
elif prop_type == "number":
return prop["number"]
elif prop_type == "date":
return prop["date"]["start"] if prop["date"] else ""
elif prop_type == "email":
return prop["email"] or ""
return ""
Шаг 4: Создание и обновление страниц
Создание новой записи
def create_task(title: str, status: str, priority: str, deadline: str):
"""Создаёт новую задачу в базе данных Notion."""
notion.pages.create(
parent={"database_id": DATABASE_ID},
properties={
"Название": {
"title": [{"text": {"content": title}}]
},
"Статус": {
"select": {"name": status}
},
"Приоритет": {
"select": {"name": priority}
},
"Дедлайн": {
"date": {"start": deadline}
}
}
)
Обновление существующей записи
def update_task_status(page_id: str, new_status: str):
"""Обновляет статус задачи."""
notion.pages.update(
page_id=page_id,
properties={
"Статус": {
"select": {"name": new_status}
}
}
)
Добавление контента в страницу
Notion различает свойства страницы (properties) и содержимое (blocks). Чтобы добавить текст, списки, код — нужно
работать с блоками:
def add_content_to_page(page_id: str, text: str):
"""Добавляет текстовый блок в страницу."""
notion.blocks.children.append(
block_id=page_id,
children=[
{
"object": "block",
"type": "heading_2",
"heading_2": {
"rich_text": [{"text": {"content": "Автоматический отчёт"}}]
}
},
{
"object": "block",
"type": "paragraph",
"paragraph": {
"rich_text": [{"text": {"content": text}}]
}
}
]
)
Шаг 5: Реальные сценарии автоматизации
Сценарий 1: Синхронизация CRM с Notion
Архитектура:
┌──────────┐ ┌──────────────┐ ┌──────────┐
│ amoCRM │────▶│ Python-скрипт│────▶│ Notion │
│ │◀────│ (sync.py) │◀────│ DB │
└──────────┘ └──────────────┘ └──────────┘
│ │ │
└────────────────┼────────────────────┘
│
Cron (каждые 5 мин)
Скрипт каждые 5 минут проверяет новые сделки в CRM и создаёт/обновляет записи в Notion:
def sync_crm_to_notion():
"""Синхронизирует сделки из CRM в Notion."""
# Получаем сделки из CRM
deals = crm_client.get_deals(modified_since=last_sync_time)
for deal in deals:
# Проверяем, есть ли уже запись в Notion
existing = find_notion_page_by_crm_id(deal["id"])
if existing:
update_notion_deal(existing["id"], deal)
else:
create_notion_deal(deal)
save_last_sync_time()
Этот сценарий позволяет менеджерам видеть актуальную воронку продаж в Notion, а аналитикам — строить отчёты без доступа
к CRM.
Сценарий 2: Автоматизация контент-плана
def create_content_plan(topics: list, start_date: str):
"""Создаёт контент-план в Notion на основе списка тем."""
current_date = datetime.strptime(start_date, "%Y-%m-%d")
for topic in topics:
notion.pages.create(
parent={"database_id": CONTENT_DB_ID},
properties={
"Тема": {"title": [{"text": {"content": topic["title"]}}]},
"Статус": {"select": {"name": "Запланировано"}},
"Автор": {"people": [{"id": topic["author_id"]}]},
"Дата публикации": {"date": {"start": current_date.isoformat()}},
"Канал": {"multi_select": [{"name": ch} for ch in topic["channels"]]},
"SEO-ключи": {"rich_text": [{"text": {"content": ", ".join(topic["keywords"])}}]}
}
)
current_date += timedelta(days=topic.get("interval", 3))
Сценарий 3: Автоматические отчёты
Скрипт, который каждую пятницу генерирует сводку по задачам:
def generate_weekly_report():
"""Генерирует еженедельный отчёт в Notion."""
tasks = get_all_records(TASKS_DB_ID)
completed = [t for t in tasks if extract_property(t, "Статус") == "Готово"]
in_progress = [t for t in tasks if extract_property(t, "Статус") == "В работе"]
overdue = [t for t in tasks if is_overdue(t)]
report_text = f"""
Завершено задач: {len(completed)}
В работе: {len(in_progress)}
Просрочено: {len(overdue)}
"""
# Создаём страницу отчёта
report_page = notion.pages.create(
parent={"database_id": REPORTS_DB_ID},
properties={
"Название": {"title": [{"text": {"content": f"Отчёт за неделю {date.today().isocalendar()[1]}"}}]},
"Дата": {"date": {"start": date.today().isoformat()}}
}
)
add_content_to_page(report_page["id"], report_text)
Такие отчёты можно автоматически отправлять в Telegram-бот для руководителя, дублируя информацию из
Notion.
Шаг 6: Вебхуки и триггеры
Проблема: Notion API не поддерживает вебхуки
На момент написания статьи Notion API не предоставляет нативные вебхуки. Это значит, что вы не можете получать
уведомления при изменении данных в реальном времени. Есть несколько обходных решений:
Polling (опрос): запрашивайте данные по расписанию и сравнивайте с предыдущим состоянием.
def poll_for_changes(database_id: str, interval: int = 60):
"""Опрашивает базу данных на предмет изменений."""
last_edit_time = load_last_check_time()
response = notion.databases.query(
database_id=database_id,
filter={
"timestamp": "last_edited_time",
"last_edited_time": {"after": last_edit_time}
}
)
if response["results"]:
for page in response["results"]:
handle_change(page)
save_last_check_time(datetime.now().isoformat())
n8n или Make: используйте специализированные инструменты автоматизации с готовыми Notion-триггерами. Мы подробно
сравниваем эти инструменты в статье n8n vs Make.
Notion + Zapier/Make: как промежуточное решение можно настроить триггер в Make, который будет вызывать ваш
Python-скрипт через webhook.
Шаг 7: Обработка ошибок и лимиты
Rate limits Notion API
Notion API ограничивает количество запросов: максимум 3 запроса в секунду на одну интеграцию. При превышении лимита вы
получите ошибку 429 (Too Many Requests).
Реализуйте retry с exponential backoff:
import time
from notion_client import APIResponseError
def safe_api_call(func, *args, max_retries=3, **kwargs):
"""Выполняет API-вызов с retry-логикой."""
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except APIResponseError as e:
if e.status == 429:
wait_time = 2 ** attempt
print(f"Rate limit, ожидание {wait_time}с...")
time.sleep(wait_time)
elif e.status == 409:
# Конфликт — данные изменились
print("Конфликт данных, повторная попытка...")
time.sleep(1)
else:
raise
raise Exception(f"Не удалось выполнить запрос после {max_retries} попыток")
Лимиты на размер данных
- Максимальный размер текстового блока: 2000 символов
- Максимум 100 блоков за один запрос
- Максимум 100 записей в ответе на query
Для больших объёмов данных всегда используйте пагинацию и разбивайте длинные тексты на блоки.
Логирование
Для production-сценариев обязательно ведите логи:
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("notion_sync.log"),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
Шаг 8: Деплой и автоматизация запуска
Вариант 1: Cron на сервере
Самый простой способ — добавить скрипт в crontab:
# Синхронизация каждые 5 минут
*/5 * * * * cd /opt/notion-sync && /usr/bin/python3 sync.py >> /var/log/notion-sync.log 2>&1
Вариант 2: Cloud Functions
Для серверлесс-подхода используйте AWS Lambda, Google Cloud Functions или Yandex Cloud Functions:
# handler.py для Yandex Cloud Functions
import json
def handler(event, context):
"""Обработчик для Cloud Function."""
sync_crm_to_notion()
return {
"statusCode": 200,
"body": json.dumps({"status": "ok", "synced": True})
}
Настройте триггер по расписанию (Cloud Scheduler / EventBridge).
Вариант 3: Docker + Docker Compose
Для более надёжного деплоя оберните скрипт в Docker:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "main.py"]
# docker-compose.yml
version: "3.8"
services:
notion-sync:
build: .
env_file: .env
restart: unless-stopped
Архитектура production-решения
┌─────────────┐ ┌──────────────┐ ┌──────────┐
│ Источники │ │ Middleware │ │ Notion │
│ │ │ │ │ │
│ ● CRM │───▶│ ● Queue │───▶│ ● DB 1 │
│ ● Email │ │ ● Workers │ │ ● DB 2 │
│ ● Forms │ │ ● Rate limiter│ │ ● Pages │
│ ● Telegram │ │ ● Error handler│ │ │
└─────────────┘ └──────────────┘ └──────────┘
│
┌──────┴──────┐
│ Мониторинг │
│ ● Логи │
│ ● Алерты │
│ ● Метрики │
└─────────────┘
Для серьёзного production-решения рекомендуем использовать очередь сообщений (Redis Queue или RabbitMQ) между
источниками данных и Notion API. Это позволяет обрабатывать пиковые нагрузки, не превышая rate limits.
Типичные ошибки и как их избежать
-
Забыть расшарить базу с интеграцией — самая частая ошибка новичков. API возвращает пустой результат без ошибки.
-
Не обрабатывать пагинацию — если записей больше 100, вы получите только первую страницу. Всегда проверяйте
has_more.
-
Хардкод ID баз данных — используйте переменные окружения или конфиг-файл. ID может измениться при переносе базы.
-
Игнорировать rate limits — без retry-логики скрипт упадёт при большом объёме данных.
-
Не валидировать данные — перед отправкой в Notion проверяйте типы данных и длину строк.
Заключение
Notion API в связке с Python — мощный инструмент для автоматизации рабочих процессов. Вы можете
синхронизировать данные между любыми системами, автоматизировать рутинные операции и строить отчёты без ручной работы.
Начните с простого сценария — например, автоматического создания задач из формы обратной связи. Когда убедитесь, что всё
работает — масштабируйте решение, добавляя синхронизацию с CRM, контент-планы и дашборды.
Если вам нужна помощь с настройкой интеграций или вы хотите автоматизировать процессы в Notion для вашей
команды — свяжитесь с нами. Мы настроим синхронизацию, напишем кастомные скрипты и обеспечим поддержку.