Skip to content
Syncline

Notion API reference

<p>This page documents the Syncline endpoints for the Notion connector. Every route below lives on the Syncline API, is authenticated with a Syncline key, and returns a Syncline response shape. It is not a mirror of the Notion API and it does not document Notion's own routes: when you need those, read the vendor's own developer site.</p><p>The connector sits between your code and the workspace you authorised. You address pages and databases by their Notion identifiers, we handle paging, backoff and property casting, and every call returns a run identifier that appears in the flow history for the same connection. Quote that identifier in a support ticket and we can find the exact call in seconds.</p>
Base URL: https://api.syncline.topclb.com

Authentication

Send a Syncline secret key as a bearer token: Authorization: Bearer sk_live_... . The key identifies your workspace, and the connection_id parameter identifies which authorised Notion workspace the call should use. Keys are scoped read or read and write when you create them, and a read key calling a write route gets 403 rather than a partial result.

Endpoints

<article class="sy-endpoint"><h3><code>GET /v1/connectors/notion/databases</code></h3><p>List the Notion databases shared with a connection, newest first.</p><div class="sy-table-wrap"><table><thead><tr><th>Param</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>connection_id</td><td>string</td><td>yes</td><td>The authorised Notion connection to read through, for example con_notion_7g2.</td></tr><tr><td>query</td><td>string</td><td>no</td><td>Case insensitive substring match on the database title.</td></tr><tr><td>limit</td><td>integer</td><td>no</td><td>Results per page, 1 to 100. Defaults to 25.</td></tr><tr><td>cursor</td><td>string</td><td>no</td><td>Opaque cursor from the previous response to fetch the next page.</td></tr></tbody></table></div><pre><code>{ &quot;run_id&quot;: &quot;run_4c81ba90&quot;, &quot;connector&quot;: &quot;notion&quot;, &quot;status&quot;: &quot;ok&quot;, &quot;data&quot;: [ { &quot;database_id&quot;: &quot;a3f1c9d24b8e4f01&quot;, &quot;title&quot;: &quot;Launch tracker&quot;, &quot;properties&quot;: [&quot;Name&quot;, &quot;Status&quot;, &quot;Owner&quot;, &quot;Due date&quot;], &quot;last_edited_at&quot;: &quot;2026-09-05T14:22:11Z&quot; } ], &quot;next_cursor&quot;: null }</code></pre></article><article class="sy-endpoint"><h3><code>POST /v1/connectors/notion/pages</code></h3><p>Create a page as a row in a database, with typed properties.</p><div class="sy-table-wrap"><table><thead><tr><th>Param</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>connection_id</td><td>string</td><td>yes</td><td>The authorised Notion connection to write through.</td></tr><tr><td>database_id</td><td>string</td><td>yes</td><td>The database that will hold the new row.</td></tr><tr><td>properties</td><td>object</td><td>yes</td><td>Property name to value. Values are cast to the destination type and rejected rather than coerced when the cast is lossy.</td></tr><tr><td>children</td><td>array</td><td>no</td><td>Body blocks to append after the page is created. Paragraph, bulleted_list_item and callout are supported.</td></tr><tr><td>idempotency_key</td><td>string</td><td>no</td><td>Any string you generate. Repeating it within 24 hours returns the first result instead of creating a second page.</td></tr></tbody></table></div><pre><code>{ &quot;run_id&quot;: &quot;run_51de73aa&quot;, &quot;connector&quot;: &quot;notion&quot;, &quot;status&quot;: &quot;ok&quot;, &quot;data&quot;: { &quot;page_id&quot;: &quot;7b0e5cf1a94d4c8e&quot;, &quot;url&quot;: &quot;https://www.notion.so/7b0e5cf1a94d4c8e&quot;, &quot;created_at&quot;: &quot;2026-09-07T09:41:02Z&quot;, &quot;properties_written&quot;: 4, &quot;properties_skipped&quot;: [] } }</code></pre></article><article class="sy-endpoint"><h3><code>PATCH /v1/connectors/notion/pages/{page_id}</code></h3><p>Update properties on an existing page, or send it to the trash.</p><div class="sy-table-wrap"><table><thead><tr><th>Param</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>connection_id</td><td>string</td><td>yes</td><td>The authorised Notion connection to write through.</td></tr><tr><td>properties</td><td>object</td><td>no</td><td>Only the properties you send are touched. Everything else on the page is left alone.</td></tr><tr><td>archived</td><td>boolean</td><td>no</td><td>Set true to move the page to the Notion trash, where it stays recoverable for thirty days.</td></tr><tr><td>if_unmodified_since</td><td>string</td><td>no</td><td>ISO 8601 timestamp. The write is refused with 409 when the page changed after it.</td></tr></tbody></table></div><pre><code>{ &quot;run_id&quot;: &quot;run_60a2c118&quot;, &quot;connector&quot;: &quot;notion&quot;, &quot;status&quot;: &quot;ok&quot;, &quot;data&quot;: { &quot;page_id&quot;: &quot;7b0e5cf1a94d4c8e&quot;, &quot;updated&quot;: [&quot;Status&quot;, &quot;Owner&quot;], &quot;archived&quot;: false, &quot;last_edited_at&quot;: &quot;2026-09-07T09:44:50Z&quot; } }</code></pre></article><article class="sy-endpoint"><h3><code>POST /v1/connectors/notion/pages/search</code></h3><p>Find a page by a property value so a flow can update instead of duplicate.</p><div class="sy-table-wrap"><table><thead><tr><th>Param</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>connection_id</td><td>string</td><td>yes</td><td>The authorised Notion connection to read through.</td></tr><tr><td>database_id</td><td>string</td><td>yes</td><td>The database to search inside. Cross database search is not offered on purpose.</td></tr><tr><td>property</td><td>string</td><td>yes</td><td>The property to match on, such as Email or Order number.</td></tr><tr><td>value</td><td>string</td><td>yes</td><td>The value to look for. Compared after trimming whitespace on both sides.</td></tr><tr><td>match</td><td>string</td><td>no</td><td>One of exact or contains. Defaults to exact, which is what deduplication wants.</td></tr></tbody></table></div><pre><code>{ &quot;run_id&quot;: &quot;run_7712fe3b&quot;, &quot;connector&quot;: &quot;notion&quot;, &quot;status&quot;: &quot;ok&quot;, &quot;data&quot;: { &quot;match_count&quot;: 1, &quot;pages&quot;: [ { &quot;page_id&quot;: &quot;7b0e5cf1a94d4c8e&quot;, &quot;title&quot;: &quot;Harbour Line Coffee&quot;, &quot;matched_on&quot;: &quot;Email&quot; } ] } }</code></pre></article><article class="sy-endpoint"><h3><code>GET /v1/connectors/notion/events</code></h3><p>Read the trigger events this connection observed, for replay or reconciliation.</p><div class="sy-table-wrap"><table><thead><tr><th>Param</th><th>Type</th><th>Required</th><th>Description</th></tr></thead><tbody><tr><td>connection_id</td><td>string</td><td>yes</td><td>The authorised Notion connection whose events you want.</td></tr><tr><td>since</td><td>string</td><td>no</td><td>ISO 8601 timestamp. Events are retained for 30 days on paid plans and 7 on the free plan.</td></tr><tr><td>type</td><td>string</td><td>no</td><td>Filter to one event type, such as page.created or page.property_updated.</td></tr><tr><td>limit</td><td>integer</td><td>no</td><td>Results per page, 1 to 200. Defaults to 50.</td></tr></tbody></table></div><pre><code>{ &quot;run_id&quot;: &quot;run_88c04ad2&quot;, &quot;connector&quot;: &quot;notion&quot;, &quot;status&quot;: &quot;ok&quot;, &quot;data&quot;: [ { &quot;event_id&quot;: &quot;evt_2f9a71&quot;, &quot;type&quot;: &quot;page.property_updated&quot;, &quot;page_id&quot;: &quot;7b0e5cf1a94d4c8e&quot;, &quot;property&quot;: &quot;Status&quot;, &quot;from&quot;: &quot;Draft&quot;, &quot;to&quot;: &quot;Ready&quot;, &quot;observed_at&quot;: &quot;2026-09-07T09:12:44Z&quot; } ], &quot;next_cursor&quot;: &quot;evt_2f9a71&quot; }</code></pre></article>

Errors

<div class="sy-table-wrap"><table><thead><tr><th>Code</th><th>Meaning</th><th>What to do</th></tr></thead><tbody><tr><td>401 connection_revoked</td><td>The Notion authorisation behind this connection was withdrawn, so no call can be made through it.</td><td>Reconnect the workspace from Connections in your Syncline account. Flows resume on their next scheduled run with nothing lost.</td></tr><tr><td>403 database_not_shared</td><td>The database exists but was not ticked during the Notion connection step, so the API never returns it to us.</td><td>Open the Notion connections screen, add the database to the shared selection, then retry. No change is needed on the Syncline side.</td></tr><tr><td>404 property_not_found</td><td>A property name in your request does not exist on the target database, usually because somebody renamed it.</td><td>Call the databases endpoint to read the current property list, then update the name in your request or in the flow mapping.</td></tr><tr><td>409 select_option_missing</td><td>You sent a select or multi select value that is not one of the configured options, and we refuse to guess.</td><td>Add the option in Notion, or set allow_new_options on the mapping, or translate the value before sending it.</td></tr><tr><td>429 upstream_rate_limited</td><td>Notion throttled the call. We already retried with backoff and the window did not clear in time.</td><td>Read the retry_after seconds in the response body and try again after it. Batched writes and fewer polls both reduce how often this appears.</td></tr></tbody></table></div>

Rate limits

Syncline allows 120 requests per minute per API key across all connectors, measured on a rolling window. Notion itself accepts roughly three requests per second per integration, so the connector paces calls to stay under that and queues bursts rather than dropping them. Every response carries X-Syncline-RateLimit-Remaining and, when throttling is active, a retry_after value in seconds.

Code samples

<h4>curl</h4><pre><code>curl -X POST https://api.syncline.topclb.com/v1/connectors/notion/pages \ -H &quot;Authorization: Bearer $SYNCLINE_KEY&quot; \ -H &quot;Content-Type: application/json&quot; \ -d '{ &quot;connection_id&quot;: &quot;con_notion_7g2&quot;, &quot;database_id&quot;: &quot;a3f1c9d24b8e4f01&quot;, &quot;properties&quot;: { &quot;Name&quot;: &quot;Q3 launch brief&quot;, &quot;Status&quot;: &quot;Ready&quot;, &quot;Due date&quot;: &quot;2026-09-30&quot; } }'</code></pre><h4>node</h4><pre><code>const res = await fetch( &quot;https://api.syncline.topclb.com/v1/connectors/notion/pages&quot;, { method: &quot;POST&quot;, headers: { Authorization: `Bearer ${process.env.SYNCLINE_KEY}`, &quot;Content-Type&quot;: &quot;application/json&quot; }, body: JSON.stringify({ connection_id: &quot;con_notion_7g2&quot;, database_id: &quot;a3f1c9d24b8e4f01&quot;, properties: { Name: &quot;Q3 launch brief&quot;, Status: &quot;Ready&quot; } }) } ); if (!res.ok) throw new Error(`Syncline ${res.status}`); const { data } = await res.json(); console.log(data.page_id);</code></pre><h4>python</h4><pre><code>import os import requests resp = requests.post( &quot;https://api.syncline.topclb.com/v1/connectors/notion/pages&quot;, headers={&quot;Authorization&quot;: f&quot;Bearer {os.environ['SYNCLINE_KEY']}&quot;}, json={ &quot;connection_id&quot;: &quot;con_notion_7g2&quot;, &quot;database_id&quot;: &quot;a3f1c9d24b8e4f01&quot;, &quot;properties&quot;: {&quot;Name&quot;: &quot;Q3 launch brief&quot;, &quot;Status&quot;: &quot;Ready&quot;}, }, timeout=30, ) resp.raise_for_status() print(resp.json()[&quot;data&quot;][&quot;page_id&quot;])</code></pre>