{"repo":"ramnes/notion-sdk-py","free":true,"listed":false,"github":"https://github.com/ramnes/notion-sdk-py","clone":"git clone https://github.com/ramnes/notion-sdk-py.git","description":"Notion API client SDK, rewritten in Python! (sync + async)","language":"Python","stars":2176,"topics":["api-client","async","dataclasses","httpx","notion","notion-api","python","python-client"],"license":"MIT","category":"api_client","readme_excerpt":"<!-- markdownlint-disable -->\n![notion-sdk-py](https://socialify.git.ci/ramnes/notion-sdk-py/image?font=Bitter&language=1&logo=https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F4%2F45%2FNotion_app_logo.png&owner=1&pattern=Circuit%20Board&theme=Light)\n\n<div align=\"center\">\n  <p>\n    <a href=\"https://pypi.org/project/notion-client\"><img src=\"https://img.shields.io/pypi/v/notion-client.svg\" alt=\"PyPI\"></a>\n    <a href=\"tox.ini\"><img src=\"https://img.shields.io/pypi/pyversions/notion-client\" alt=\"Supported Python Versions\"></a>\n    <br/>\n    <a href=\"LICENSE\"><img src=\"https://img.shields.io/github/license/ramnes/notion-sdk-py\" alt=\"License\"></a>\n    <a href=\"https://github.com/ambv/black\"><img src=\"https://img.shields.io/badge/code%20style-black-black\" alt=\"Code style\"></a>\n    <a href=\"https://codecov.io/github/ramnes/notion-sdk-py\"><img src=\"https://codecov.io/gh/ramnes/notion-sdk-py/branch/main/graphs/badge.svg\" alt=\"Coverage\"></a>\n    <a href=\"https://pypistats.org/packages/notion-client\"><img src=\"https://img.shields.io/pypi/dm/notion-client\" alt=\"Package downloads\"></a>\n    <br/>\n    <a href=\"https://github.com/ramnes/notion-sdk-py/actions/workflows/quality.yml\"><img src=\"https://github.com/ramnes/notion-sdk-py/actions/workflows/quality.yml/badge.svg\" alt=\"Code Quality\"></a>\n    <a href=\"https://github.com/ramnes/notion-sdk-py/actions/workflows/test.yml\"><img src=\"https://github.com/ramnes/notion-sdk-py/actions/workflows/test.yml/badge.svg\" alt=\"Tests\"></a>\n    <a href=\"https://github.com/ramnes/notion-sdk-py/actions/workflows/docs.yml\"><img src=\"https://github.com/ramnes/notion-sdk-py/actions/workflows/docs.yml/badge.svg\" alt=\"Docs\"></a>\n  </p>\n</div>\n<!-- markdownlint-enable -->\n\n**_notion-sdk-py_ is a simple and easy to use client library for the official\n[Notion API](https://developers.notion.com/).**\n\nIt is meant to be a Python version of the reference [JavaScript SDK](https://github.com/makenotion/notion-sdk-js),\nso usage should be very similar between both. 😊 (If not, please open an issue\nor PR!)\n\n<!-- markdownlint-disable -->\n## Installation\n<!-- markdownlint-enable -->\n```shell\npip install notion-client\n```\n\n## Usage\n\n> Use Notion's [Getting Started Guide](https://developers.notion.com/docs/getting-started)\n> to get set up to use Notion's API.\n\nImport and initialize a client using an **integration token** or an\nOAuth **access token**.\n\n```python\nimport os\nfrom notion_client import Client\n\nnotion = Client(auth=os.environ[\"NOTION_TOKEN\"])\n```\n\nIn an asyncio environment, use the asynchronous client instead:\n\n```python\nfrom notion_client import AsyncClient\n\nnotion = AsyncClient(auth=os.environ[\"NOTION_TOKEN\"])\n```\n\nMake a request to any Notion API endpoint.\n\n```python\nfrom pprint import pprint\n\nlist_users_response = notion.users.list()\npprint(list_users_response)\n```\n\n> [!NOTE]\n> See the complete list of endpoints in the [API reference](https://developers.notion.com/reference).\n\nor with the asynchronous client:\n\n```python\nlist_users_response = await notion.users.list()\npprint(list_users_response)\n```\n\nThis would output something like:\n\n```text\n{'results': [{'avatar_url': 'https://secure.notion-static.com/e6a352a8-8381-44d0-a1dc-9ed80e62b53d.jpg',\n              'id': 'd40e767c-d7af-4b18-a86d-55c61f1e39a4',\n              'name': 'Avocado Lovelace',\n              'object': 'user',\n              'person': {'email': 'avo@example.org'},\n              'type': 'person'},\n             ...]}\n```\n\nAll API endpoints are available in both the synchronous and asynchronous clients.\n\nEndpoint parameters are grouped into a single object. You don't need to remember\nwhich parameters go in the path, query, or body.\n\n```python\nmy_page = notion.data_sources.query(\n    **{\n        \"data_source_id\": \"897e5a76-ae52-4b48-9fdf-e71f5945d1af\",\n        \"filter\": {\n            \"property\": \"Landmark\",\n            \"rich_text\": {\n                \"contains\": \"Bridge\",\n            },\n        },\n    }\n)\n```\n\n### Handling errors\n\nIf the API returns an unsuccessful response, an `APIResponseError` will be raised.\n\nThe error contains properties from the response, and the most helpful is `code`.\nYou can compare `code` to the values in the `APIErrorCode` object to avoid\nmisspelling error codes.\n\n```python\nimport logging\nfrom notion_client import APIErrorCode, APIResponseError, Client\n\ntry:\n    notion = Client(auth=os.environ[\"NOTION_TOKEN\"])\n    my_page = notion.data_sources.query(\n        **{\n            \"data_source_id\": \"897e5a76-ae52-4b48-9fdf-e71f5945d1af\",\n            \"filter\": {\n                \"property\": \"Landmark\",\n                \"rich_text\": {\n                    \"contains\": \"Bridge\",\n                },\n            },\n        }\n    )\nexcept APIResponseError as error:\n    if error.code == APIErrorCode.ObjectNotFound:\n        #\n        # For example: handle by asking the user to select a different data source\n        #\n        ...\n    else:\n        # Other error handling code\n        print(error)\n```\n\n### Logging\n\nThe client emits useful information to a logger. By default, it only emits warnings\nand errors.\n\nIf you're debugging an application, and would like the client to log request & response\nbodies, set the `log_level` option to `logging.DEBUG`.\n\n```python\nimport logging\nfrom notion_client import Client\n\nnotion = Client(\n    auth=os.environ[\"NOTION_TOKEN\"],\n    log_level=logging.DEBUG,\n)\n```\n\nYou may also set a custom `logger` to emit logs to a destination other than `stdout`.\nHave a look at [Python's logging cookbook](https://docs.python.org/3/howto/logging-cookbook.html)\nif you want to create your own logger.\n\n### Client options\n\n`Client` and `AsyncClient` both support the following options on initialization.\nThese options are all keys in the single constructor parameter.\n\n<!-- markdownlint-disable -->\n| Option       | Default value               | Type              | Description                                                                                                                               |\n| ------------ | --------------------------  | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |\n| `auth`       | `None`                      | `string`          | Bearer token for authentication. If left undefined, the `auth` parameter should be set on each request.                                   |\n| `log_level`  | `logging.WARNING`           | `int`             | Verbosity of logs the instance will produce. By default, logs are written to `stdout`.                                                    |\n| `timeout_ms` | `DEFAULT_TIMEOUT_MS`        | `int`             | Number of milliseconds to wait before emitting a `RequestTimeoutError`                                                                    |\n| `base_url`   | `DEFAULT_BASE_URL`          | `string`          | The root URL for sending API requests. This can be changed to test with a mock server.                                                    |\n| `logger`     | Log to console              | `logging.Logger`  | A custom logger.                                                                                                                          |\n| `retry`      | See [constants](#constants) | `RetryOptions`    | Configuration for automatic retries on rate limits (429) and server errors (500, 503). See [Automatic retries](#automatic-retries) below. |\n<!-- markdownlint-enable -->\n\n### Automatic retries\n\nThe client automatically retries requests that fail due to rate limiting or\ntransient server errors. By default, it will retry up to 2 times using\nexponential back-off with jitter.\n\n**Retryable errors:**\n\n- `rate_limited` (HTTP 429) - Too many requests; retried for all HTTP methods\n- `internal_server_error` (HTTP 500) - Server error; retried only for GET and DELETE\n- `service_unavailable` (HTTP 503) - Service temporarily unavailable;\n  retried only for GET and DELETE\n\nServer errors (500, 503) are only retried for idempotent HTTP methods\n(GET, DELETE) to avoid duplicate side effects. Rate limits (429) are\nretried for all methods since the server explicitly asks clients to retry.\n\n**Configuration:**\n\n```python\nfrom notion_client import Client, RetryOptions\n\nnotion = Client(\n    auth=\"secret_...\",\n    retry=RetryOptions(\n        max_retries=5,          # Maximum retry attempts (default: 2)\n        initial_retry_delay_ms=500,  # Initial delay in ms (default: 1000)\n        max_retry_delay_ms=60000,    # Maximum delay in ms (default: 60000)\n    ),\n)\n```\n\nTo disable automatic retries:\n\n```python\nnotion = Client(auth=\"secret_...\", retry=False)\n```\n\n### Constants\n\nThe SDK exports named constants for all default values used by the client, as well\nas useful Notion-specific values. You can import them directly:\n\n```python\nfrom notion_client import (\n    DEFAULT_BASE_URL,          # \"https://api.notion.com\"\n    DEFAULT_TIMEOUT_MS,        # 60_000\n    DEFAULT_MAX_RETRIES,       # 2\n    DEFAULT_INITIAL_RETRY_DELAY_MS,  # 1_000\n    DEFAULT_MAX_RETRY_DELAY_MS,      # 60_000\n    MIN_VIEW_COLUMN_WIDTH,     # 32\n)\n```\n\n`MIN_VIEW_COLUMN_WIDTH` is the minimum width (in pixels) that a table column can\nhave in the Notion UI. Set a property's `width` to this value when creating or\nupdating a view to make a column appear collapsed -- useful for checkbox or\nstatus-as-checkbox columns:\n\n```python\nawait notion.views.create(\n    database_id=database_id,\n    name=\"My view\",\n    type=\"table\",\n    configuration={\n        \"table\": {\n            \"properties\": [\n                {\n                    \"property_id\": checkbox_prop_id,\n                    \"visible\": True,\n                    \"width\": MIN_VIEW_COLUMN_WIDTH,\n                },\n            ],\n        },\n    },\n)\n```\n\n### Full API responses\n\nThe following functions can distinguish between full and partial API responses.\n\n<!-- markdownlint-disable -->\n| Function                      | Purpose                               ","default_branch":"main","files":171,"tree":[".coveragerc",".ecrc",".editorconfig",".github/CONTRIBUTING.md",".github/FUNDING.yml",".github/scripts/known_entries.json",".github/scripts/monitor_notion_changelog.py",".github/workflows/docs.yml",".github/workflows/links.yml",".github/workflows/notion-changelog-monitor.yml",".github/workflows/quality.yml",".github/workflows/test.yml",".gitignore",".pre-commit-config.yaml","LICENSE","README.md","SECURITY.md","docs/SUMMARY.md","docs/extra.css","docs/generate.py","docs/index.md","docs/user_guides/quick_start.md","docs/user_guides/structured_logging.md","examples/database_email_update/.env.example","examples/database_email_update/README.md","examples/database_email_update/database_email_update.py","examples/database_email_update/requirements.txt","examples/databases/README.md","examples/databases/create_database.py","examples/file_uploads/create_file_uploads_extenal.py","examples/file_uploads/create_file_uploads_multi.py","examples/file_uploads/create_file_uploads_single.py","examples/first_project/README.md","examples/first_project/script.py","examples/generate_random_data/.env.example","examples/generate_random_data/README.md","examples/generate_random_data/generate_random_data.py","examples/generate_random_data/requirements.txt","examples/intro_to_notion_api/.env.example","examples/intro_to_notion_api/README.md","examples/intro_to_notion_api/assets/page_id.png","examples/intro_to_notion_api/basic/1_add_block.py","examples/intro_to_notion_api/basic/2_add_linked_block.py","examples/intro_to_notion_api/basic/3_add_styled_block.py","examples/intro_to_notion_api/intermediate/1_create_a_database.py","examples/intro_to_notion_api/intermediate/2_add_page_to_database.py","examples/intro_to_notion_api/intermediate/3_query_database.py","examples/intro_to_notion_api/intermediate/4_sort_database.py","examples/intro_to_notion_api/intermediate/5_upload_file.py","examples/intro_to_notion_api/intermediate/sample_data.py","examples/intro_to_notion_api/requirements.txt","examples/notion_github_sync/.env.example","examples/notion_github_sync/README.md","examples/notion_github_sync/notion_github_sync.py","examples/notion_task_github_pr_sync/.env.example","examples/notion_task_github_pr_sync/README.md","examples/notion_task_github_pr_sync/notion_task_github_pr_sync.py","examples/parse_text_from_any_block_type/.env.example","examples/parse_text_from_any_block_type/README.md","examples/parse_text_from_any_block_type/parse_text_from_any_block_type.py","examples/web_form_with_fastapi/.env.example","examples/web_form_with_fastapi/README.md","examples/web_form_with_fastapi/public/client.js","examples/web_form_with_fastapi/public/style.css","examples/web_form_with_fastapi/views/index.html","examples/web_form_with_fastapi/web_form_with_fastapi.py","mkdocs.yml","notion_client/__init__.py","notion_client/api_endpoints.py","notion_client/client.py","notion_client/constants.py","notion_client/errors.py","notion_client/helpers.py","notion_client/logging.py","notion_client/py.typed","notion_client/typing.py","notion_client/webhooks.py","requirements/base.txt","requirements/dev.txt","requirements/docs.txt","requirements/quality.txt","requirements/tests.txt","setup.cfg","setup.py","tests/__init__.py","tests/cassettes/test_api_async_request_bad_request_error.yaml","tests/cassettes/test_api_http_response_error.yaml","tests/cassettes/test_api_response_error.yaml","tests/cassettes/test_api_response_error_additional_data.yaml","tests/cassettes/test_api_response_error_request_id.yaml","tests/cassettes/test_async_api_response_error.yaml","tests/cassettes/test_async_api_response_error_additional_data.yaml","tests/cassettes/test_async_api_response_error_request_id.yaml","tests/cassettes/test_async_client_request.yaml","tests/cassettes/test_async_client_request_auth.yaml","tests/cassettes/test_async_collect_data_source_templates.yaml","tests/cassettes/test_async_collect_paginated_api.yaml","tests/cassettes/test_async_iterate_data_source_templates.yaml","tests/cassettes/test_async_iterate_paginated_api.yaml","tests/cassettes/test_async_tasks_retrieve.yaml","tests/cassettes/test_blocks_children_create.yaml","tests/cassettes/test_blocks_children_list.yaml","tests/cassettes/test_blocks_delete.yaml","tests/cassettes/test_blocks_retrieve.yaml","tests/cassettes/test_blocks_update.yaml","tests/cassettes/test_build_request_error_integration.yaml","tests/cassettes/test_client_request.yaml","tests/cassettes/test_client_request_auth.yaml","tests/cassettes/test_collect_data_source_templates.yaml","tests/cassettes/test_collect_paginated_api.yaml","tests/cassettes/test_comments_create.yaml","tests/cassettes/test_comments_delete.yaml","tests/cassettes/test_comments_list.yaml","tests/cassettes/test_comments_retrieve.yaml","tests/cassettes/test_comments_update.yaml","tests/cassettes/test_custom_emojis_list.yaml","tests/cassettes/test_data_sources_create.yaml","tests/cassettes/test_data_sources_list_templates.yaml","tests/cassettes/test_data_sources_query.yaml","tests/cassettes/test_data_sources_retrieve.yaml","tests/cassettes/test_data_sources_update.yaml","tests/cassettes/test_databases_create.yaml","tests/cassettes/test_databases_retrieve.yaml","tests/cassettes/test_databases_update.yaml","tests/cassettes/test_file_uploads_create.yaml","tests/cassettes/test_file_uploads_create_external.yaml","tests/cassettes/test_file_uploads_list.yaml","tests/cassettes/test_file_uploads_list_with_pagination.yaml","tests/cassettes/test_file_uploads_list_with_start_cursor.yaml","tests/cassettes/test_file_uploads_list_with_status_filter.yaml","tests/cassettes/test_file_uploads_retrieve.yaml","tests/cassettes/test_file_uploads_send.yaml","tests/cassettes/test_is_equation_rich_text_item_response.yaml","tests/cassettes/test_is_full_block.yaml","tests/cassettes/test_is_full_comment.yaml","tests/cassettes/test_is_full_data_source.yaml","tests/cassettes/test_is_full_database.yaml","tests/cassettes/test_is_full_page.yaml","tests/cassettes/test_is_full_page_or_data_source.yaml","tests/cassettes/test_is_full_user.yaml","tests/cassettes/test_is_mention_rich_text_item_response.yaml","tests/cassettes/test_is_text_rich_text_item_response.yaml","tests/cassettes/test_iterate_data_source_templates.yaml","tests/cassettes/test_iterate_paginated_api.yaml","tests/cassettes/test_move_pages.yaml","tests/cassettes/test_pages_create.yaml","tests/cassettes/test_pages_delete.yaml","tests/cassettes/test_pages_properties_retrieve.yaml","tests/cassettes/test_pages_retrieve.yaml","tests/cassettes/test_pages_retrieve_markdown.yaml","tests/cassettes/test_pages_update.yaml","tests/cassettes/test_pages_update_markdown.yaml","tests/cassettes/test_search.yaml","tests/cassettes/test_users_list.yaml","tests/cassettes/test_users_me.yaml","tests/cassettes/test_users_retrieve.yaml","tests/cassettes/test_views_create.yaml","tests/cassettes/test_views_delete.yaml","tests/cassettes/test_views_list.yaml","tests/cassettes/test_views_queries_create.yaml","tests/cassettes/test_views_queries_delete.yaml","tests/cassettes/test_views_queries_results.yaml","tests/cassettes/test_views_retrieve.yaml","tests/cassettes/test_views_update.yaml","tests/conftest.py","tests/test_client.py","tests/test_endpoints.py","tests/test_errors.py","tests/test_helpers.py","tests/test_webhooks.py","tox.ini"],"storefront":"/r/ramnes","claimed":false,"request_supported":{"post":"https://gitbuyer.com/r/ramnes/notion-sdk-py/request-supported","requests":0},"note":"indexed from public GitHub; nothing is for sale on this page. Clone it from GitHub. Paid listings live at /search."}