Unofficial Python client for official Notion API
Project description
pytion
Independent unofficial Python client for the official Notion API (for internal integrations only)
Client is built with its own object model based on API
So if you are using notion.so and want to automate some stuff with the original API, you're welcome!
You can read any available data, create basic models, and even work with databases.
Current Notion API version = "2022-06-28"
*does not use notion-sdk-py client
See Change Log
Contents
Quick start
pip install pytion
Create new integration and get your Notion API Token at notion.so -> here.
Invite your new integration 'manager' to your pages or databases.
from pytion import Notion; no = Notion(token=SOME_TOKEN)
Or put your token for Notion API into file token
at script directory and use simple no = Notion()
from pytion import Notion
no = Notion(token=SOME_TOKEN)
page = no.pages.get("PAGE ID") # retrieve page data (not content) and create object
blocks = page.get_block_children() # retrieve page content and create list of objects
database = no.databases.get("Database ID") # retrieve database data (not content) and create object
# retrieve database content by filtering with sorting
pages = database.db_filter(property_name="Done", property_type="checkbox", value=False, descending="title")
In [1]: from pytion import Notion
In [2]: no = Notion(token=SOME_TOKEN)
In [3]: page = no.pages.get("a458613160da45fa96367c8a594297c7")
In [4]: print(page)
Notion/pages/Page(Example page)
In [5]: blocks = page.get_block_children_recursive()
In [6]: print(blocks)
Notion/blocks/BlockArray(## Migration planning [x] Rese)
In [7]: print(blocks.obj)
## Migration planning
[x] Reset new switch 2022-05-12T00:00:00.000+03:00 → 2022-05-13T01:00:00.000+03:00
- reboot
- hold reset button
[x] Connect to console with baud rate 9600
[ ] Skip default configuration dialog
Use LinkTo(configs)
[Integration changes](https://developers.notion.com/changelog?page=2)
In [8]: print(blocks.obj.simple)
Migration planning
Reset new switch 2022-05-12T00:00:00.000+03:00 → 2022-05-13T01:00:00.000+03:00
reboot
hold reset button
Connect to console with baud rate 9600
Skip default configuration dialog
Use https://api.notion.com/v1/pages/90ea1231865f4af28055b855c2fba267
https://developers.notion.com/changelog?page=2
Pytion API
Almost all operations are carried out through Notion
or Element
object:
page = no.pages.get("a458613160da45fa96367c8a594297c7")
# no -> Notion
# pages -> URI in https://api.notion.com/v1/PAGES/a458613160da45fa96367c8a594297c7
# get -> pytion API method
# page -> Element
# page.obj -> Page (main data structure)
so:
isinstance(no, Notion) == True
isinstance(no.pages, Element) == True
isinstance(no.databases, Element) == True
isinstance(page, Element) == True
isinstance(page.obj, Page) == True
and if you want to retrieve a database - you must use "databases" URI
database = no.databases.get("123412341234")
and the same applies for "blocks" and "users". Valid URI-s are:
- pages
- blocks
- databases
- users
When you work with existing Element
object like page
above, all methods below will be applied to this Page:
new_page = page.page_update(title="new page name 2")
# new_page is not a new page, it is updated page
# new_page.obj is equal page.obj except title and last_edited properties
Search
There is a search example:
no = Notion(token)
r = no.search("updating", object_type="page")
print(r.obj)
# output:
# Page for updating
# Page to updating databases
pytion.api.Element
There is a list of available methods for communicate with api.notion.com. These methods are better structured in next chapter.
.get(id_)
- Get Element by ID.
.get_parent(id_)
- Get parent object of current object if possible.
.get_block_children(id_, limit)
- Get children Block objects of current Block object (tabulated texts) if exist.
.get_block_children_recursive(id_, max_depth, limit, force)
- Get children Block objects of current Block object (tabulated texts) if exist recursive.
.get_page_property(property_id, id_, limit)
- Retrieve a page property item.
.get_page_properties(title_only, obj)
- Retrieve the title or all properties of current Page or Page obj
(deprecated, useful for v1.3.0 only)
.db_query(id_, limit, filter_, sorts)
- Query Database.
.db_filter(...see desc...)
- Query Database.
.db_create(database_obj, parent, properties, title)
- Create Database.
There is no way to delete a database object yet!
.db_update(id_, title, properties)
- Update Database.
.page_create(page_obj, parent, properties, title)
- Create Page.
.page_update(id_, properties, title, archived)
- Update Page.
.block_update(id_, block_obj, new_text, archived)
- Update text in Block.
.block_append(id_, block, blocks)
- Append block or blocks children.
.get_myself()
- Retrieve my bot User.
.from_linkto(linkto)
- Creates new Element object based on LinkTo information.
.from_object(model)
- Creates new Element object from Page, Block or Database object. Usable while Element object contains an Array.
More details and usage examples of these methods you can see into func descriptions.
Models
pytion.models
There are classes based on API structures:
RichText
based on Rich text objectRichTextArray
is a list of RichText objects with useful methods- You can create object by simple
RichTextArray.create("My title text")
and then use it in any methods - Any Rich Text getting from API will be RichTextArray
str()
returns plain text of all "Rich Texts". ez+
operator is available withstr
andRichTextArray
.simple
property returns stripped plain text without any markdown syntax. useful for URL
- You can create object by simple
Database
based on Database object- You can create object
Database.create(...)
and/or use.db_create(...)
API method - attrs represent API model. Complex structures like
created_by
are wrapped in internal objects - use
.db_update()
API method for modify a real database (for ex. properties or title) - use
.db_query()
to get all database content (it will bePageArray
) - use
.db_filter()
to get database content with filtering and/or sorting
- You can create object
Page
based on Page object- You can create object
Page.create(...)
and/or use.page_create(...)
API method - use
.page_update()
method to modify attributes or delete the page - use
.get_block_children()
to get page content (without nested blocks) (it will beBlockArray
) - use
.get_block_children_recursive()
to get page content with nested blocks - use
.get_page_property()
to retrieve the specificPropertyValue
of the page
- You can create object
Block
based on Block object- You can create object
Block.create(...)
of specific type from support matrix below and then use it while creating pages or appending - use
.block_update()
to replace content or change extension attributes or delete the block - use
.block_append()
to add a new block to a page or add a nested block to another block - use
.get_block_children()
to get first level nested blocks - use
.get_block_children_recursive()
to get all levels nested blocks
- You can create object
User
based on User object- You can create object
User.create(...)
and use it in some properties likepeople
type property - You can retrieve more data about a User by his ID using
.get()
- use
.get_myself()
to retrieve the current bot User
- You can create object
Property
based on Property object- You can create object
Property.create(...)
while creating or editing database:.db_create()
or.db_update()
formula
,rollup
type properties configuration is not supported
- You can create object
PropertyValue
based on Property values- You can create object
PropertyValue.create(...)
to set or edit page properties by.page_create()
or.page_update()
files
,formula
,rollup
type properties are not editable
- You can create object
There are also useful internal classes:
BlockArray
is found when API returns page content in "list of blocks" format- it is useful to represent all content by
str()
- also it has
simple
property likeRichTextArray
object - it automatically indents
str
output of nested blocks
- it is useful to represent all content by
PageArray
is found when API returns the result of database query (list of pages)LinkTo
is basic internal model to link to any Notion object- You can create object
LinkTo.create()
and use it in many places and methods - use
LinkTo(from_object=my_page1)
to quickly create a link to any existing object of pytion.models link
property ofLinkTo
returns expanded URL
- You can create object
ElementArray
is found while using.search()
endpoint. It's a parent ofPageArray
And every model has a
.get()
method that returns API friendly JSON.
Supported block types
At present the API only supports the block types which are listed in the reference below. Any unsupported block types will continue to appear in the structure, but only contain a type
set to "unsupported"
.
Colors are not yet supported.
Every Block has mandatory attributes and extension attributes. There are mandatory:
id: str
- UUID-64 without hyphensobject: str
- always"block"
(from API)created_time: datetime
- from APIcreated_by: User
- from APIlast_edited_time: datetime
- from APIlast_edited_by: User
- from APItype: str
- the type of block (from API)has_children: bool
- does the block have children blocks (from API)archived: bool
- does the block marked as deleted (from API)text: Union[str, RichTextArray]
- main contentsimple: str
- only simple text string (url expanded)
Extension attributes are listed below in support matrix:
Block Type | Description | Read support | Create support | Can have children | Extension attributes |
---|---|---|---|---|---|
paragraph |
Simple Block with text | + | + | + | |
heading_1 |
Heading Block with text highest level | + | - | - | |
heading_2 |
Heading Block with text medium level | + | - | - | |
heading_3 |
Heading Block with text lowest level | + | - | - | |
bulleted_list_item |
Text Block with bullet | + | - | + | |
numbered_list_item |
Text Block with number | + | - | + | |
to_do |
Text Block with checkbox | + | + | + | checked: bool |
toggle |
Text Block with toggle to children blocks | + | - | + | |
code |
Text Block with code style | + | + | + | language: str , caption: RichTextArray |
child_page |
Page inside | + | - | + | |
child_database |
Database inside | + | - | + | |
embed |
Embed online content | + | - | - | caption: RichTextArray |
image |
Embed image content | + | - | - | caption: RichTextArray , expiry_time: datetime |
video |
Embed video content | + | - | - | caption: RichTextArray , expiry_time: datetime |
file |
Embed file content | + | - | - | caption: RichTextArray , expiry_time: datetime |
pdf |
Embed pdf content | + | - | - | caption: RichTextArray , expiry_time: datetime |
bookmark |
Block for URL Link | + | - | - | caption: RichTextArray |
callout |
Highlighted footnote text Block | + | - | + | icon: dict |
quote |
Text Block with quote style | + | - | + | |
equation |
KaTeX compatible text Block | + | - | - | |
divider |
Simple line to divide the page | + | - | - | |
table_of_contents |
Block with content structure in the page | + | - | - | |
column |
- | - | + | ||
column_list |
- | - | - | ||
link_preview |
Same as bookmark |
+ | - | - | |
synced_block |
Block for synced content aka parent | + | - | + | synced_from: LinkTo |
template |
Template Block title | + | - | + | |
link_to_page |
Block with link to particular page @... |
+ | - | - | link: LinkTo |
table |
Table Block with some attrs | + | - | + | table_width: int |
table_row |
Children Blocks with table row content | + | - | - | |
breadcrumb |
Empty Block actually | + | - | - | |
unsupported |
Blocks unsupported by API | + | - | - |
API converts toggle heading Block to simple heading Block.
Supported Property types
Property type | value type | read (DB) | read value (Page) | create (DB) | create value (Page) | Oper attrs | Config attrs |
---|---|---|---|---|---|---|---|
title |
RichTextArray |
+ | + | + | + | ||
rich_text |
RichTextArray |
+ | + | + | + | ||
number |
int /float |
+ | + | + | + | ||
select |
str |
+ | + | + | + | ||
multi_select |
List[str] |
+ | + | + | + | ||
status |
str |
+ | + | + | +**** | options , groups (read-only) |
|
date |
str |
+ | + | + | + | start: datetime end: datetime * |
|
people |
List[User] |
+ | + | + | +** | ||
files |
+ | - | + | - | |||
checkbox |
bool |
+ | + | + | + | ||
url |
str |
+ | + | + | + | ||
email |
str |
+ | + | + | + | ||
phone_number |
str |
+ | + | + | + | ||
formula |
- | + | - | - | |||
relation |
List[LinkTo] |
+ | + | + | + | single_property /dual_property |
|
rollup |
depends on relation | - | + | - | - | ||
created_time *** |
datetime |
+ | + | + | - | ||
created_by *** |
User |
+ | + | + | - | ||
last_edited_time *** |
datetime |
+ | + | + | - | ||
last_edited_by *** |
User |
+ | + | + | - |
[*] - Create examples:
pv = PropertyValue.create(type_="date", value=datetime.now())
pv = PropertyValue.create(type_="date", date={"start": str(datetime(2022, 2, 1, 5)), "end": str(datetime.now())})
[**] - Create example:
user = User.create('1d393ffb5efd4d09adfc2cb6738e4812')
pv = PropertyValue.create(type_="people", value=[user])
[***] - Every Base model like Page already has mandatory attributes created/last_edited returned by API
[****] - Status type is not configurable. API doesn't support NEW options added via Property modify or updating a Page
Block creating examples
Create paragraph
block object and add it to Notion:
from pytion.models import Block
my_text_block = Block.create("Hello World!")
my_text_block = Block.create(text="Hello World!", type_="paragraph") # the same
# indented append my block to other known block:
no.blocks.block_append("5f60073a9dda4a9c93a212a74a107359", block=my_text_block)
# append my block to a known page (in the end)
no.blocks.block_append("9796f2525016128d9af4bf12b236b555", block=my_text_block) # the same operation actually
# another way to append:
my_page = no.pages.get("9796f2525016128d9af4bf12b236b555")
my_page.block_append(block=my_text_block)
Create to_do
block object:
from pytion.models import Block
my_todo_block = Block.create("create readme documentation", type_="to_do")
my_todo_block2 = Block.create("add 'create' method", type_="to_do", checked=True)
Create code
block object:
from pytion.models import Block
my_code_block = Block.create("code example here", type_="code", language="javascript")
my_code_block2 = Block.create("another code example", type_="code", caption="it will be plain text code block with caption")
Logging
Logging is muted by default. To enable to stdout and/or to file:
from pytion import setup_logging
setup_logging(level="debug", to_console=True, filename="pytion.log")
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file pytion-1.3.2.tar.gz
.
File metadata
- Download URL: pytion-1.3.2.tar.gz
- Upload date:
- Size: 48.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.0 CPython/3.8.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c5cff5f35fe3e23fd4dc7d81bbabeaa23dd9ed7775d3dadf77dd9bc60d8973b5 |
|
MD5 | b362c15937841feb26f6a0f174b6d944 |
|
BLAKE2b-256 | 75e7e96ce24f298ef24752bc2dacaf6ffbfddc79d96f67a6cb2853a766092791 |
File details
Details for the file pytion-1.3.2-py3-none-any.whl
.
File metadata
- Download URL: pytion-1.3.2-py3-none-any.whl
- Upload date:
- Size: 46.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.0 CPython/3.8.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f82c8a1cf7629afdfd806a663f66ad79ada708e6f2340efbc653981365937955 |
|
MD5 | e4f3f98c1fba4ccd7264aed37f741611 |
|
BLAKE2b-256 | 88827aed617343222e8222ad5a916232cc1766e271901f9a4fdd875e723c3034 |