# Source: https://developers.notion.com/guides/data-apis/working-with-page-content.md

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

# Working with page content

> Learn about page content and how to add or retrieve it with the Notion API.

## Overview

[Pages](https://www.notion.so/help/category/write-edit-and-customize) are where users write everything from quick notes, to shared documents, to curated landing pages in Notion. Integrations can help users turn Notion into the single source of truth by syndicating content or help users gather, connect, and visualize content inside Notion.

In this guide, you'll learn about how the building blocks of page content are represented in the API and what you can do with them. By the end, you'll be able to create new pages with content, read content from other pages, and add blocks to existing pages.

### Page content versus properties

In general, **page properties** are best for capturing structured information such as a due date, a category, or a relationship to another page. **Page content** is best for looser structures or free form content. Page content is where users compose their thoughts or tell a story. Page properties are where users capture data and build systems. Your integration should aim to use each in the way users expect.

<Frame caption="Visualizing page properties versus page content">
  <img src="https://mintcdn.com/notion-demo/LHm9qfrJYJOPRxs6/images/docs/369b6a5-page-properties-and-content.png?fit=max&auto=format&n=LHm9qfrJYJOPRxs6&q=85&s=0887fcbaa5537be57e38124ee0fa409c" data-og-width="2238" width="2238" data-og-height="1248" height="1248" data-path="images/docs/369b6a5-page-properties-and-content.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/notion-demo/LHm9qfrJYJOPRxs6/images/docs/369b6a5-page-properties-and-content.png?w=280&fit=max&auto=format&n=LHm9qfrJYJOPRxs6&q=85&s=6de7cd31f3ca9ae96fe3195cc0264e3d 280w, https://mintcdn.com/notion-demo/LHm9qfrJYJOPRxs6/images/docs/369b6a5-page-properties-and-content.png?w=560&fit=max&auto=format&n=LHm9qfrJYJOPRxs6&q=85&s=93a7971408d3c1ec8ce49addce6682b9 560w, https://mintcdn.com/notion-demo/LHm9qfrJYJOPRxs6/images/docs/369b6a5-page-properties-and-content.png?w=840&fit=max&auto=format&n=LHm9qfrJYJOPRxs6&q=85&s=eaef9403390591aac412d18aff1b782c 840w, https://mintcdn.com/notion-demo/LHm9qfrJYJOPRxs6/images/docs/369b6a5-page-properties-and-content.png?w=1100&fit=max&auto=format&n=LHm9qfrJYJOPRxs6&q=85&s=4d8075a5466886863a875fab98ccbe8f 1100w, https://mintcdn.com/notion-demo/LHm9qfrJYJOPRxs6/images/docs/369b6a5-page-properties-and-content.png?w=1650&fit=max&auto=format&n=LHm9qfrJYJOPRxs6&q=85&s=5731c3eba6f407aeeee944e05cbb0c68 1650w, https://mintcdn.com/notion-demo/LHm9qfrJYJOPRxs6/images/docs/369b6a5-page-properties-and-content.png?w=2500&fit=max&auto=format&n=LHm9qfrJYJOPRxs6&q=85&s=d15b19751ffa67b5cac88806cd8e3f17 2500w" />
</Frame>

## Modeling content as blocks

A page's content is represented by a list of [block objects](/reference/block). These blocks are referred to as the page's children. Each block has a type, such as a paragraph, a heading, or an image. Some types of blocks, such as a toggle list, have children of their own.

Let's start with a simple example, a [paragraph block](/reference/block#paragraph):

<CodeGroup>
  ```js JavaScript theme={null}
  {
    "object": "block",
    "id": "380c78c0-e0f5-4565-bdbd-c4ccb079050d",
    "type": "paragraph",
    "created_time": "",
    "last_edited_time": "",
    "has_children": false,

    "paragraph": {
      "text": [/* details omitted */]
    }
  }
  ```
</CodeGroup>

Paragraph blocks include common properties which every block includes: `object`, `type`, `created_time`, `last_edited_time`, and `has_children`. In addition, it contains type-specific information inside the `paragraph` property. Paragraph blocks have a `text` property. Other block types have different type-specific properties.

Now let's look at an example where the block has child blocks: a paragraph followed by an indented [todo block](/reference/block#to-do):

<CodeGroup>
  ```js JavaScript expandable theme={null}
  {
    "object": "block",
    "id": "380c78c0-e0f5-4565-bdbd-c4ccb079050d",
    "type": "paragraph",
    "created_time": "",
    "last_edited_time": "",
    "has_children": true,

    "paragraph": {
      "text": [/* details omitted */],
      "children": [
        {
          "object": "block",
          "id": "6d5b2463-a1c1-4e22-9b3b-49b3fe7ad384",
          "type": "to_do",
          "created_time": "",
          "last_edited_time": "",
          "has_children": false,

          "to_do": {
            "text": [/* details omitted */],
            "checked": false
          }
        }
      ]
    }
  }
  ```
</CodeGroup>

Child blocks are represented as a list of blocks inside the type-specific property. When a block has children, the `has_children` property is `true`. Only some block types, like paragraph blocks, support children.

<Note>
  **Pages are also blocks**

  Pages are a special kind of block, but they have children like many other block types. When [retrieving a list of child blocks](/reference/get-block-children), you can use the page ID as a block ID.

  When a child page appears inside another page, it's represented as a `child_page` block, which does not have children. You should think of this as a reference to the page block.
</Note>

<Warning>
  **Unsupported block types**

  The Notion API currently supports a subset of Notion [block](/reference/block#block-type-objects) types, with support for more coming soon. When an unsupported block type appears in a page, it will have the type `"unsupported"`.
</Warning>

### Rich text

In the previous block examples, the omitted value of the text property is a list of [rich text objects](/reference/rich-text). Rich text objects can describe more than a simple string - the object includes style information, links, mentions, and more.

Let's look at a simple example that just contains the words "Grocery List":

<CodeGroup>
  ```js JavaScript theme={null}
  {
    "type": "text",
    "text": {
      "content": "Grocery List",
      "link": null
    },
    "annotations": {
      "bold": false,
      "italic": false,
      "strikethrough": false,
      "underline": false,
      "code": false,
      "color": "default"
    },
    "plain_text": "Grocery List",
    "href": null
  }
  ```
</CodeGroup>

Rich text objects follow a similar pattern for type-specific configuration. The rich text object above has a type of `"text"`, and it has additional configuration related to that type in the `text` property. Other information that does not depend on the type, such as `annotations`, `plain_text`, and `href`, are at the top level of the rich text object.

Rich text is used both in page content and inside [page property values](/reference/page-property-values).

## Creating a page with content

Pages can be created with child blocks using the [create a page](/reference/post-page) endpoint. This endpoint supports creating a page within another page, or creating a page within a database.

Let's try creating a page within another page with some sample content. We will use all three parameters for this endpoint. The parent parameter is a [page parent](/reference/page#page-parent). We can build this object using an existing page ID:

<CodeGroup>
  ```js JavaScript theme={null}
  {
    "type": "page_id",
    "page_id": "494c87d0-72c4-4cf6-960f-55f8427f7692"
  }
  ```
</CodeGroup>

<Note>
  **Permissions**

  Before an integration can create a page within another page, it needs access to the page parent. To share a page with an integration, click the `•••` menu at the top right of a page, scroll to `Add connections`, and use the search bar to find and select the integration from the dropdown list.
</Note>

<Note>
  **Where can I find my page's ID?**

  Here's a quick procedure to find the page ID for a specific page in Notion:

  <Steps>
    <Step>
      Open the page in Notion.
    </Step>

    <Step>
      Use the Share menu to Copy link.
    </Step>

    <Step>
      Now paste the link in your text editor so you can take a closer look.
    </Step>

    <Step>
      The URL ends in a page ID. It should be a 32 character long string. Format this value by inserting hyphens (-) in the following pattern:

      1. 8-4-4-4-12 (each number is the length of characters between the hyphens).
      2. Example: `1429989fe8ac4effbc8f57f56486db54` becomes `1429989f-e8ac-4eff-bc8f-57f56486db54`.
      3. This value is your page ID.

      While this procedure is helpful to try the API, **you shouldn't ask users to do this for your integration**. It's more common for an integration to determine a page ID by calling the [search API](/reference/post-search).
    </Step>
  </Steps>
</Note>

The `properties` parameter is an object which describes the page properties. Let's use a simple example with only the required `title` property:

<CodeGroup>
  ```js JavaScript theme={null}
  {
    "Name": {
      "type": "title",
      "title": [{ "type": "text", "text": { "content": "A note from your pals at Notion" } }]
    }
  }
  ```
</CodeGroup>

<Note>
  **Page properties within a database**

  Pages within a database parent require properties to conform to the database's schema. Follow the [working with databases guide](/guides/data-apis/working-with-databases) for an in-depth discussion with examples.
</Note>

The children parameter is a list of [block objects]() which describe the page content. Let's use some sample content:

<CodeGroup>
  ```js JavaScript theme={null}
  [
    {
      "object": "block",
      "type": "paragraph",
      "paragraph": {
        "rich_text": [{ "type": "text", "text": { "content": "You made this page using the Notion API. Pretty cool, huh? We hope you enjoy building with us." } }]
      }
    }
  ]
  ```
</CodeGroup>

<Note>
  **Size limits**

  When creating new blocks, keep in mind that the Notion API has [size limits](/reference/errors#size-limits) for the content.
</Note>

Using all three of the parameters, we create a page by sending a request to [the endpoint](/reference/post-page).

<CodeGroup>
  ```bash cURL expandable theme={null}
  curl -X POST https://api.notion.com/v1/pages \
    -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
    -H "Content-Type: application/json" \
    -H "Notion-Version: 2022-06-28" \
    --data '{
  	"parent": { "page_id": "494c87d0-72c4-4cf6-960f-55f8427f7692" },
  	"properties": {
  		"title": {
        "title": [{ "type": "text", "text": { "content": "A note from your pals at Notion" } }]
  		}
  	},
  	"children": [
      {
        "object": "block",
        "type": "paragraph",
        "paragraph": {
          "rich_text": [{ "type": "text", "text": { "content": "You made this page using the Notion API. Pretty cool, huh? We hope you enjoy building with us." } }]
        }
      }
    ]
  }'
  ```

  ```javascript JavaScript expandable theme={null}
  const { Client } = require('@notionhq/client');

  const notion = new Client({ auth: process.env.NOTION_API_KEY });

  (async () => {
    const response = await notion.pages.create({
      parent: {
        page_id: '494c87d072c44cf6960f55f8427f7692',
      },
      properties: {
        title: {
          type: 'title',
          title: [
            {
              type: 'text',
              text: {
                content: 'A note from your pals at Notion',
              },
            },
          ],
        },
      },
      children: [
        {
          object: 'block',
          type: 'paragraph',
          paragraph: {
            text: [
              {
                type: 'text',
                text: {
                  content: 'You made this page using the Notion API. Pretty cool, huh? We hope you enjoy building with us.',
                },
              },
            ],
          },
        },
      ],
    });
    console.log(response);
  })();
  ```
</CodeGroup>

Once the page is added, you'll receive a response containing the new [page object](/reference/page). Take a look inside Notion and view your new page.

## Reading blocks from a page

Page content can be read from a page using the [retrieve block children](/reference/get-block-children) endpoint. This endpoint returns a list of children for any block which supports children. While pages are a common starting point for reading block children, you can retrieve the block children of other kinds of blocks, too.

The `block_id` parameter is the ID of any existing block. If you're following from the example above, the response contained a page ID. Let's use that page ID to read the sample content from the page. We'll use `"16d8004e-5f6a-42a6-9811-51c22ddada12"` as the block ID.

Using this `block_id`, we retrieve the block children by sending a request to [the endpoint](/reference/get-block-children).

<CodeGroup>
  ```curl cURL theme={null}
  curl https://api.notion.com/v1/blocks/16d8004e-5f6a-42a6-9811-51c22ddada12/children?page_size=100 \
  -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
  -H "Notion-Version: 2025-09-03"
  ```

  ```javascript JavaScript theme={null}
  const { Client } = require('@notionhq/client');

  const notion = new Client({ auth: process.env.NOTION_API_KEY });

  (async () => {
    const blockId = '16d8004e5f6a42a6981151c22ddada12';
    const response = await notion.blocks.children.list({
      block_id: blockId,
    });
    console.log(response);
  })();
  ```
</CodeGroup>

You'll receive a response that contains a list of block objects.

<CodeGroup>
  ```js JavaScript theme={null}
  {
    "object": "list",
    "results": [
      {
        "object": "block",
        /* details omitted */
      }
    ],
    "has_more": false,
    "next_cursor": null
  }
  ```
</CodeGroup>

This is a paginated response. Paginated responses are used throughout the Notion API when returning a potentially large list of objects. The maximum number of results in one paginated response is 100. The [pagination reference](/reference/pagination) explains how to use the "start\_cursor" and "page\_size" parameters to get more than 100 results.

In this case, the individual child blocks we requested are in the "results" array.

### Reading nested blocks

What happens when the results contain a block that has its own children? In this case, the response will not contain those children, but the `has_children` property will be `true`. If your integration needs a complete representation of a page's (or any block's) content, it should search the results for blocks with `has_children` set to `true`, and recursively call the [retrieve block children](/reference/get-block-children) endpoint.

Reading large pages may take some time. We recommend using asynchronous operations in your architecture, such as a job queue. You will also need to be mindful of [rate limits](/reference/errors#rate-limits) to appropriately slow down making new requests after the limit is met.

## Appending blocks to a page

Integrations can add more content to a page by using the [append block children](/reference/patch-block-children) endpoint. Let's try to add another block to the page we created in the example above. This endpoint requires two parameters: `block_id` and `children`.

The `block_id` parameter is the ID of any existing block. If you're following from the example above, let's use the same page ID as the block ID: `"16d8004e-5f6a-42a6-9811-51c22ddada12"`.

The `children` parameter is a list of [block objects](/reference/block) which describe the content we want to append. Let's use some more sample content:

<CodeGroup>
  ```js JavaScript theme={null}
  [
    {
      "object": "block",
      "type": "paragraph",
      "paragraph": {
        "text": [{ "type": "text", "text": { "content": "– Notion API Team", "link": { "type": "url", "url": "https://twitter.com/NotionAPI" } } }]
      }
    }
  ]
  ```
</CodeGroup>

Using both parameters, we append blocks by sending a request to [the endpoint](/reference/patch-block-children).

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://api.notion.com/v1/blocks/16d8004e-5f6a-42a6-9811-51c22ddada12/children \
    -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
    -H "Content-Type: application/json" \
    -H "Notion-Version: 2022-06-28" \
    --data '{
    "children": [
      {
        "object": "block",
        "type": "paragraph",
        "paragraph": {
          "text": [{ "type": "text", "text": { "content": "– Notion API Team", "link": { "type": "url", "url": "https://twitter.com/NotionAPI" } } }]
        }
      }
    ]
  }'
  ```

  ```javascript JavaScript theme={null}
  const { Client } = require('@notionhq/client');

  const notion = new Client({ auth: process.env.NOTION_API_KEY });

  (async () => {
    const blockId = '16d8004e5f6a42a6981151c22ddada12';
    const response = await notion.blocks.children.append({
      block_id: blockId,
      children: [
        {
          object: 'block',
          type: 'paragraph',
          paragraph: {
            text: [
              {
                type: 'text',
                text: {
                  content: '– Notion API Team',
                  link: {
                    type: 'url',
                    url: 'https://twitter.com/NotionAPI',
                  },
                },
              },
            ],
          },
        },
      ],
    });
    console.log(response);
  })();
  ```
</CodeGroup>

You'll receive a response that contains the updated block. The response does not contain the child blocks, but it will show `has_children` set to `true`.

By default, new block children are appended at the end of the parent block. To place the block after a specific child block and not at the end, use the `after` body parameter. `after` should be set to the ID of the existing child block you are appending the new block after. For example, if the parent `block_id` is for a block that contains a bulleted list, you can set the `after` parameter to the block ID of the list item you want the new block children to be appended after.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://api.notion.com/v1/blocks/16d8004e-5f6a-42a6-9811-51c22ddada12/children \
    -H 'Authorization: Bearer '"$NOTION_API_KEY"'' \
    -H "Content-Type: application/json" \
    -H "Notion-Version: 2022-06-28" \
    --data '{
      "children": [
      {
        "object": "block",
        "type": "paragraph",
        "paragraph": {
          "text": [{ "type": "text", "text": { "content": "– Notion API Team", "link": { "type": "url", "url": "https://twitter.com/NotionAPI" } } }]
        }
      }
    ], after: "<block_id_to_append_after>"
  }'
  ```
</CodeGroup>

## Conclusion

Nearly everything users see inside Notion is represented as blocks. Now that you've understood how your integration can build pages with blocks, read blocks, and add blocks to pages - you've unlocked most of the surface area in Notion. You integration can engage users where they do everything from creative writing, to building documentation, and more.

### Next steps

* This guide explains working with page content. Take a look at [working with database properties](/guides/data-apis/working-with-databases#database-properties).
* Explore the [block object](/reference/block) to see other types of blocks you can create.
* Learn more about the various kinds of [rich text objects](/reference/rich-text).
* Learn more about [pagination](/reference/intro#pagination).