chub-dev 0.1.0 → 0.1.2-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. package/src/mcp/tools.js +251 -0
@@ -0,0 +1,1400 @@
1
+ ---
2
+ name: workspace-api
3
+ description: "Notion Python SDK for interacting with Notion workspaces, pages, and databases via the official API."
4
+ metadata:
5
+ languages: "python"
6
+ versions: "2.6.0"
7
+ updated-on: "2026-03-01"
8
+ source: maintainer
9
+ tags: "notion,api,workspace,pages,databases"
10
+ ---
11
+
12
+ # Notion API Python SDK Coding Guidelines
13
+
14
+ You are a Notion API coding expert. Help me with writing code using the Notion API calling the official libraries and SDKs.
15
+
16
+ You can find the official SDK documentation and code samples here:
17
+ https://developers.notion.com/reference/
18
+ https://ramnes.github.io/notion-sdk-py/
19
+
20
+ ## Golden Rule: Use the Correct and Current SDK
21
+
22
+ Always use the Notion Python SDK to interact with Notion workspaces, which is the standard library for all Notion API interactions. Do not use legacy libraries or unofficial SDKs.
23
+
24
+ - **Library Name:** Notion Python SDK
25
+ - **PyPI Package:** `notion-client`
26
+ - **Legacy Libraries**: `notion-py`, `notion`, `notion-sdk`, and other unofficial packages are not recommended
27
+
28
+ **Installation:**
29
+
30
+ - **Correct:** `pip install notion-client`
31
+
32
+ **APIs and Usage:**
33
+
34
+ - **Correct:** `from notion_client import Client`
35
+ - **Correct:** `notion = Client(auth=os.environ["NOTION_TOKEN"])`
36
+ - **Correct:** `notion.pages.create(...)`
37
+ - **Correct:** `notion.databases.query(...)`
38
+ - **Correct:** `notion.data_sources.query(...)`
39
+ - **Incorrect:** `NotionClient` or `NotionAPI` from other packages
40
+ - **Incorrect:** Legacy notion-py APIs
41
+
42
+ ## Installation
43
+
44
+ Install the Notion Python SDK using pip:
45
+
46
+ ```bash
47
+ pip install notion-client
48
+ ```
49
+
50
+ Set your environment variable for the Notion API token:
51
+
52
+ ```bash
53
+ export NOTION_TOKEN="your_integration_token_here"
54
+ ```
55
+
56
+ You can obtain an integration token by creating a new integration at:
57
+ https://www.notion.com/my-integrations
58
+
59
+ ## Initialization and API Key
60
+
61
+ The `notion-client` library requires creating a `Client` instance for all API calls.
62
+
63
+ - Always use `notion = Client(auth=token)` to create an instance.
64
+ - Set the `NOTION_TOKEN` environment variable for security.
65
+
66
+ ```python
67
+ import os
68
+ from notion_client import Client
69
+
70
+ # Uses the NOTION_TOKEN environment variable
71
+ notion = Client(auth=os.environ["NOTION_TOKEN"])
72
+ ```
73
+
74
+ ### Client Configuration Options
75
+
76
+ ```python
77
+ from notion_client import Client
78
+
79
+ notion = Client(
80
+ auth=os.environ["NOTION_TOKEN"],
81
+ log_level="DEBUG", # Logging verbosity: DEBUG, INFO, WARNING, ERROR
82
+ timeout_ms=60000, # Request timeout in milliseconds (default: 60000)
83
+ )
84
+ ```
85
+
86
+ ### Async Client
87
+
88
+ The SDK supports async/await operations:
89
+
90
+ ```python
91
+ import os
92
+ from notion_client import AsyncClient
93
+
94
+ async_notion = AsyncClient(auth=os.environ["NOTION_TOKEN"])
95
+
96
+ # Use with async/await
97
+ async def get_users():
98
+ response = await async_notion.users.list()
99
+ return response
100
+ ```
101
+
102
+ ## Important API Version Changes (2025-09-03)
103
+
104
+ As of API version 2025-09-03, Notion separated databases and data sources:
105
+
106
+ - **Databases** parent one or more data sources
107
+ - **Data Sources** each parent zero or more pages
108
+ - Use `notion.data_sources.query()` instead of `notion.databases.query()` for newer implementations
109
+
110
+ ## Pages API
111
+
112
+ ### Create a Page
113
+
114
+ Create a new page as a child of an existing page:
115
+
116
+ ```python
117
+ from notion_client import Client
118
+ import os
119
+
120
+ notion = Client(auth=os.environ["NOTION_TOKEN"])
121
+
122
+ def create_page():
123
+ response = notion.pages.create(
124
+ parent={
125
+ "page_id": "494c87d072c44cf6960f55f8427f7692"
126
+ },
127
+ properties={
128
+ "title": {
129
+ "type": "title",
130
+ "title": [
131
+ {
132
+ "type": "text",
133
+ "text": {"content": "A note from your pals at Notion"}
134
+ }
135
+ ]
136
+ }
137
+ }
138
+ )
139
+
140
+ return response
141
+
142
+ page = create_page()
143
+ print(page)
144
+ ```
145
+
146
+ ### Create a Page in a Data Source (Database)
147
+
148
+ ```python
149
+ def create_page_in_data_source():
150
+ response = notion.pages.create(
151
+ parent={
152
+ "data_source_id": "897e5a76-ae52-4b48-9fdf-e71f5945d1af"
153
+ },
154
+ properties={
155
+ "Name": {
156
+ "title": [
157
+ {
158
+ "text": {
159
+ "content": "Golden Gate Bridge"
160
+ }
161
+ }
162
+ ]
163
+ },
164
+ "Tags": {
165
+ "multi_select": [
166
+ {"name": "Landmark"},
167
+ {"name": "San Francisco"}
168
+ ]
169
+ },
170
+ "Status": {
171
+ "status": {
172
+ "name": "Active"
173
+ }
174
+ }
175
+ }
176
+ )
177
+
178
+ return response
179
+ ```
180
+
181
+ ### Create a Page with Content Blocks
182
+
183
+ ```python
184
+ def create_page_with_content():
185
+ response = notion.pages.create(
186
+ parent={
187
+ "page_id": "494c87d072c44cf6960f55f8427f7692"
188
+ },
189
+ properties={
190
+ "title": {
191
+ "title": [{"text": {"content": "My New Page"}}]
192
+ }
193
+ },
194
+ children=[
195
+ {
196
+ "object": "block",
197
+ "type": "heading_2",
198
+ "heading_2": {
199
+ "rich_text": [{"type": "text", "text": {"content": "Introduction"}}]
200
+ }
201
+ },
202
+ {
203
+ "object": "block",
204
+ "type": "paragraph",
205
+ "paragraph": {
206
+ "rich_text": [
207
+ {
208
+ "type": "text",
209
+ "text": {"content": "This is a paragraph with "}
210
+ },
211
+ {
212
+ "type": "text",
213
+ "text": {"content": "bold text"},
214
+ "annotations": {"bold": True}
215
+ }
216
+ ]
217
+ }
218
+ },
219
+ {
220
+ "object": "block",
221
+ "type": "bulleted_list_item",
222
+ "bulleted_list_item": {
223
+ "rich_text": [{"type": "text", "text": {"content": "First bullet point"}}]
224
+ }
225
+ }
226
+ ]
227
+ )
228
+
229
+ return response
230
+ ```
231
+
232
+ ### Retrieve a Page
233
+
234
+ ```python
235
+ def retrieve_page(page_id):
236
+ response = notion.pages.retrieve(page_id=page_id)
237
+
238
+ print("Page title:", response["properties"]["title"])
239
+ print("Created at:", response["created_time"])
240
+ print("Last edited:", response["last_edited_time"])
241
+
242
+ return response
243
+ ```
244
+
245
+ ### Update Page Properties
246
+
247
+ ```python
248
+ def update_page(page_id):
249
+ response = notion.pages.update(
250
+ page_id=page_id,
251
+ properties={
252
+ "Status": {
253
+ "status": {
254
+ "name": "Completed"
255
+ }
256
+ },
257
+ "Tags": {
258
+ "multi_select": [
259
+ {"name": "Updated"},
260
+ {"name": "Important"}
261
+ ]
262
+ }
263
+ }
264
+ )
265
+
266
+ return response
267
+ ```
268
+
269
+ ### Archive (Trash) a Page
270
+
271
+ ```python
272
+ def archive_page(page_id):
273
+ response = notion.pages.update(
274
+ page_id=page_id,
275
+ archived=True
276
+ )
277
+
278
+ return response
279
+ ```
280
+
281
+ ## Databases API
282
+
283
+ ### Query a Database
284
+
285
+ ```python
286
+ def query_database(database_id):
287
+ response = notion.databases.query(database_id=database_id)
288
+
289
+ print("Results:", response["results"])
290
+ return response
291
+ ```
292
+
293
+ ### Query with Filters and Sorting
294
+
295
+ ```python
296
+ def query_database_with_filters(database_id):
297
+ response = notion.databases.query(
298
+ database_id=database_id,
299
+ filter={
300
+ "and": [
301
+ {
302
+ "property": "Status",
303
+ "status": {
304
+ "equals": "Active"
305
+ }
306
+ },
307
+ {
308
+ "property": "Priority",
309
+ "select": {
310
+ "equals": "High"
311
+ }
312
+ }
313
+ ]
314
+ },
315
+ sorts=[
316
+ {
317
+ "property": "Created",
318
+ "direction": "descending"
319
+ }
320
+ ]
321
+ )
322
+
323
+ return response
324
+ ```
325
+
326
+ ### Query Data Source (API Version 2025-09-03+)
327
+
328
+ ```python
329
+ def query_data_source(data_source_id):
330
+ response = notion.data_sources.query(
331
+ data_source_id=data_source_id,
332
+ filter={
333
+ "property": "Landmark",
334
+ "rich_text": {
335
+ "contains": "Bridge"
336
+ }
337
+ }
338
+ )
339
+
340
+ return response
341
+ ```
342
+
343
+ ### Filter Examples
344
+
345
+ ```python
346
+ # Text property contains value
347
+ text_filter = {
348
+ "property": "Description",
349
+ "rich_text": {"contains": "important"}
350
+ }
351
+
352
+ # Checkbox equals value
353
+ checkbox_filter = {
354
+ "property": "Done",
355
+ "checkbox": {"equals": True}
356
+ }
357
+
358
+ # Date is after specific date
359
+ date_filter = {
360
+ "property": "Due Date",
361
+ "date": {"after": "2025-01-01"}
362
+ }
363
+
364
+ # Number greater than value
365
+ number_filter = {
366
+ "property": "Score",
367
+ "number": {"greater_than": 50}
368
+ }
369
+
370
+ # Multi-select contains value
371
+ multi_select_filter = {
372
+ "property": "Tags",
373
+ "multi_select": {"contains": "urgent"}
374
+ }
375
+
376
+ # Compound OR filter
377
+ or_filter = {
378
+ "or": [
379
+ {"property": "Tags", "multi_select": {"contains": "A"}},
380
+ {"property": "Tags", "multi_select": {"contains": "B"}}
381
+ ]
382
+ }
383
+
384
+ # Compound AND filter
385
+ and_filter = {
386
+ "and": [
387
+ {"property": "Done", "checkbox": {"equals": False}},
388
+ {"property": "Priority", "select": {"equals": "High"}}
389
+ ]
390
+ }
391
+ ```
392
+
393
+ ### Retrieve a Database
394
+
395
+ ```python
396
+ def retrieve_database(database_id):
397
+ response = notion.databases.retrieve(database_id=database_id)
398
+
399
+ print("Database title:", response["title"])
400
+ print("Properties:", response["properties"])
401
+
402
+ return response
403
+ ```
404
+
405
+ ### Create a Database
406
+
407
+ ```python
408
+ def create_database(parent_page_id):
409
+ response = notion.databases.create(
410
+ parent={
411
+ "type": "page_id",
412
+ "page_id": parent_page_id
413
+ },
414
+ title=[
415
+ {
416
+ "type": "text",
417
+ "text": {
418
+ "content": "Task List"
419
+ }
420
+ }
421
+ ],
422
+ properties={
423
+ "Name": {
424
+ "title": {}
425
+ },
426
+ "Status": {
427
+ "status": {
428
+ "options": [
429
+ {"name": "Not started", "color": "red"},
430
+ {"name": "In progress", "color": "yellow"},
431
+ {"name": "Completed", "color": "green"}
432
+ ]
433
+ }
434
+ },
435
+ "Priority": {
436
+ "select": {
437
+ "options": [
438
+ {"name": "High", "color": "red"},
439
+ {"name": "Medium", "color": "yellow"},
440
+ {"name": "Low", "color": "gray"}
441
+ ]
442
+ }
443
+ },
444
+ "Tags": {
445
+ "multi_select": {
446
+ "options": [
447
+ {"name": "Bug", "color": "red"},
448
+ {"name": "Feature", "color": "blue"}
449
+ ]
450
+ }
451
+ },
452
+ "Due Date": {
453
+ "date": {}
454
+ }
455
+ }
456
+ )
457
+
458
+ return response
459
+ ```
460
+
461
+ ### Update Database Properties
462
+
463
+ ```python
464
+ def update_database(database_id):
465
+ response = notion.databases.update(
466
+ database_id=database_id,
467
+ title=[
468
+ {
469
+ "text": {
470
+ "content": "Updated Task List"
471
+ }
472
+ }
473
+ ],
474
+ properties={
475
+ "Status": {
476
+ "status": {
477
+ "options": [
478
+ {"name": "To Do", "color": "red"},
479
+ {"name": "Doing", "color": "yellow"},
480
+ {"name": "Done", "color": "green"}
481
+ ]
482
+ }
483
+ }
484
+ }
485
+ )
486
+
487
+ return response
488
+ ```
489
+
490
+ ## Blocks API
491
+
492
+ ### Retrieve Block Children
493
+
494
+ ```python
495
+ def get_block_children(block_id):
496
+ response = notion.blocks.children.list(
497
+ block_id=block_id,
498
+ page_size=50
499
+ )
500
+
501
+ print("Number of blocks:", len(response["results"]))
502
+ return response["results"]
503
+ ```
504
+
505
+ ### Append Block Children
506
+
507
+ ```python
508
+ def append_block_children(block_id):
509
+ response = notion.blocks.children.append(
510
+ block_id=block_id,
511
+ children=[
512
+ {
513
+ "object": "block",
514
+ "type": "heading_2",
515
+ "heading_2": {
516
+ "rich_text": [
517
+ {
518
+ "type": "text",
519
+ "text": {"content": "New Section"}
520
+ }
521
+ ]
522
+ }
523
+ },
524
+ {
525
+ "object": "block",
526
+ "type": "paragraph",
527
+ "paragraph": {
528
+ "rich_text": [
529
+ {
530
+ "type": "text",
531
+ "text": {"content": "This is a new paragraph."}
532
+ }
533
+ ]
534
+ }
535
+ },
536
+ {
537
+ "object": "block",
538
+ "type": "to_do",
539
+ "to_do": {
540
+ "rich_text": [{"type": "text", "text": {"content": "Complete this task"}}],
541
+ "checked": False
542
+ }
543
+ }
544
+ ]
545
+ )
546
+
547
+ return response
548
+ ```
549
+
550
+ ### Common Block Types
551
+
552
+ ```python
553
+ # Heading blocks
554
+ heading_1 = {
555
+ "type": "heading_1",
556
+ "heading_1": {
557
+ "rich_text": [{"text": {"content": "Heading 1"}}]
558
+ }
559
+ }
560
+
561
+ heading_2 = {
562
+ "type": "heading_2",
563
+ "heading_2": {
564
+ "rich_text": [{"text": {"content": "Heading 2"}}]
565
+ }
566
+ }
567
+
568
+ heading_3 = {
569
+ "type": "heading_3",
570
+ "heading_3": {
571
+ "rich_text": [{"text": {"content": "Heading 3"}}]
572
+ }
573
+ }
574
+
575
+ # Paragraph with formatting
576
+ paragraph = {
577
+ "type": "paragraph",
578
+ "paragraph": {
579
+ "rich_text": [
580
+ {"text": {"content": "Normal text "}},
581
+ {"text": {"content": "bold"}, "annotations": {"bold": True}},
582
+ {"text": {"content": " and "}},
583
+ {"text": {"content": "italic"}, "annotations": {"italic": True}},
584
+ {"text": {"content": " and "}},
585
+ {"text": {"content": "code"}, "annotations": {"code": True}}
586
+ ]
587
+ }
588
+ }
589
+
590
+ # Bulleted list item
591
+ bulleted_list_item = {
592
+ "type": "bulleted_list_item",
593
+ "bulleted_list_item": {
594
+ "rich_text": [{"text": {"content": "Bullet point"}}]
595
+ }
596
+ }
597
+
598
+ # Numbered list item
599
+ numbered_list_item = {
600
+ "type": "numbered_list_item",
601
+ "numbered_list_item": {
602
+ "rich_text": [{"text": {"content": "Numbered item"}}]
603
+ }
604
+ }
605
+
606
+ # To-do item
607
+ to_do = {
608
+ "type": "to_do",
609
+ "to_do": {
610
+ "rich_text": [{"text": {"content": "Task to complete"}}],
611
+ "checked": False
612
+ }
613
+ }
614
+
615
+ # Toggle block (collapsible)
616
+ toggle = {
617
+ "type": "toggle",
618
+ "toggle": {
619
+ "rich_text": [{"text": {"content": "Click to expand"}}]
620
+ }
621
+ }
622
+
623
+ # Quote
624
+ quote = {
625
+ "type": "quote",
626
+ "quote": {
627
+ "rich_text": [{"text": {"content": "This is a quote"}}]
628
+ }
629
+ }
630
+
631
+ # Callout
632
+ callout = {
633
+ "type": "callout",
634
+ "callout": {
635
+ "rich_text": [{"text": {"content": "Important note"}}],
636
+ "icon": {"emoji": "💡"}
637
+ }
638
+ }
639
+
640
+ # Code block
641
+ code_block = {
642
+ "type": "code",
643
+ "code": {
644
+ "rich_text": [{"text": {"content": "x = 42"}}],
645
+ "language": "python"
646
+ }
647
+ }
648
+
649
+ # Divider
650
+ divider = {
651
+ "type": "divider",
652
+ "divider": {}
653
+ }
654
+
655
+ # Image
656
+ image = {
657
+ "type": "image",
658
+ "image": {
659
+ "type": "external",
660
+ "external": {
661
+ "url": "https://example.com/image.png"
662
+ }
663
+ }
664
+ }
665
+
666
+ # Bookmark
667
+ bookmark = {
668
+ "type": "bookmark",
669
+ "bookmark": {
670
+ "url": "https://notion.so"
671
+ }
672
+ }
673
+ ```
674
+
675
+ ### Update a Block
676
+
677
+ ```python
678
+ def update_block(block_id):
679
+ response = notion.blocks.update(
680
+ block_id=block_id,
681
+ paragraph={
682
+ "rich_text": [
683
+ {
684
+ "text": {
685
+ "content": "Updated paragraph content"
686
+ }
687
+ }
688
+ ]
689
+ }
690
+ )
691
+
692
+ return response
693
+ ```
694
+
695
+ ### Delete a Block
696
+
697
+ ```python
698
+ def delete_block(block_id):
699
+ response = notion.blocks.delete(block_id=block_id)
700
+ return response
701
+ ```
702
+
703
+ ### Retrieve a Block
704
+
705
+ ```python
706
+ def retrieve_block(block_id):
707
+ response = notion.blocks.retrieve(block_id=block_id)
708
+
709
+ print("Block type:", response["type"])
710
+ return response
711
+ ```
712
+
713
+ ## Users API
714
+
715
+ ### List All Users
716
+
717
+ ```python
718
+ def list_users():
719
+ response = notion.users.list()
720
+
721
+ print("Users:", response["results"])
722
+ return response["results"]
723
+ ```
724
+
725
+ ### Retrieve a User
726
+
727
+ ```python
728
+ def retrieve_user(user_id):
729
+ response = notion.users.retrieve(user_id=user_id)
730
+
731
+ print("User name:", response["name"])
732
+ print("User type:", response["type"])
733
+
734
+ return response
735
+ ```
736
+
737
+ ### Retrieve Bot User (Your Integration)
738
+
739
+ ```python
740
+ def retrieve_bot_user():
741
+ response = notion.users.me()
742
+
743
+ print("Bot name:", response["name"])
744
+ print("Bot ID:", response["id"])
745
+
746
+ return response
747
+ ```
748
+
749
+ ## Search API
750
+
751
+ ### Basic Search
752
+
753
+ ```python
754
+ def search_pages(query):
755
+ response = notion.search(
756
+ query=query,
757
+ filter={
758
+ "value": "page",
759
+ "property": "object"
760
+ },
761
+ sort={
762
+ "direction": "descending",
763
+ "timestamp": "last_edited_time"
764
+ }
765
+ )
766
+
767
+ return response["results"]
768
+ ```
769
+
770
+ ### Search for Databases
771
+
772
+ ```python
773
+ def search_databases(query):
774
+ response = notion.search(
775
+ query=query,
776
+ filter={
777
+ "value": "database",
778
+ "property": "object"
779
+ }
780
+ )
781
+
782
+ return response["results"]
783
+ ```
784
+
785
+ ### Search All Content
786
+
787
+ ```python
788
+ def search_all(query):
789
+ response = notion.search(
790
+ query=query,
791
+ page_size=100
792
+ )
793
+
794
+ print(f"Found {len(response['results'])} results")
795
+ return response["results"]
796
+ ```
797
+
798
+ ## Comments API
799
+
800
+ ### Create a Comment
801
+
802
+ ```python
803
+ def create_comment(page_id):
804
+ response = notion.comments.create(
805
+ parent={
806
+ "page_id": page_id
807
+ },
808
+ rich_text=[
809
+ {
810
+ "text": {
811
+ "content": "This is a comment on the page."
812
+ }
813
+ }
814
+ ]
815
+ )
816
+
817
+ return response
818
+ ```
819
+
820
+ ### Create a Discussion Comment (Reply)
821
+
822
+ ```python
823
+ def create_discussion_comment(discussion_id):
824
+ response = notion.comments.create(
825
+ discussion_id=discussion_id,
826
+ rich_text=[
827
+ {
828
+ "text": {
829
+ "content": "This is a reply to an existing comment."
830
+ }
831
+ }
832
+ ]
833
+ )
834
+
835
+ return response
836
+ ```
837
+
838
+ ### List Comments
839
+
840
+ ```python
841
+ def list_comments(block_id):
842
+ response = notion.comments.list(block_id=block_id)
843
+
844
+ print("Comments:", response["results"])
845
+ return response["results"]
846
+ ```
847
+
848
+ ## Pagination
849
+
850
+ ### Manual Pagination
851
+
852
+ ```python
853
+ def get_all_pages(database_id):
854
+ has_more = True
855
+ start_cursor = None
856
+ all_results = []
857
+
858
+ while has_more:
859
+ response = notion.databases.query(
860
+ database_id=database_id,
861
+ start_cursor=start_cursor,
862
+ page_size=100
863
+ )
864
+
865
+ all_results.extend(response["results"])
866
+ has_more = response["has_more"]
867
+ start_cursor = response.get("next_cursor")
868
+
869
+ print(f"Retrieved {len(all_results)} total results")
870
+ return all_results
871
+ ```
872
+
873
+ ### Using Pagination Helpers
874
+
875
+ ```python
876
+ from notion_client import iterate_paginated_api, collect_paginated_api
877
+
878
+ # Iterate through paginated results
879
+ def iterate_blocks(block_id):
880
+ for block in iterate_paginated_api(
881
+ notion.blocks.children.list,
882
+ block_id=block_id
883
+ ):
884
+ print("Block type:", block["type"])
885
+
886
+ # Collect all paginated results at once
887
+ def collect_all_blocks(block_id):
888
+ blocks = collect_paginated_api(
889
+ notion.blocks.children.list,
890
+ block_id=block_id
891
+ )
892
+
893
+ print(f"Total blocks: {len(blocks)}")
894
+ return blocks
895
+ ```
896
+
897
+ ### Async Pagination Helpers
898
+
899
+ ```python
900
+ from notion_client import AsyncClient, async_iterate_paginated_api, async_collect_paginated_api
901
+
902
+ async_notion = AsyncClient(auth=os.environ["NOTION_TOKEN"])
903
+
904
+ # Async iteration
905
+ async def async_iterate_blocks(block_id):
906
+ async for block in async_iterate_paginated_api(
907
+ async_notion.blocks.children.list,
908
+ block_id=block_id
909
+ ):
910
+ print("Block type:", block["type"])
911
+
912
+ # Async collection
913
+ async def async_collect_blocks(block_id):
914
+ blocks = await async_collect_paginated_api(
915
+ async_notion.blocks.children.list,
916
+ block_id=block_id
917
+ )
918
+ return blocks
919
+ ```
920
+
921
+ ## Error Handling
922
+
923
+ ### Basic Error Handling
924
+
925
+ ```python
926
+ from notion_client import Client, APIResponseError
927
+
928
+ notion = Client(auth=os.environ["NOTION_TOKEN"])
929
+
930
+ def handle_errors():
931
+ try:
932
+ response = notion.pages.retrieve(page_id="invalid-id")
933
+ except APIResponseError as error:
934
+ if error.code == "object_not_found":
935
+ print("Page not found")
936
+ elif error.code == "unauthorized":
937
+ print("Invalid API token")
938
+ elif error.code == "restricted_resource":
939
+ print("Access denied - integration not shared with this resource")
940
+ elif error.code == "rate_limited":
941
+ print("Rate limit exceeded")
942
+ else:
943
+ print(f"Error: {error.message}")
944
+ ```
945
+
946
+ ### Advanced Error Handling
947
+
948
+ ```python
949
+ from notion_client import Client, APIResponseError, RequestTimeoutError
950
+
951
+ def advanced_error_handling():
952
+ try:
953
+ response = notion.pages.retrieve(page_id="some-id")
954
+ except RequestTimeoutError:
955
+ print("Request timed out")
956
+ except APIResponseError as error:
957
+ print(f"API Error: {error.code}")
958
+ print(f"Status: {error.status}")
959
+ print(f"Message: {error.message}")
960
+
961
+ # Access full response
962
+ if hasattr(error, 'response'):
963
+ print(f"Full response: {error.response}")
964
+ except Exception as error:
965
+ print(f"Unexpected error: {error}")
966
+ ```
967
+
968
+ ### Common Error Codes
969
+
970
+ | Error Code | Description |
971
+ |------------|-------------|
972
+ | `object_not_found` | The requested resource doesn't exist |
973
+ | `unauthorized` | Invalid or missing API token |
974
+ | `restricted_resource` | Integration doesn't have access to the resource |
975
+ | `rate_limited` | Too many requests, retry after delay |
976
+ | `validation_error` | Invalid request parameters |
977
+ | `conflict_error` | Resource conflict (e.g., duplicate) |
978
+
979
+ ## Advanced Features
980
+
981
+ ### Rich Text Formatting
982
+
983
+ ```python
984
+ rich_text = [
985
+ {
986
+ "type": "text",
987
+ "text": {"content": "Plain text "}
988
+ },
989
+ {
990
+ "type": "text",
991
+ "text": {"content": "bold text"},
992
+ "annotations": {"bold": True}
993
+ },
994
+ {
995
+ "type": "text",
996
+ "text": {"content": " "}
997
+ },
998
+ {
999
+ "type": "text",
1000
+ "text": {"content": "italic text"},
1001
+ "annotations": {"italic": True}
1002
+ },
1003
+ {
1004
+ "type": "text",
1005
+ "text": {"content": " "}
1006
+ },
1007
+ {
1008
+ "type": "text",
1009
+ "text": {"content": "strikethrough"},
1010
+ "annotations": {"strikethrough": True}
1011
+ },
1012
+ {
1013
+ "type": "text",
1014
+ "text": {"content": " "}
1015
+ },
1016
+ {
1017
+ "type": "text",
1018
+ "text": {"content": "underline"},
1019
+ "annotations": {"underline": True}
1020
+ },
1021
+ {
1022
+ "type": "text",
1023
+ "text": {"content": " "}
1024
+ },
1025
+ {
1026
+ "type": "text",
1027
+ "text": {"content": "code"},
1028
+ "annotations": {"code": True}
1029
+ },
1030
+ {
1031
+ "type": "text",
1032
+ "text": {"content": " "}
1033
+ },
1034
+ {
1035
+ "type": "text",
1036
+ "text": {"content": "colored text"},
1037
+ "annotations": {"color": "red"}
1038
+ },
1039
+ {
1040
+ "type": "text",
1041
+ "text": {"content": " "}
1042
+ },
1043
+ {
1044
+ "type": "text",
1045
+ "text": {"content": "link text", "link": {"url": "https://notion.so"}}
1046
+ }
1047
+ ]
1048
+ ```
1049
+
1050
+ ### Property Types
1051
+
1052
+ ```python
1053
+ # Title property
1054
+ title_property = {
1055
+ "title": [
1056
+ {
1057
+ "text": {"content": "Page Title"}
1058
+ }
1059
+ ]
1060
+ }
1061
+
1062
+ # Rich text property
1063
+ rich_text_property = {
1064
+ "rich_text": [
1065
+ {
1066
+ "text": {"content": "Some text content"}
1067
+ }
1068
+ ]
1069
+ }
1070
+
1071
+ # Number property
1072
+ number_property = {
1073
+ "number": 42
1074
+ }
1075
+
1076
+ # Select property
1077
+ select_property = {
1078
+ "select": {
1079
+ "name": "Option 1"
1080
+ }
1081
+ }
1082
+
1083
+ # Multi-select property
1084
+ multi_select_property = {
1085
+ "multi_select": [
1086
+ {"name": "Tag 1"},
1087
+ {"name": "Tag 2"}
1088
+ ]
1089
+ }
1090
+
1091
+ # Date property
1092
+ date_property = {
1093
+ "date": {
1094
+ "start": "2025-01-01",
1095
+ "end": "2025-01-31"
1096
+ }
1097
+ }
1098
+
1099
+ # Checkbox property
1100
+ checkbox_property = {
1101
+ "checkbox": True
1102
+ }
1103
+
1104
+ # URL property
1105
+ url_property = {
1106
+ "url": "https://notion.so"
1107
+ }
1108
+
1109
+ # Email property
1110
+ email_property = {
1111
+ "email": "user@example.com"
1112
+ }
1113
+
1114
+ # Phone number property
1115
+ phone_property = {
1116
+ "phone_number": "+1-555-0100"
1117
+ }
1118
+
1119
+ # Status property
1120
+ status_property = {
1121
+ "status": {
1122
+ "name": "In Progress"
1123
+ }
1124
+ }
1125
+
1126
+ # People property
1127
+ people_property = {
1128
+ "people": [
1129
+ {
1130
+ "object": "user",
1131
+ "id": "user-id-here"
1132
+ }
1133
+ ]
1134
+ }
1135
+
1136
+ # Files property
1137
+ files_property = {
1138
+ "files": [
1139
+ {
1140
+ "name": "Document.pdf",
1141
+ "type": "external",
1142
+ "external": {
1143
+ "url": "https://example.com/document.pdf"
1144
+ }
1145
+ }
1146
+ ]
1147
+ }
1148
+
1149
+ # Relation property
1150
+ relation_property = {
1151
+ "relation": [
1152
+ {
1153
+ "id": "related-page-id"
1154
+ }
1155
+ ]
1156
+ }
1157
+ ```
1158
+
1159
+ ## Complete Example: Task Management System
1160
+
1161
+ ```python
1162
+ import os
1163
+ from notion_client import Client
1164
+
1165
+ notion = Client(auth=os.environ["NOTION_TOKEN"])
1166
+
1167
+ def create_task_management_system():
1168
+ # 1. Create a database for tasks
1169
+ database = notion.databases.create(
1170
+ parent={
1171
+ "type": "page_id",
1172
+ "page_id": os.environ["PARENT_PAGE_ID"]
1173
+ },
1174
+ title=[
1175
+ {
1176
+ "type": "text",
1177
+ "text": {"content": "Task Manager"}
1178
+ }
1179
+ ],
1180
+ properties={
1181
+ "Name": {
1182
+ "title": {}
1183
+ },
1184
+ "Status": {
1185
+ "status": {
1186
+ "options": [
1187
+ {"name": "Not started", "color": "red"},
1188
+ {"name": "In progress", "color": "yellow"},
1189
+ {"name": "Completed", "color": "green"}
1190
+ ]
1191
+ }
1192
+ },
1193
+ "Priority": {
1194
+ "select": {
1195
+ "options": [
1196
+ {"name": "High", "color": "red"},
1197
+ {"name": "Medium", "color": "yellow"},
1198
+ {"name": "Low", "color": "gray"}
1199
+ ]
1200
+ }
1201
+ },
1202
+ "Due Date": {
1203
+ "date": {}
1204
+ },
1205
+ "Tags": {
1206
+ "multi_select": {
1207
+ "options": [
1208
+ {"name": "Bug", "color": "red"},
1209
+ {"name": "Feature", "color": "blue"},
1210
+ {"name": "Documentation", "color": "purple"}
1211
+ ]
1212
+ }
1213
+ },
1214
+ "Assignee": {
1215
+ "people": {}
1216
+ }
1217
+ }
1218
+ )
1219
+
1220
+ print(f"Database created: {database['id']}")
1221
+
1222
+ # 2. Create a task
1223
+ task = notion.pages.create(
1224
+ parent={
1225
+ "database_id": database["id"]
1226
+ },
1227
+ properties={
1228
+ "Name": {
1229
+ "title": [
1230
+ {
1231
+ "text": {"content": "Implement user authentication"}
1232
+ }
1233
+ ]
1234
+ },
1235
+ "Status": {
1236
+ "status": {"name": "In progress"}
1237
+ },
1238
+ "Priority": {
1239
+ "select": {"name": "High"}
1240
+ },
1241
+ "Due Date": {
1242
+ "date": {"start": "2025-02-01"}
1243
+ },
1244
+ "Tags": {
1245
+ "multi_select": [{"name": "Feature"}]
1246
+ }
1247
+ }
1248
+ )
1249
+
1250
+ print(f"Task created: {task['id']}")
1251
+
1252
+ # 3. Add content to the task
1253
+ notion.blocks.children.append(
1254
+ block_id=task["id"],
1255
+ children=[
1256
+ {
1257
+ "object": "block",
1258
+ "type": "heading_2",
1259
+ "heading_2": {
1260
+ "rich_text": [{"text": {"content": "Task Details"}}]
1261
+ }
1262
+ },
1263
+ {
1264
+ "object": "block",
1265
+ "type": "paragraph",
1266
+ "paragraph": {
1267
+ "rich_text": [
1268
+ {
1269
+ "text": {
1270
+ "content": "Implement OAuth 2.0 authentication for user login."
1271
+ }
1272
+ }
1273
+ ]
1274
+ }
1275
+ },
1276
+ {
1277
+ "object": "block",
1278
+ "type": "heading_3",
1279
+ "heading_3": {
1280
+ "rich_text": [{"text": {"content": "Requirements"}}]
1281
+ }
1282
+ },
1283
+ {
1284
+ "object": "block",
1285
+ "type": "to_do",
1286
+ "to_do": {
1287
+ "rich_text": [{"text": {"content": "Set up OAuth provider"}}],
1288
+ "checked": False
1289
+ }
1290
+ },
1291
+ {
1292
+ "object": "block",
1293
+ "type": "to_do",
1294
+ "to_do": {
1295
+ "rich_text": [{"text": {"content": "Create login page"}}],
1296
+ "checked": False
1297
+ }
1298
+ },
1299
+ {
1300
+ "object": "block",
1301
+ "type": "to_do",
1302
+ "to_do": {
1303
+ "rich_text": [{"text": {"content": "Test authentication flow"}}],
1304
+ "checked": False
1305
+ }
1306
+ }
1307
+ ]
1308
+ )
1309
+
1310
+ # 4. Add a comment
1311
+ notion.comments.create(
1312
+ parent={"page_id": task["id"]},
1313
+ rich_text=[
1314
+ {
1315
+ "text": {"content": "Starting work on this task today."}
1316
+ }
1317
+ ]
1318
+ )
1319
+
1320
+ # 5. Query all high priority tasks
1321
+ high_priority_tasks = notion.databases.query(
1322
+ database_id=database["id"],
1323
+ filter={
1324
+ "property": "Priority",
1325
+ "select": {
1326
+ "equals": "High"
1327
+ }
1328
+ },
1329
+ sorts=[
1330
+ {
1331
+ "property": "Due Date",
1332
+ "direction": "ascending"
1333
+ }
1334
+ ]
1335
+ )
1336
+
1337
+ print(f"High priority tasks: {len(high_priority_tasks['results'])}")
1338
+
1339
+ # 6. Update task status
1340
+ notion.pages.update(
1341
+ page_id=task["id"],
1342
+ properties={
1343
+ "Status": {
1344
+ "status": {"name": "Completed"}
1345
+ }
1346
+ }
1347
+ )
1348
+
1349
+ print("Task marked as completed")
1350
+
1351
+ # Run the example
1352
+ if __name__ == "__main__":
1353
+ create_task_management_system()
1354
+ ```
1355
+
1356
+ ## Async Example
1357
+
1358
+ ```python
1359
+ import os
1360
+ import asyncio
1361
+ from notion_client import AsyncClient
1362
+
1363
+ async_notion = AsyncClient(auth=os.environ["NOTION_TOKEN"])
1364
+
1365
+ async def async_example():
1366
+ # List users
1367
+ users = await async_notion.users.list()
1368
+ print(f"Found {len(users['results'])} users")
1369
+
1370
+ # Create a page
1371
+ page = await async_notion.pages.create(
1372
+ parent={"page_id": "parent-page-id"},
1373
+ properties={
1374
+ "title": {
1375
+ "title": [{"text": {"content": "Async Page"}}]
1376
+ }
1377
+ }
1378
+ )
1379
+
1380
+ # Query a database
1381
+ results = await async_notion.databases.query(
1382
+ database_id="database-id"
1383
+ )
1384
+
1385
+ return results
1386
+
1387
+ # Run async code
1388
+ if __name__ == "__main__":
1389
+ asyncio.run(async_example())
1390
+ ```
1391
+
1392
+ ## Useful Links
1393
+
1394
+ - Documentation: https://developers.notion.com/
1395
+ - API Reference: https://developers.notion.com/reference/
1396
+ - SDK Repository: https://github.com/ramnes/notion-sdk-py
1397
+ - SDK Documentation: https://ramnes.github.io/notion-sdk-py/
1398
+ - PyPI Package: https://pypi.org/project/notion-client/
1399
+ - Create Integration: https://www.notion.com/my-integrations
1400
+ - Community: Notion Developers Slack