Автоматизация Notion через API: сводки и теги для базы с помощью Claude

Скрипт на Python читает записи базы Notion, получает от Claude сводку и теги и записывает их в свойства страницы. С учётом data sources и лимитов Notion API.

5 мин · · автоматизация · разработка

Заметки, протоколы встреч и входящие заявки в Notion быстро копятся, и разбирать их вручную долго. Автоматизация Notion через API решает это: скрипт берёт новые записи базы, отправляет текст в Claude и записывает обратно короткую сводку и теги. Ниже — рабочий скрипт на Python с актуальной версией notion-client, которая работает с базами через data sources.

Что понадобится

  • Python 3.10+ и пакеты:
bash
pip install notion-client openai python-dotenv
  • Токен Notion. По документации Notion есть два варианта: внутреннее подключение (internal connection) — отдельный бот-пользователь, которому нужно дать доступ к базе, или персональный токен (personal access token), который действует с вашими правами. Оба создаются в Developer portal Notion. Для командной автоматизации документация советует внутреннее подключение.
  • Ключ Zukko вида fetch_... из кабинета.

Если используете внутреннее подключение, откройте базу в Notion и выдайте подключению доступ к ней: без этого API вернёт ошибку «not found», даже если ID верный. Доступ к родительской странице наследуется дочерними.

Подготовка базы

В примере база содержит свойства:

СвойствоТипЗачем
NameTitleзаголовок записи
StatusSelectзначения To process и Processed
SummaryTextсюда пишется сводка
TagsMulti-selectсюда пишутся теги

Если у вас колонка статуса типа Status (а не Select), замените в коде ключ "select" на "status" — и в фильтре, и при обновлении.

ID базы берётся из её адреса: это 32 символа перед ?v=. Удобнее передать в скрипт адрес целиком — функция extract_database_id из notion_client.helpers сама вытащит ID.

Файл .env:

NOTION_TOKEN=ntn_... NOTION_DATABASE=https://www.notion.so/workspace/Inbox-0123456789abcdef0123456789abcdef?v=... ZUKKO_API_KEY=fetch_... ZUKKO_MODEL=claude-sonnet-5

Автоматизация Notion через API: базы и data sources

С версии Notion API 2025-09-03 у базы может быть несколько источников данных (data sources), и запрос записей идёт к источнику, а не к базе: POST /v1/data_sources/{data_source_id}/query. Старый метод databases.query в свежем notion-client (ветка 3.x) отсутствует, поэтому код из старых примеров падает с AttributeError.

Порядок такой, как в руководстве по переходу:

  1. notion.databases.retrieve(database_id=...) возвращает базу со списком data_sources.
  2. Берём id нужного источника (у обычной базы он один).
  3. notion.data_sources.query(data_source_id=..., filter=...) возвращает страницы.

Ответы Notion постраничные (has_more, next_cursor). Вспомогательная функция collect_paginated_api из notion_client.helpers сама проходит по всем страницам результата.

Текст страницы

Содержимое страницы — это блоки: notion.blocks.children.list(block_id=page_id). У текстовых блоков (абзацы, заголовки, пункты списков, цитаты) текст лежит в rich_text — массиве фрагментов. Склеиваем plain_text всех фрагментов, а не только первого: иначе обрежется всё после первой ссылки или выделения.

Вложенные блоки (содержимое toggle, вложенные списки) этот код не обходит. Если они важны, вызывайте blocks.children.list рекурсивно для блоков с has_children: true.

Полный скрипт

bash
import json
import os

from dotenv import load_dotenv
from notion_client import Client
from notion_client.helpers import collect_paginated_api, extract_database_id
from openai import APIStatusError, OpenAI

load_dotenv()

notion = Client(auth=os.environ["NOTION_TOKEN"])
llm = OpenAI(base_url="https://api.zukko.pro/v1", api_key=os.environ["ZUKKO_API_KEY"])

DATABASE_ID = extract_database_id(os.environ["NOTION_DATABASE"])

Несколько решений в коде:

  • Один запрос вместо двух. Сводка и теги просим одним JSON-ответом — вдвое меньше запросов и повторной отправки одного и того же текста.
  • Ограничение длины. MAX_CHARS не даёт отправить в модель огромную страницу целиком. Точное число токенов показывает поле usage в ответе или эндпоинт подсчёта токенов.
  • Лимиты Notion. Notion ограничивает частоту запросов (по документации — в среднем 3 запроса в секунду на подключение для большинства тарифов) и длину текстового фрагмента в свойстве (2000 символов). Свежий notion-client сам повторяет запрос после ответа 429; сводку режем до 2000 символов.
  • Запятые в тегах. Notion не принимает запятые в названиях вариантов multi-select, поэтому заменяем их пробелами.

Запуск по расписанию

Скрипт обрабатывает только записи со статусом To process и после обработки меняет статус, поэтому его безопасно запускать повторно. Например, cron раз в час:

bash
0 * * * * cd /opt/notion-bot && .venv/bin/python notion_summary.py >> run.log 2>&1

Подойдёт и GitHub Actions по расписанию — тогда NOTION_TOKEN и ZUKKO_API_KEY храните в секретах репозитория.

Сколько стоит

Цены в Zukko за 1M токенов (вход/выход): claude-sonnet-5 — $0.30 / $0.30, gpt-5.6-luna — $0.10 / $0.10, glm-5.3-flash — $0.04 / $0.04. Актуальный список — GET https://api.zukko.pro/v1/models или страница «Модели» в кабинете.

Пример: 100 заметок, на каждую 3 000 входных токенов и 200 выходных.

  • claude-sonnet-5: 100 × (3 000 + 200) × $0.30 / 1 000 000 = $0.096.
  • glm-5.3-flash: 100 × (3 000 + 200) × $0.04 / 1 000 000 = $0.0128.

Какая модель даёт подходящие сводки и теги для ваших заметок, проверьте на своих задачах: прогоните 20 записей через две модели и сравните результат. Задача несложная, и дешёвая модель может оказаться достаточной.

Частые вопросы

API Notion отвечает «Could not find database»

Чаще всего у подключения нет доступа к базе: выдайте его в меню страницы базы или во вкладке доступа подключения в Developer portal. Вторая причина — ID страницы вместо ID базы: для встроенной (inline) базы откройте её как отдельную страницу и возьмите адрес оттуда.

Почему notion.databases.query не работает?

В notion-client 3.x этого метода нет: начиная с версии API 2025-09-03 записи запрашиваются через источник данных — notion.data_sources.query. ID источника берётся из notion.databases.retrieve.

Модель иногда возвращает текст вместо JSON

Код такие записи пропускает и оставляет статус To process, так что при следующем запуске они обработаются снова. Чтобы ошибок было меньше, держите промпт коротким и явно пишите «только JSON».

Можно ли писать результат в тело страницы, а не в свойство?

Да, через notion.blocks.children.append(block_id=page_id, children=[...]) с блоком paragraph. Для сводки удобнее свойство: по нему можно фильтровать и сортировать базу. Подробнее о запросах к модели — в справочнике chat completions.

zukko

Платите в 25×
меньше

за Claude Code & ChatGPT

Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.