Notion API Changelog April 2026: Every Developer-Facing Change

Matthew Diakonov··13 min read

Notion API Changelog April 2026: Every Developer-Facing Change

Notion shipped API version 2026-04-01 in the first week of April 2026. The update includes eight new Views API endpoints, expanded smart filter operators, webhook delivery improvements, and two new block types. This post covers every change, shows the request/response shapes, and flags the migration gotchas you will hit when upgrading from 2026-03-01.

What Shipped in April 2026

The April changelog is one of the larger API updates Notion has released. Most of the changes center on programmable database views, which were previously only configurable through the Notion UI.

| Change | Category | API Version | Breaking? | |---|---|---|---| | Views API (8 endpoints) | New feature | 2026-04-01 | No | | Smart filter relative dates | Enhancement | 2026-04-01 | No | | Smart filter "me" operator | Enhancement | 2026-04-01 | No | | Webhook retry backoff update | Infrastructure | 2026-04-01 | No | | Webhook event_type field addition | Enhancement | 2026-04-01 | No | | Toggle heading block type | New block | 2026-04-01 | No | | Synced block copy support | New block | 2026-04-01 | No | | archived filter on database queries | Enhancement | 2026-04-01 | No | | Rate limit header changes | Infrastructure | 2026-04-01 | Yes (soft) | | Pagination cursor format change | Infrastructure | 2026-04-01 | Yes (soft) |

Tip

None of the April changes are hard breaking. You can upgrade by setting the Notion-Version header to 2026-04-01. The two "soft breaking" changes (rate limit headers and pagination cursors) only affect code that parses those values directly.

Views API: 8 New Endpoints

The biggest addition is the Views API. Before April 2026, there was no way to create, read, update, or delete database views through the API. If your integration needed a filtered or sorted view, you had to set up the view manually in the Notion UI and then query the database with equivalent filter/sort parameters.

Now you can manage views programmatically:

# List all views on a database
curl -X GET "https://api.notion.com/v1/databases/{database_id}/views" \
  -H "Authorization: Bearer $NOTION_TOKEN" \
  -H "Notion-Version: 2026-04-01"

# Create a new table view with a filter
curl -X POST "https://api.notion.com/v1/databases/{database_id}/views" \
  -H "Authorization: Bearer $NOTION_TOKEN" \
  -H "Notion-Version: 2026-04-01" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "table",
    "name": "Active Tasks",
    "filter": {
      "property": "Status",
      "status": { "equals": "In Progress" }
    },
    "sorts": [
      { "property": "Due Date", "direction": "ascending" }
    ]
  }'

Endpoint Reference

| Method | Path | Description | |---|---|---| | GET | /v1/databases/{id}/views | List views on a database | | POST | /v1/databases/{id}/views | Create a new view | | GET | /v1/views/{id} | Retrieve a single view | | PATCH | /v1/views/{id} | Update a view's config | | DELETE | /v1/views/{id} | Delete a view | | POST | /v1/views/{id}/duplicate | Clone a view | | PATCH | /v1/views/{id}/properties | Configure visible columns | | POST | /v1/views/{id}/query | Query a database through a specific view |

The /v1/views/{id}/query endpoint is particularly useful. Instead of rebuilding a view's filter and sort logic in your integration code, you can query through the view directly and get the same results the user sees in the Notion UI.

Supported View Types

The API supports five view types: table, board, timeline, calendar, and list. Gallery views are not yet supported through the API (the endpoint returns a 400 if you try to create one).

DatabaseTable ViewBoard ViewGallery(not yet in API)TimelineCalendarList ViewFilter + Sort(per view)

Smart Filter Improvements

Two new filter operators landed in 2026-04-01:

Relative Date References

You can now filter by relative dates without computing timestamps in your integration code. The new operators are past_week, past_month, next_week, next_month, and this_week.

{
  "filter": {
    "property": "Due Date",
    "date": {
      "this_week": {}
    }
  }
}

Before this update, you had to calculate the start and end of the current week in your timezone and pass explicit on_or_after / on_or_before values. The new operators handle timezone logic server-side using the workspace's configured timezone.

The "me" Operator

For people type properties, you can now use "me" as a filter value that resolves to the integration's connected user (or the user who authorized the OAuth token):

{
  "filter": {
    "property": "Assigned To",
    "people": {
      "contains": "me"
    }
  }
}

This is especially useful for building personal dashboards where each user sees only their own assignments. Previously, you needed to resolve the current user's Notion ID first and pass it explicitly.

Webhook Improvements

The webhook system (introduced in March 2026) received two upgrades:

New event_type Field

Webhook payloads now include an event_type field at the top level. Before, you had to inspect the nested data object to determine what happened. The new field simplifies routing:

{
  "event_type": "page.property_changed",
  "event_id": "evt_abc123",
  "timestamp": "2026-04-09T14:23:01.000Z",
  "data": {
    "page_id": "page_xyz",
    "property": "Status",
    "old_value": { "status": { "name": "To Do" } },
    "new_value": { "status": { "name": "Done" } }
  }
}

The supported event types are:

| Event Type | Trigger | |---|---| | page.created | New page added to database | | page.updated | Any page property changed | | page.property_changed | Specific property changed | | page.deleted | Page moved to trash | | page.restored | Page restored from trash | | block.created | Block added to a page | | block.updated | Block content changed | | block.deleted | Block removed |

Retry Backoff Changes

The retry window expanded from 24 hours to 48 hours, and the backoff schedule changed. Retries now happen at 1 minute, 5 minutes, 30 minutes, 2 hours, 8 hours, 24 hours, and 48 hours (seven attempts total, up from five). If your endpoint is down for a brief maintenance window, you are less likely to miss events.

New Block Types

Toggle Heading

The API now supports heading_1, heading_2, and heading_3 blocks with a is_toggleable property:

{
  "type": "heading_2",
  "heading_2": {
    "rich_text": [{ "text": { "content": "FAQ Section" } }],
    "is_toggleable": true,
    "children": [
      {
        "type": "paragraph",
        "paragraph": {
          "rich_text": [{ "text": { "content": "Content hidden under the toggle." } }]
        }
      }
    ]
  }
}

Toggle headings have been available in the Notion UI for over a year, but the API previously treated them as plain headings and silently dropped the toggle children on read. Now the is_toggleable field is returned on GET requests and accepted on POST/PATCH.

Synced Block Copy

You can now create a synced copy of an existing synced block via the API:

{
  "type": "synced_block",
  "synced_block": {
    "synced_from": {
      "block_id": "original_block_id"
    }
  }
}

The synced_from.block_id must reference the original (source) synced block, not another copy. Attempting to sync from a copy returns a 400 with a message pointing you to the original.

Rate Limit Header Changes

The rate limit response headers changed format in 2026-04-01:

| Old Header (pre-April) | New Header (April+) | Example | |---|---|---| | X-RateLimit-Limit | RateLimit-Limit | 3 | | X-RateLimit-Remaining | RateLimit-Remaining | 2 | | X-RateLimit-Reset | RateLimit-Reset | 1 (seconds) | | (none) | RateLimit-Policy | 3;w=1 |

The X- prefixed headers are still returned alongside the new ones for backward compatibility, but the Notion team has marked them as deprecated. If your retry logic parses these headers directly, update to the new names. The RateLimit-Policy header follows the IETF draft standard (RFC 9110) and tells you the quota window size.

Warning

The RateLimit-Reset value changed from a Unix timestamp to a delta in seconds. If your retry logic does reset_time - current_time, it will compute a wildly wrong sleep duration with the new format. Check which API version your requests use before parsing this header.

Pagination Cursor Format Change

Pagination cursors returned by 2026-04-01 are now opaque base64-encoded strings instead of the previous UUID-like format. The cursors from the old format still work when passed to newer API versions, but cursors from the new format do not work with older API versions. If your system stores cursors across sessions (for incremental sync, for example), you will need to invalidate stored cursors when you upgrade your Notion-Version header.

Archived Pages Filter

Database queries now accept an archived filter parameter:

curl -X POST "https://api.notion.com/v1/databases/{database_id}/query" \
  -H "Authorization: Bearer $NOTION_TOKEN" \
  -H "Notion-Version: 2026-04-01" \
  -H "Content-Type: application/json" \
  -d '{
    "filter": {
      "property": "Status",
      "status": { "equals": "Done" }
    },
    "archived": true
  }'

Setting "archived": true returns only archived (trashed) pages. Setting it to false (the default) returns only active pages. This is useful for building "recently deleted" views or audit trails. Before this parameter, archived pages were invisible to the API entirely.

Migration Checklist

If you are upgrading from 2026-03-01 to 2026-04-01, here is what to check:

Update Notion-Version header to 2026-04-01
Switch rate limit header parsing from X-RateLimit prefixed headers to RateLimit prefixed headers
Fix RateLimit-Reset parsing (now seconds delta, not Unix timestamp)
Invalidate any stored pagination cursors (new format is not backward-compatible)
Update webhook handler to read event_type at top level
Test heading block reads for the new is_toggleable field (may affect content diffing)

Common Pitfalls

  • Gallery view creation via API: The endpoint accepts a type field but returns 400 for "gallery". There is no error message explaining that gallery is unsupported; you just get a generic validation error. Check the view type before creating.

  • Synced block chain: Trying to sync from a synced copy (instead of the original) silently returns a 400. If your code discovers synced blocks by traversing a page, you need to check the synced_from field to find the original.

  • Cursor migration: If you cache start_cursor values for incremental database syncs, upgrading the API version mid-sync will break. Finish any in-progress sync before changing the Notion-Version header, or reset your sync state entirely.

  • Relative date timezone: The new relative date filters use the workspace timezone, not UTC and not the requesting user's timezone. If your workspace is set to America/Los_Angeles and your server is in UTC, "this_week" starts on Sunday Pacific time. Verify your workspace timezone setting matches your expectations.

  • Rate limit double headers: Both old and new rate limit headers are returned simultaneously. If your HTTP client merges duplicate header names (some do), you may get unexpected values. Parse by the new header name explicitly.

Minimal Working Example

A complete script that creates a filtered view on a database, queries through it, and prints results:

import requests
import os

NOTION_TOKEN = os.environ["NOTION_TOKEN"]
DATABASE_ID = os.environ["NOTION_DATABASE_ID"]
BASE = "https://api.notion.com/v1"
HEADERS = {
    "Authorization": f"Bearer {NOTION_TOKEN}",
    "Notion-Version": "2026-04-01",
    "Content-Type": "application/json",
}

# Create a view showing only this week's tasks
view = requests.post(
    f"{BASE}/databases/{DATABASE_ID}/views",
    headers=HEADERS,
    json={
        "type": "table",
        "name": "This Week",
        "filter": {
            "property": "Due Date",
            "date": {"this_week": {}},
        },
        "sorts": [{"property": "Due Date", "direction": "ascending"}],
    },
).json()

print(f"Created view: {view['id']}")

# Query through the view
results = requests.post(
    f"{BASE}/views/{view['id']}/query",
    headers=HEADERS,
    json={},
).json()

for page in results.get("results", []):
    title_prop = page["properties"].get("Name", {})
    title = title_prop.get("title", [{}])[0].get("plain_text", "Untitled")
    print(f"  - {title}")

Wrapping Up

The April 2026 Notion API update is primarily about the Views API, which fills one of the longest-standing gaps in the platform's developer tooling. The smart filter improvements and webhook upgrades are smaller but immediately practical. If you are upgrading, the rate limit header change and pagination cursor format are the two things most likely to bite you in production.

Fazm is an open source macOS AI agent. Open source on GitHub.

Related Posts