Notion API client SDK, rewritten in Python! (sync + async)
This report summarizes scanner evidence for ramnes/notion-sdk-py, a Python project with 2,193 GitHub stars. SynthScan examined 21,095 lines of code across 157 source files, recording 306 pattern matches distributed across 11 syntactic categories. The legacy adjusted pattern signal is 18.9 (Elevated pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 220 finding(s).
Are imports declared and package/API names verified? 40 finding(s).
Are wrappers, guards, and repeated structures necessary? 248 finding(s).
Which changes need focused human review? 306 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 306 distinct pattern matches across 11 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/conftest.py | 39 | def remove_page_id_for_matches(r1, r2): | CODE |
| LOW | tests/conftest.py | 213 | def pending_single_file_upload_id(client): | CODE |
| LOW | tests/conftest.py | 222 | def pending_multi_file_upload_id(client): | CODE |
| LOW | tests/conftest.py | 234 | def part_uploaded_file_upload_id(client, pending_multi_file_upload_id): | CODE |
| LOW⚡ | tests/test_errors.py | 42 | def test_api_http_response_error(client): | CODE |
| LOW⚡ | tests/test_errors.py | 48 | async def test_async_api_response_error(async_client): | CODE |
| LOW⚡ | tests/test_errors.py | 55 | async def test_async_api_request_timeout_error(monkeypatch, async_client): | CODE |
| LOW⚡ | tests/test_errors.py | 66 | async def test_api_async_request_bad_request_error(async_client): | CODE |
| LOW⚡ | tests/test_errors.py | 72 | def test_api_response_error_request_id(client): | CODE |
| LOW⚡ | tests/test_errors.py | 81 | async def test_async_api_response_error_request_id(async_client): | CODE |
| LOW⚡ | tests/test_errors.py | 90 | def test_api_response_error_additional_data(client): | CODE |
| LOW⚡ | tests/test_errors.py | 100 | async def test_async_api_response_error_additional_data(async_client): | CODE |
| LOW⚡ | tests/test_errors.py | 109 | def test_is_notion_client_error(): | CODE |
| LOW⚡ | tests/test_errors.py | 473 | def test_is_notion_client_error_with_invalid_path_parameter_error(): | CODE |
| LOW⚡ | tests/test_errors.py | 479 | def test_validate_request_path_allows_valid_paths(): | CODE |
| LOW⚡ | tests/test_errors.py | 488 | def test_validate_request_path_rejects_literal_path_traversal(): | CODE |
| LOW | tests/test_errors.py | 31 | def test_api_request_timeout_error(monkeypatch, client): | CODE |
| LOW | tests/test_errors.py | 132 | def test_is_http_response_error(): | CODE |
| LOW | tests/test_errors.py | 155 | def test_request_timeout_error_static_method(): | CODE |
| LOW | tests/test_errors.py | 176 | def test_unknown_http_response_error_static_method(): | CODE |
| LOW | tests/test_errors.py | 196 | def test_api_response_error_static_method(): | CODE |
| LOW | tests/test_errors.py | 220 | def test_build_request_error_integration(client): | CODE |
| LOW | tests/test_errors.py | 235 | def test_build_request_error_creates_api_response_error(): | CODE |
| LOW | tests/test_errors.py | 290 | def test_build_request_error_recognizes_invalid_beta(): | CODE |
| LOW | tests/test_errors.py | 315 | def test_build_request_error_creates_unknown_http_response_error(): | CODE |
| LOW | tests/test_errors.py | 370 | def test_unknown_http_response_error_default_message(): | CODE |
| LOW | tests/test_errors.py | 382 | def test_unknown_http_response_error_custom_message(): | CODE |
| LOW | tests/test_errors.py | 410 | def test_request_timeout_error_custom_message(): | CODE |
| LOW | tests/test_errors.py | 420 | async def test_request_timeout_error_reject_after_timeout(): | CODE |
| LOW | tests/test_errors.py | 438 | def test_invalid_path_parameter_error(): | CODE |
| LOW | tests/test_errors.py | 449 | def test_invalid_path_parameter_error_static_method(): | CODE |
| LOW | tests/test_errors.py | 505 | def test_validate_request_path_rejects_encoded_path_traversal(): | CODE |
| LOW | tests/test_errors.py | 525 | def test_validate_request_path_handles_invalid_percent_encoding(): | CODE |
| LOW | tests/test_errors.py | 533 | def test_unknown_error_surfaces_edge_metadata_for_blocked_request(): | CODE |
| LOW | tests/test_errors.py | 562 | def test_unknown_error_reads_edge_metadata_from_mixed_case_headers(): | CODE |
| LOW | tests/test_errors.py | 577 | def test_unknown_error_does_not_blame_security_rule_for_server_error(): | CODE |
| LOW | tests/test_errors.py | 593 | def test_unknown_error_omits_content_type_note_when_absent(): | CODE |
| LOW | tests/test_errors.py | 610 | def test_unknown_error_does_not_attribute_origin_response_to_edge(): | CODE |
| LOW | tests/test_errors.py | 631 | def test_unknown_error_keeps_generic_message_without_proxy_metadata(): | CODE |
| LOW | tests/test_errors.py | 646 | def test_api_response_error_exposes_response_metadata(): | CODE |
| LOW | tests/test_errors.py | 671 | def test_request_id_from_body_wins_over_header(): | CODE |
| LOW⚡ | tests/test_endpoints.py | 323 | def test_views_queries_create(client, view_id): | CODE |
| LOW⚡ | tests/test_endpoints.py | 330 | def test_views_queries_results(client, view_id): | CODE |
| LOW⚡ | tests/test_endpoints.py | 338 | def test_views_queries_delete(client, view_id): | CODE |
| LOW⚡ | tests/test_endpoints.py | 399 | def test_pages_retrieve_markdown(client, page_id): | CODE |
| LOW⚡ | tests/test_endpoints.py | 405 | def test_pages_update_markdown(client, page_id): | CODE |
| LOW⚡ | tests/test_endpoints.py | 415 | def test_async_tasks_retrieve(client, page_id): | CODE |
| LOW | tests/test_endpoints.py | 41 | def test_pages_properties_retrieve(client, page_id): | CODE |
| LOW | tests/test_endpoints.py | 47 | def test_blocks_children_create(client, page_id) -> str: | CODE |
| LOW | tests/test_endpoints.py | 60 | def test_blocks_children_list(client, page_id): | CODE |
| LOW | tests/test_endpoints.py | 68 | def test_blocks_meeting_notes_create_from_file_upload(client, mocker): | CODE |
| LOW | tests/test_endpoints.py | 101 | def test_blocks_meeting_notes_create_from_block(client, mocker): | CODE |
| LOW | tests/test_endpoints.py | 118 | def test_blocks_meeting_notes_query(client, mocker): | CODE |
| LOW | tests/test_endpoints.py | 244 | def test_data_sources_retrieve(client, data_source_id): | CODE |
| LOW | tests/test_endpoints.py | 264 | def test_data_sources_list_templates(client, data_source_id): | CODE |
| LOW | tests/test_endpoints.py | 453 | def test_file_uploads_create_multipart(client, mocker): | CODE |
| LOW | tests/test_endpoints.py | 480 | def test_file_uploads_create_external(client): | CODE |
| LOW | tests/test_endpoints.py | 494 | def test_file_uploads_retrieve(client, single_file_upload_id): | CODE |
| LOW | tests/test_endpoints.py | 516 | def test_file_uploads_list_with_status_filter(client, setup_file_uploads_ids): | CODE |
| LOW | tests/test_endpoints.py | 528 | def test_file_uploads_list_with_start_cursor(client, setup_file_uploads_ids): | CODE |
| 142 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/conftest.py | 102 | except Exception: | CODE |
| HIGH | tests/conftest.py | 320 | except Exception: | CODE |
| HIGH | tests/conftest.py | 324 | except Exception: | CODE |
| HIGH | notion_client/client.py | 307 | except ValueError: | CODE |
| HIGH | notion_client/client.py | 315 | except (ValueError, TypeError): | CODE |
| HIGH | examples/notion_github_sync/notion_github_sync.py | 10 | except ModuleNotFoundError: | CODE |
| HIGH | examples/database_email_update/database_email_update.py | 9 | except ModuleNotFoundError: | CODE |
| HIGH | …_from_any_block_type/parse_text_from_any_block_type.py | 8 | except ModuleNotFoundError: | CODE |
| HIGH | …tion_task_github_pr_sync/notion_task_github_pr_sync.py | 10 | except ModuleNotFoundError: | CODE |
| HIGH | examples/web_form_with_fastapi/web_form_with_fastapi.py | 12 | except ModuleNotFoundError: | CODE |
| HIGH | examples/intro_to_notion_api/basic/1_add_block.py | 10 | except ModuleNotFoundError: | CODE |
| HIGH | …amples/intro_to_notion_api/basic/3_add_styled_block.py | 10 | except ModuleNotFoundError: | CODE |
| HIGH | …amples/intro_to_notion_api/basic/2_add_linked_block.py | 10 | except ModuleNotFoundError: | CODE |
| HIGH | …s/intro_to_notion_api/intermediate/3_query_database.py | 12 | except ModuleNotFoundError: | CODE |
| HIGH | …ples/intro_to_notion_api/intermediate/5_upload_file.py | 10 | except ModuleNotFoundError: | CODE |
| HIGH | …o_to_notion_api/intermediate/2_add_page_to_database.py | 12 | except ModuleNotFoundError: | CODE |
| HIGH | …ntro_to_notion_api/intermediate/1_create_a_database.py | 10 | except ModuleNotFoundError: | CODE |
| HIGH | …es/intro_to_notion_api/intermediate/4_sort_database.py | 12 | except ModuleNotFoundError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | notion_client/__init__.py | 9 | CODE | |
| LOW | notion_client/__init__.py | 9 | CODE | |
| LOW | notion_client/__init__.py | 9 | CODE | |
| LOW | notion_client/__init__.py | 10 | CODE | |
| LOW | notion_client/__init__.py | 10 | CODE | |
| LOW | notion_client/__init__.py | 10 | CODE | |
| LOW | notion_client/__init__.py | 10 | CODE | |
| LOW | notion_client/__init__.py | 10 | CODE | |
| LOW | notion_client/__init__.py | 10 | CODE | |
| LOW | notion_client/__init__.py | 18 | CODE | |
| LOW | notion_client/__init__.py | 18 | CODE | |
| LOW | notion_client/__init__.py | 18 | CODE | |
| LOW | notion_client/__init__.py | 18 | CODE | |
| LOW | notion_client/__init__.py | 18 | CODE | |
| LOW | notion_client/__init__.py | 18 | CODE | |
| LOW | notion_client/__init__.py | 18 | CODE | |
| LOW | notion_client/__init__.py | 18 | CODE | |
| LOW | notion_client/__init__.py | 18 | CODE | |
| LOW | notion_client/__init__.py | 18 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 33 | CODE | |
| LOW | notion_client/__init__.py | 51 | CODE | |
| LOW | notion_client/__init__.py | 51 | CODE | |
| LOW | notion_client/api_endpoints.py | 9 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | examples/intro_to_notion_api/basic/1_add_block.py | 18 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | …amples/intro_to_notion_api/basic/3_add_styled_block.py | 18 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | …amples/intro_to_notion_api/basic/2_add_linked_block.py | 18 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | …s/intro_to_notion_api/intermediate/3_query_database.py | 20 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | …ples/intro_to_notion_api/intermediate/5_upload_file.py | 18 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | …o_to_notion_api/intermediate/2_add_page_to_database.py | 20 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | …ntro_to_notion_api/intermediate/1_create_a_database.py | 18 | # --------------------------------------------------------------------------- | COMMENT |
| MEDIUM | …es/intro_to_notion_api/intermediate/4_sort_database.py | 20 | # --------------------------------------------------------------------------- | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM⚡ | tests/test_helpers.py | 492 | # Create a scenario that causes urlparse to fail | COMMENT |
| MEDIUM | examples/first_project/script.py | 34 | # Create a new page | COMMENT |
| MEDIUM | …s/intro_to_notion_api/intermediate/3_query_database.py | 62 | # Create a new database | COMMENT |
| MEDIUM | …ples/intro_to_notion_api/intermediate/5_upload_file.py | 87 | # Create a comment on the text block with the same image by providing | COMMENT |
| MEDIUM | …o_to_notion_api/intermediate/2_add_page_to_database.py | 41 | # Create a new database | COMMENT |
| MEDIUM | …ntro_to_notion_api/intermediate/1_create_a_database.py | 28 | # Create a new database | COMMENT |
| MEDIUM | …es/intro_to_notion_api/intermediate/4_sort_database.py | 70 | # Create a new database | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/conftest.py | 66 | except Exception: | CODE |
| LOW | tests/conftest.py | 102 | except Exception: | CODE |
| LOW | tests/conftest.py | 320 | except Exception: | CODE |
| LOW | tests/conftest.py | 324 | except Exception: | CODE |
| LOW | notion_client/client.py | 396 | except Exception as error: | CODE |
| LOW | notion_client/client.py | 487 | except Exception as error: | CODE |
| LOW | examples/database_email_update/database_email_update.py | 125 | except Exception as e: | CODE |
| LOW | examples/databases/create_database.py | 34 | except Exception as e: | CODE |
| LOW | examples/file_uploads/create_file_uploads_multi.py | 78 | except Exception as e: | CODE |
| LOW | examples/file_uploads/create_file_uploads_multi.py | 110 | except Exception as exc: | CODE |
| LOW | examples/web_form_with_fastapi/web_form_with_fastapi.py | 64 | except Exception as error: | CODE |
| LOW | examples/web_form_with_fastapi/web_form_with_fastapi.py | 93 | except Exception as error: | CODE |
| LOW | examples/web_form_with_fastapi/web_form_with_fastapi.py | 115 | except Exception as error: | CODE |
| LOW | examples/web_form_with_fastapi/web_form_with_fastapi.py | 131 | except Exception as error: | CODE |
| LOW | .github/scripts/monitor_notion_changelog.py | 119 | except Exception as e: | CODE |
| LOW | .github/scripts/monitor_notion_changelog.py | 125 | except Exception as e: | CODE |
| LOW | .github/scripts/monitor_notion_changelog.py | 160 | except Exception as e: | CODE |
| MEDIUM | .github/scripts/monitor_notion_changelog.py | 116 | def main(): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | notion_client/client.py | 161 | CODE | |
| LOW | examples/generate_random_data/generate_random_data.py | 41 | CODE | |
| LOW | examples/generate_random_data/generate_random_data.py | 102 | CODE | |
| LOW | examples/databases/create_database.py | 16 | CODE | |
| LOW | …_from_any_block_type/parse_text_from_any_block_type.py | 48 | CODE | |
| LOW | examples/file_uploads/create_file_uploads_multi.py | 14 | CODE | |
| LOW | examples/file_uploads/create_file_uploads_extenal.py | 13 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | notion_client/helpers.py | 182 | # Check if it's already a properly formatted UUID | COMMENT |
| LOW | notion_client/helpers.py | 189 | # Check if it's a compact UUID (32 chars, no hyphens) | COMMENT |
| LOW | examples/file_uploads/create_file_uploads_multi.py | 114 | # Check if all parts uploaded successfully | COMMENT |
| LOW | .github/scripts/monitor_notion_changelog.py | 107 | # Check if an issue with same title exists | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | notion_client/client.py | 74 | auth: Bearer token for authentication. If left undefined, the `auth` parameter | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | notion_client/__init__.py | 56 | __all__ = [ | CODE |
| LOW | .github/scripts/monitor_notion_changelog.py | 14 | logger = logging.getLogger(__name__) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | README.md | 84 | 'person': {'email': 'avo@example.org'}, | CODE |