Skip to main content
RapidDev - Software Development Agency
bubble-integrationsBubble API Connector

Notion

Connect Bubble to Notion using the API Connector plugin with two required shared headers: Authorization (Bearer token, marked Private) and Notion-Version (2022-06-28). This keeps your secret token server-side. The most common pitfall is forgetting to add your integration to the target Notion database via the three-dot Connections menu — a valid token still returns 404 without that step.

What you'll learn

  • How to create a Notion internal integration and copy the secret_ token
  • How to connect the integration to a specific Notion database via the Connections menu
  • How to configure two required shared headers in Bubble's API Connector
  • How to query a Notion database with POST /databases/{id}/query
  • How to map nested Notion property values to a Bubble Repeating Group
  • How to write new pages back to Notion from a Bubble form
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate14 min read45–60 minutesProductivityLast updated July 2026RapidDev Engineering Team
TL;DR

Connect Bubble to Notion using the API Connector plugin with two required shared headers: Authorization (Bearer token, marked Private) and Notion-Version (2022-06-28). This keeps your secret token server-side. The most common pitfall is forgetting to add your integration to the target Notion database via the three-dot Connections menu — a valid token still returns 404 without that step.

Quick facts about this guide
FactValue
ToolNotion
CategoryProductivity
MethodBubble API Connector
DifficultyIntermediate
Time required45–60 minutes
Last updatedJuly 2026

Notion as a headless CMS for your Bubble app

The most popular reason teams connect Bubble to Notion is to use Notion as the content layer — editors manage database rows (blog posts, product listings, FAQs) inside Notion's familiar interface, and the Bubble app reads and displays that content in real time. Because Bubble's API Connector is server-side, your Notion secret token stays completely hidden from users, solving the security problem that trips up client-side tools. The two critical setup steps that almost every beginner skips: (1) adding the Notion-Version shared header with the exact value 2022-06-28 — without it every call returns HTTP 400, and (2) explicitly connecting the integration to each target database via Notion's three-dot menu. A valid token still returns 404 if that connection step is missed.

Integration method

Bubble API Connector

Call the Notion REST API v1 directly from Bubble's server-side API Connector — no proxy or Edge Function needed.

Prerequisites

  • A Notion account with at least one database containing data (Notion free plan works)
  • A Bubble account with an app on any plan — reading data via API Connector works on Free; writing back from server-side Backend Workflows requires a paid Bubble plan
  • The API Connector plugin installed in your Bubble app (Plugins tab → Add plugins → search 'API Connector' by Bubble)

Step-by-step guide

1

Create a Notion internal integration and copy the token

Open your browser and go to notion.so/my-integrations. Click 'New integration' and give it a descriptive name like 'Bubble App Integration'. Under 'Capabilities', ensure Read content and (if you plan to write) Insert content and Update content are checked. Click Save. On the next screen you will see your Internal Integration Token — it starts with secret_ and is a long string. Click 'Show' and then copy it to a temporary location such as a notes file. This token is your API key and must be kept private; it grants access to any Notion content you explicitly connect the integration to. Do not share it or put it in any code that runs in the browser.

Pro tip: Notion integration tokens start with secret_ — if yours starts with something else, you may have copied from the wrong section.

Expected result: You have a secret_ token saved and ready to paste into Bubble.

2

Connect the integration to your target Notion database

Navigate to the specific Notion database you want Bubble to read or write. This step is non-negotiable and is the #1 cause of confusing 404 errors: even a perfectly valid token returns 'object not found' if the integration has not been explicitly connected to the database. To connect it, open the database page in Notion, click the three-dot menu (•••) in the top-right corner of the page, scroll down to find 'Connections', click it, search for the integration name you just created, and click 'Confirm'. You should see the integration name appear in the Connections list. Repeat this step for every Notion database your Bubble app will access — the connection is per-database, not account-wide.

Pro tip: If you have multiple databases (e.g., Posts, Products, Feedback), you must connect the integration to each one individually.

Expected result: Your integration appears in the Connections list for the target database. Notion is now authorized to let the integration read and write that database's content.

3

Configure the API Connector with two required shared headers

In Bubble, open your app and go to the Plugins tab in the left sidebar. If you have not installed it yet, click 'Add plugins', search for 'API Connector', find the one published by Bubble, and click Install. Now click the API Connector entry in your plugin list. Click 'Add another API'. Name the API group something clear like 'Notion API'. Under 'Shared headers for all calls', add the first header: key = Authorization, value = Bearer YOUR_SECRET_TOKEN (replace with your actual token, and include the word Bearer followed by a space before the token). Check the Private checkbox on this header — this ensures the token is never sent to the browser. Add a second shared header: key = Notion-Version, value = 2022-06-28. This version header is required on every Notion API call; omitting it or changing the value causes HTTP 400 errors on every request. Do NOT mark Notion-Version as Private (it is not a secret, just a required API header). The base URL field can be left empty at the group level — you will specify full URLs in each individual call.

api-connector-headers-config.json
1{
2 "shared_headers": [
3 {
4 "key": "Authorization",
5 "value": "Bearer secret_your_token_here",
6 "private": true
7 },
8 {
9 "key": "Notion-Version",
10 "value": "2022-06-28",
11 "private": false
12 }
13 ]
14}

Pro tip: The exact Notion-Version value 2022-06-28 is required. Do not put quotes around it, do not add spaces, and do not change the date — Notion validates this header strictly.

Expected result: The API Connector group for Notion shows two shared headers: Authorization (Private) and Notion-Version. You are ready to add individual API calls.

4

Add the database query call and initialize it

Within the Notion API group in the API Connector, click 'Add another call'. Name this call something like 'Query Database'. Notion's database query endpoint is a POST request — this surprises many users because it feels like a read operation, but the endpoint requires POST so you can send a JSON body with filters and sort criteria. Set the Method dropdown to POST. In the URL field enter: https://api.notion.com/v1/databases/YOUR_DATABASE_ID/query — replace YOUR_DATABASE_ID with the actual ID from your Notion database URL (the string of letters and numbers after the last slash, before any question mark). Set 'Use as' to 'Data' so Bubble treats the response as a data source for Repeating Groups. In the Body section, check 'Send arbitrary data' and enter a minimal JSON body to start: {"page_size": 10}. Now click 'Initialize call'. Bubble will send a real request to Notion and parse the response schema. You must have at least one row of data in the database for the initialization to detect the field types correctly. After a successful initialization, Bubble lists the detected fields from results[0].properties — you will see fields like results[0].properties.Name.title[0].plain_text for text properties. Map these to your Bubble data structure by selecting the fields you need in the response schema editor.

notion-query-database.json
1POST https://api.notion.com/v1/databases/YOUR_DATABASE_ID/query
2
3Headers (shared, already configured):
4 Authorization: Bearer secret_... [Private]
5 Notion-Version: 2022-06-28
6
7Body:
8{
9 "page_size": 10,
10 "filter": {
11 "property": "Status",
12 "select": {
13 "equals": "Published"
14 }
15 },
16 "sorts": [
17 {
18 "property": "Created",
19 "direction": "descending"
20 }
21 ]
22}

Pro tip: Find your Notion database ID in the browser URL when viewing the database: it is the 32-character hex string between the last slash and any '?' character. Example: notion.so/myworkspace/abc123...def456?v=... — the ID is abc123...def456.

Expected result: The Initialize call succeeds with status 200 and Bubble detects a 'results' array with property fields. You can now bind this call to a Repeating Group's data source.

5

Bind the query results to a Repeating Group

Add a Repeating Group element to your Bubble page. In the Repeating Group's property editor, set Type of content to the response type from your Notion query call (it will appear as 'Notion API — Query Database's results' in the data source dropdown). Set the Data source to 'Get data from an external API' → select your Notion Query Database call. Bubble will prompt you to fill in any dynamic parameters — for example, if you configured a filter with a dynamic value, you map a Bubble input field value to that parameter here. Inside the Repeating Group, add text elements and bind each one to a field from the current cell: for example, bind a Text element to 'Current cell's results's properties Name title 0 plain_text' to display the row name. For rich text properties, Notion returns an array of text runs — use plain_text at the first array index for most cases. For number properties, the path is properties.Price.number. Test the page in Preview mode to verify the Repeating Group populates.

Pro tip: If the Repeating Group shows blank rows instead of data, check that the database has rows with the expected properties populated, and that the Notion-Version header is correctly set — blank responses often indicate a silent 400 error on the call.

Expected result: The Repeating Group displays rows from your Notion database in Bubble Preview mode, with property values rendering correctly in bound text elements.

6

Add a write-back call to create new Notion pages from a Bubble form

To let users submit data from Bubble that creates new rows in your Notion database, add a second API call to the Notion API group. Click 'Add another call', name it 'Create Page', set Method to POST, URL to https://api.notion.com/v1/pages. Set 'Use as' to 'Action' (not Data) since this call is triggered by a workflow, not bound to a display element. In the Body, check 'Send arbitrary data' and configure the JSON structure for a new Notion page. The parent must reference the database ID, and properties must match the exact property names and types in your Notion database. For a text property named 'Name', use the title type structure shown below. For the Initialize call, provide a filled-in example body and click Initialize to confirm the call structure. Then in your Bubble workflow, add a step 'Plugins → Notion API — Create Page', map your form input values to the dynamic body parameters you defined. Note: if you want this write-back to happen completely server-side (without exposing the call in client-side JavaScript), you need a Backend Workflow — which requires a paid Bubble plan.

notion-create-page.json
1POST https://api.notion.com/v1/pages
2
3Body:
4{
5 "parent": {
6 "database_id": "YOUR_DATABASE_ID"
7 },
8 "properties": {
9 "Name": {
10 "title": [
11 {
12 "text": {
13 "content": "<dynamic: form input value>"
14 }
15 }
16 ]
17 },
18 "Status": {
19 "select": {
20 "name": "New"
21 }
22 },
23 "Notes": {
24 "rich_text": [
25 {
26 "text": {
27 "content": "<dynamic: notes input value>"
28 }
29 }
30 ]
31 }
32 }
33}

Pro tip: Property names in the API body must match exactly what's in Notion, including capitalization and spacing. If your Notion property is named 'Full Name', the JSON key must be 'Full Name' — not 'full_name' or 'fullName'.

Expected result: Submitting the Bubble form triggers the Create Page API call and a new row appears in your Notion database within a few seconds.

Common use cases

Content CMS display

Use a Notion database as the content source for blog posts, announcements, or product listings. The marketing team edits rows in Notion; Bubble reads them via API and renders the content in a Repeating Group — no Bubble editor access required for content updates.

Bubble Prompt

Show a Repeating Group of blog posts pulled from a Notion database, displaying the title, publish date, and excerpt for each row.

Copy this prompt to try it in Bubble

User feedback collection

Capture form submissions from a Bubble page and create new pages in a Notion database. Customer feedback, bug reports, or sales leads land directly in a Notion table that your team already uses for triage.

Bubble Prompt

When a user submits the feedback form, create a new page in the 'Customer Feedback' Notion database with the user's name, email, message, and submission timestamp.

Copy this prompt to try it in Bubble

Internal operations dashboard

Build a Bubble app that lets your internal team view and update task or project rows stored in Notion. Useful for teams who want a custom-branded interface on top of their Notion workspace without giving everyone full Notion access.

Bubble Prompt

Display a filtered list of Notion database rows where 'Status' equals 'In Review', with a button that updates the status to 'Approved' when clicked.

Copy this prompt to try it in Bubble

Troubleshooting

Every API call returns HTTP 400 Bad Request immediately after setup

Cause: The Notion-Version shared header is missing, misspelled, or has an incorrect date format. Notion requires this header on every request and rejects calls without it with a generic 400 error.

Solution: Go to Plugins → API Connector → Notion API group → Shared headers. Verify a header with key Notion-Version and value exactly 2022-06-28 is present. No quotes, no extra characters, no trailing space. Re-initialize the call after fixing the header.

API calls return HTTP 404 'object not found' even though the token looks correct

Cause: The Notion integration has not been connected to the specific database you are querying. Notion requires an explicit per-database connection step — the integration token alone is not sufficient.

Solution: Open the target Notion database, click the three-dot menu (•••) in the top right, click Connections, search for your integration by name, and click Confirm. Then retry the call in Bubble.

Initialize call fails with 'There was an issue setting up your call' or no response detected

Cause: The Initialize call requires a real successful API response to detect the schema. This fails if the database is empty, the database ID is wrong, or the call method is set to GET instead of POST.

Solution: First, confirm the database has at least one row of data populated. Second, verify the database ID in the URL is correct. Third, ensure the Method is set to POST (not GET) for the /query endpoint. After fixing these, click Initialize again.

Repeating Group shows the correct number of rows but all text fields are blank

Cause: Notion property values are deeply nested. For a title property called 'Name', the path is results[0].properties.Name.title[0].plain_text — skipping any level of this path returns an empty or undefined value in Bubble.

Solution: In the API Connector response schema editor, expand the results array and check the exact path Bubble detected for each property. For title properties, look for an array element under title (or rich_text) and select plain_text. Re-initialize the call if you changed the database properties since the last initialization.

Create Page returns HTTP 400 when submitting from the Bubble form

Cause: The JSON body structure for the properties does not match the property types defined in the Notion database. For example, sending a plain string for a title property instead of the title array structure causes 400.

Solution: Review the Notion API documentation for each property type you are writing to. Title properties require the title array with a text.content key. Rich text properties use rich_text. Select properties use select.name. Make sure the property names in the JSON body match exactly what is shown in the Notion database column headers.

Best practices

  • Always mark the Authorization header as Private in Bubble's API Connector — this is the single most important security step, keeping your Notion secret_ token off the client side entirely.
  • Cache infrequently changing Notion data in a Bubble database table rather than querying the Notion API on every page load. Each API call costs Bubble Workload Units (WU); caching data that changes once a day (like a blog post list) can reduce WU consumption significantly.
  • Add Privacy rules in Bubble's Data tab for any Things created from Notion data — without Privacy rules, any logged-in user can access all records via Bubble's API endpoints.
  • Use Notion's filter and sorts parameters in the query body rather than fetching all rows and filtering in Bubble workflows. Server-side filtering reduces the data volume Bubble processes and improves response time.
  • Store the Notion database ID in a Bubble App Data field or as a constant in the API Connector URL rather than hardcoding it in multiple places — makes it easier to update if you switch databases.
  • Initialize calls with a real database that has diverse content (multiple property types populated) to ensure Bubble detects all the fields you need in the response schema.
  • For heavy paginated reads using Notion's next_cursor parameter, consider scheduling a Bubble Backend Workflow (paid plan) to pre-fetch and store content in Bubble's database on a schedule, rather than paginating live on each user request.

Alternatives

Frequently asked questions

Do I need a paid Notion plan to use the Notion API with Bubble?

No. Notion's API and internal integrations are available on all plans, including the free Notion plan. You can read and write Notion databases from Bubble without any Notion subscription. However, creating a Bubble Backend Workflow to handle server-side write-backs requires a paid Bubble plan (Starter or above).

Why does the Notion API require POST for reading a database? That seems backwards.

Notion's database query endpoint (POST /databases/{id}/query) is a POST because it accepts a JSON body with complex filter and sort criteria — GET requests do not allow request bodies in the HTTP standard. This is a deliberate API design choice by Notion. In Bubble's API Connector, set the call Method to POST and check 'Send arbitrary data' to enable a body on the call.

Can I display rich text (formatted content with bold, italics, links) from Notion in Bubble?

Bubble's standard text elements render plain text, not HTML. Notion rich_text properties return an array of text runs with annotations (bold, italic, color). The simplest approach is to extract plain_text from each run and concatenate them in a Bubble workflow. For full rich text rendering with formatting, you would need to transform the Notion rich_text array into HTML and use a Bubble HTML element — this is a more advanced pattern.

What happens if I have more than 100 rows in the Notion database?

Notion's query endpoint returns a maximum of 100 results per call (controlled by the page_size parameter). If the response includes 'has_more': true and a 'next_cursor' value, there are additional pages. To load more results, make a follow-up POST to the same endpoint with the 'start_cursor' parameter set to the next_cursor value. Each pagination request counts as a separate API call and consumes Bubble WU — consider using Bubble's 'Schedule API Workflow' on paid plans to pre-fetch and store large datasets in Bubble's database.

Is my Notion token really secure if it's in Bubble's API Connector?

Yes — as long as you check the Private checkbox on the Authorization header. Bubble's API Connector runs from Bubble's servers, and Private headers are stripped from any client-side representation. The token never appears in network requests initiated from the user's browser. This is fundamentally more secure than calling the Notion API directly from client-side JavaScript.

I'm building a complex Bubble app with many Notion integrations. Can RapidDev help?

Yes. RapidDev's team has built hundreds of Bubble apps with API integrations like Notion, including content portals, internal dashboards, and feedback systems. If you need help architecting the integration or getting unstuck on a specific issue, book a free scoping call at rapidevelopers.com/contact.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Integrations are where projects stall

We wire Bubble integrations like this one every week — auth, webhooks, edge cases included. Fixed-price builds from $13K, delivered in 6–10 weeks.

Talk to an integration engineer

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.