Live demo · Notion + ChalkSQL

Your Notion workspace,
as SQL tables.

This demo shows ChalkSQL querying roadmaps, account lists and request trackers in Notion. ChalkSQL reads each Notion database as a table, turns your WHERE clause into a Notion filter, and joins what comes back with your warehouse in the same query.

Connect a SQL filter to the request Notion receives

Follow a question from SQL through the source API request to the final result.

Try this experiment · expected results

  1. 1Inspect the default query

    Choose At-risk revenue and compare the health and ARR predicates with the Notion request.

    Expected: The source receives the supported filters; ChalkSQL groups and sums the returned accounts.

  2. 2Compare a cross-source query

    Choose Join with the warehouse and inspect its result and request panel.

    Expected: Notion returns its matching pages while ChalkSQL combines them with warehouse usage.

Query result

Results appear here.

ChalkSQL

Filter and aggregate one Notion database. The IN list and the ARR threshold both become a Notion filter, so only matching pages come back.

What Notion received

Run a query to see the requests.

The workspace belongs to Fernhill, a fictional analytics company. Its roadmap, accounts and feature requests are served by a stand-in for Notion's REST API (database queries, filters, cursor pagination and search), so the exact requests are visible. The connector does not depend on the stand-in: point it at api.notion.com with an integration token and it reads a real workspace. ChalkSQL and the connector run live on a Chalk scaling group.

Built on Chalk

ChalkSQL reads the source through its connector, pushes supported work to that source, and runs the remaining SQL over the returned rows.

  • Databases become tables

    Register a Notion integration and each database it can see is a ChalkSQL table: title, text, select and status become strings, numbers become doubles, dates become dates, and people, multi-select and relations become lists.

  • WHERE becomes a Notion filter

    Before the scan runs, the optimizer offers it the query's predicates. The ones Notion can evaluate turn into a filter object on the database query, so Notion only sends back the pages you asked for.

  • SQL does the rest

    Joins, aggregates, window functions and anything Notion cannot evaluate run in ChalkSQL, across Notion and every other source in the same query. Results stream in a page at a time by following Notion's cursors.

  • Use ordinary SQL on returned rows

    ChalkSQL applies the remaining joins, grouping, and sorting to the rows returned by the connector. The request panel shows which filters the source received.

Deploy your own

A Notion source in one catalog call

The connector is a ChalkSQL schema provider. Each Notion database it is given becomes a table, discovered from the database's schema, and each scan is a pushdown-capable batch function that pages through Notion's query API.

Register it once and Notion is queryable from anywhere ChalkSQL runs, joined with anything else in the catalog.

from notion_chalksql import NotionClient, NotionSchemaProvider, RequestLog, register_client

register_client(
    "workspace",
    NotionClient("https://api.notion.com", token=NOTION_TOKEN, log=RequestLog()),
)

# One table per database the integration can see, plus notion.pages for search.
catalog.register_schema_provider(
    "notion",
    NotionSchemaProvider("workspace", {
        "roadmap": ROADMAP_DATABASE_ID,
        "accounts": ACCOUNTS_DATABASE_ID,
        "feature_requests": REQUESTS_DATABASE_ID,
    }),
)
Implementation details

How the example works

  • Databases become tables

    Register a Notion integration and each database it can see is a ChalkSQL table: title, text, select and status become strings, numbers become doubles, dates become dates, and people, multi-select and relations become lists.

  • WHERE becomes a Notion filter

    Before the scan runs, the optimizer offers it the query's predicates. The ones Notion can evaluate turn into a filter object on the database query, so Notion only sends back the pages you asked for.

  • SQL does the rest

    Joins, aggregates, window functions and anything Notion cannot evaluate run in ChalkSQL, across Notion and every other source in the same query. Results stream in a page at a time by following Notion's cursors.

SQL behavior and pushdown rules

  • Numbers and dates

    =, <>, <, <=, > and >= against a literal become Notion's number and date conditions, like greater_than or on_or_before. Exact, so ChalkSQL drops them.

  • Selects and statuses

    = and <> become equals and does_not_equal, and an IN list becomes an or of equals. Exact.

  • Text

    = and LIKE 'x%', '%x' or '%x%' become equals, starts_with, ends_with and contains. Notion compares text case-insensitively, so these narrow the scan and ChalkSQL re-checks every row.

  • Lists

    contains(tags, 'ml') on a multi-select or relation becomes Notion's contains. IS NULL and IS NOT NULL become is_empty and is_not_empty on any property.

  • AND and OR

    Combined into Notion's compound and / or filters. If only part of an AND can be pushed, that part still narrows the scan.

  • LIMIT

    A LIMIT with no ORDER BY becomes the request's page_size, and the scan stops once it has enough rows.

Query the tools your team already uses

Notion, your warehouse, your databases and your vector indexes in one SQL query. Talk to an engineer about the sources you want to join.