> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mark2notion.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Append to Notion Page

> Convert Markdown and append it directly to a Notion page

## Overview

The append endpoint converts Markdown text to Notion blocks and appends them directly to an existing Notion page. This combines the convert functionality with direct Notion integration.

**Full Markdown Support**: The append endpoint uses the same conversion engine as the `/api/convert` endpoint, which means it supports all markdown features including:

* HTML colors: `<span style="color: red">colored text</span>`
* Toggle blocks: `<details><summary>Title</summary>Content</details>`
* GFM alerts (callouts): `> [!NOTE]`, `> [!WARNING]`, etc.
* Nested lists with unlimited depth
* Tables with headers
* All standard formatting (bold, italic, code, strikethrough)

If you already have Notion block JSON (for example from a previous `/convert` call or another system), use the [`/append-blocks`](./append-blocks) endpoint to append without reconverting.

## Request

<ParamField header="x-api-key" type="string" required>
  Your Mark2Notion API key
</ParamField>

<ParamField body="markdown" type="string" required>
  The Markdown content to convert and append. The API accepts both actual newlines and escaped sequences (`\n`, `\r\n`, `\r`), so you can use either format based on your tooling.
</ParamField>

<ParamField body="notionToken" type="string">
  Your Notion integration token. Optional when your workspace is connected via OAuth in the dashboard. Pass this if you prefer to authenticate with a manual integration token instead. See [Using a Manual Notion Token](/quickstart#using-a-manual-notion-token-advanced).
</ParamField>

<ParamField body="pageId" type="string" required>
  The URL or page ID of the Notion page to append content to. Pass the full `notion.so` page URL or just the page ID — both are accepted.
</ParamField>

<ParamField body="after" type="string">
  Optional block ID to append content after. If not provided, content is appended to the end of the page.
</ParamField>

## Response

<ResponseField name="status" type="string">
  Will be "success" for successful requests
</ResponseField>

<ResponseField name="data" type="object">
  <Expandable title="data">
    <ResponseField name="totalBlocks" type="number">
      Total number of blocks that were appended
    </ResponseField>

    <ResponseField name="requestCount" type="number">
      Number of internal Notion API requests made (for transparency - does NOT affect your usage quota)
    </ResponseField>

    <ResponseField name="retryCount" type="number">
      Number of retry attempts due to rate limiting
    </ResponseField>

    <ResponseField name="lastBlockId" type="string">
      ID of the last block that was created (useful for subsequent appends)
    </ResponseField>

    <ResponseField name="warnings" type="array">
      Array of warning messages when content modifications are made:

      * URL truncation notifications (when URLs exceed 2000 characters)
      * Equation truncation notifications (when equations exceed 1000 characters)
    </ResponseField>
  </Expandable>
</ResponseField>

## Examples

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST "https://api.mark2notion.com/api/append" \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_API_KEY" \
    -d '{
      "markdown": "# New Section\n\nThis content will be appended to my Notion page.\n\n- Item 1\n- Item 2",
      "pageId": "https://notion.so/Your-Page-Title-a1b2c3d4e5f67890abcdef1234567890"
    }'
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.mark2notion.com/api/append', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-api-key': 'YOUR_API_KEY'
    },
    body: JSON.stringify({
      markdown: `# New Section

  This content will be appended to my Notion page.

  - Item 1
  - Item 2`,
      pageId: 'https://notion.so/Your-Page-Title-a1b2c3d4e5f67890abcdef1234567890'
    })
  });

  const data = await response.json();
  console.log(`Added ${data.data.totalBlocks} blocks`);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.mark2notion.com/api/append',
      headers={
          'Content-Type': 'application/json',
          'x-api-key': 'YOUR_API_KEY'
      },
      json={
          'markdown': '''# New Section

  This content will be appended to my Notion page.

  - Item 1
  - Item 2''',
          'pageId': 'https://notion.so/Your-Page-Title-a1b2c3d4e5f67890abcdef1234567890'
      }
  )

  data = response.json()
  print(f"Added {data['data']['totalBlocks']} blocks")
  ```
</CodeGroup>

<ResponseExample>
  ```json Standard Response theme={null}
  {
    "status": "success",
    "data": {
      "totalBlocks": 4,
      "requestCount": 1,
      "retryCount": 0,
      "lastBlockId": "f9e8d7c6-b5a4-3210-9876-543210fedcba"
    }
  }
  ```

  ```json Standard Response (Large Content) theme={null}
  {
    "status": "success",
    "data": {
      "totalBlocks": 1247,
      "requestCount": 3,
      "retryCount": 1,
      "lastBlockId": "f9e8d7c6-b5a4-3210-9876-543210fedcba"
    }
  }
  ```

  ```json Response with Content Truncation theme={null}
  {
    "status": "success",
    "data": {
      "totalBlocks": 15,
      "requestCount": 1,
      "retryCount": 0,
      "lastBlockId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "warnings": [
        "Content truncated due to Notion API limits:",
        "• 2 URLs truncated to 2000 characters",
        "• 1 equation truncated to 1000 characters"
      ]
    }
  }
  ```

  ```json Response with Deep Nesting theme={null}
  {
    "status": "success", 
    "data": {
      "totalBlocks": 87,
      "requestCount": 4,
      "retryCount": 0,
      "lastBlockId": "xyz789-abc123-def456-ghi789"
    }
  }
  ```
</ResponseExample>

## Append Positioning

### Default Behavior

By default, content is appended to the end of the page.

### Using the `after` Parameter

Specify a block ID to append content after a specific block:

```json theme={null}
{
  "markdown": "# New content",
  "pageId": "https://notion.so/Your-Page-Title-abc123",
  "after": "block-id-to-append-after"
}
```

### Chaining Appends

Use the `lastBlockId` from the response to chain multiple append operations:

```javascript theme={null}
// First append
const firstAppend = await appendToNotion({
  markdown: "# First section",
  // ... other params
});

// Second append after the first
const secondAppend = await appendToNotion({
  markdown: "# Second section", 
  after: firstAppend.data.lastBlockId,
  // ... other params
});
```

## Rate Limiting & Retries

The append endpoint includes intelligent rate limiting:

* **Automatic chunking**: Large content is split into chunks of 100 blocks
* **Sequential processing**: Chunks are processed in order to maintain content sequence
* **Retry logic**: Automatic retries with exponential backoff for rate limits
* **Respectful timing**: 400ms delay between requests to respect Notion's limits

<Info>
  Each append API call counts as **1 usage** regardless of complexity. The `requestCount` is provided for transparency but does NOT affect your quota.
</Info>

## Notion Setup

Before using the append endpoint, connect your Notion workspace in the [dashboard](https://dashboard.mark2notion.com) via OAuth. Then pass your target page as a URL or page ID in the `pageId` field.

## Error Responses

<Card title="Handle Errors" icon="triangle-exclamation" href="/errors">
  Understand error responses and how to handle them.
</Card>

## Usage Notes

<Info>
  Every append request counts as **exactly 1 API call** in your usage quota, regardless of content size or complexity.
</Info>

<Warning>
  Make sure your Notion integration has access to the target page. The integration must be explicitly shared with the page.
</Warning>

## Idempotency

The append endpoint includes built-in idempotency protection:

* Duplicate requests with the same content and parameters are automatically detected
* Returns the original response for duplicate requests
* Prevents accidental duplicate content on your pages

## Notion API Limits Handling

Mark2Notion provides **comprehensive protection** against all Notion API limits with intelligent handling strategies:

### ✅ **Fully Protected Limits**

<AccordionGroup>
  <Accordion title="Content Size Limits">
    * **Rich Text**: 2000 characters per block (automatic text splitting)
    * **URLs**: 2000 characters (truncated with warnings if exceeded)
    * **Equations**: 1000 characters (truncated with warnings if exceeded)
    * **Email/Phone**: Handled as regular text content (no special limits)
  </Accordion>

  <Accordion title="Block Structure Limits">
    * **Payload Size**: 500KB maximum per request (pre-validation with detailed errors)
    * **Total Blocks**: 1000 blocks maximum (automatic splitting into manageable chunks)
    * **Children Count**: 100 blocks per request (intelligent chunking)
    * **Nesting Depth**: 2 levels maximum (deep nesting preserved via sub-request strategy)
  </Accordion>

  <Accordion title="Rate & Access Limits">
    * **Request Rate**: 3 requests/second (exponential backoff + 400ms delays)
    * **Integration Capabilities**: Proper error handling for access issues
    * **Page Access**: Automatic verification before processing
  </Accordion>
</AccordionGroup>

### 📊 **Advanced Capabilities**

<CardGroup cols={2}>
  <Card title="Deep Nesting Preservation" icon="sitemap">
    Handles unlimited nesting depth (5, 6, 7+ levels) by breaking into sub-requests while maintaining parent-child relationships
  </Card>

  <Card title="Large Document Support" icon="file-lines">
    Automatically splits documents with 1000+ blocks into manageable chunks without losing content order
  </Card>

  <Card title="Content Truncation Warnings" icon="triangle-exclamation">
    Provides clear warnings when URLs or equations are truncated, so you know exactly what was modified
  </Card>

  <Card title="Transparent Pricing" icon="eye">
    Every append request counts as exactly 1 API call, regardless of internal complexity
  </Card>
</CardGroup>

<Info>
  **Perfect Coverage**: Mark2Notion handles 100% of applicable Notion API limits automatically. You can send any markdown content without worrying about hitting Notion's restrictions.
</Info>
