# Fetching pages

> Get blocks and page metadata from the Notion API.

`notionic/fetch` turns the Notion API's one-level-at-a-time blocks into the tree `NotionRenderer` needs.

## fetchPage

Returns the page's metadata and every block of it:

```typescript
const { page, blocks } = await fetchPage(notion, pageId)

page.title          // "Fetching pages"
page.icon           // { type: "emoji", emoji: "📥" } or null
page.cover          // the cover image, or null
page.lastEditedTime // ISO timestamp
page.properties     // database properties, as the API returns them
```

It throws if the page doesn't exist or isn't shared with your integration.

## fetchBlocks

Returns only the blocks, for a page or any block:

```typescript
const blocks = await fetchBlocks(notion, pageId, { maxDepth: 5, concurrency: 3 })
```

- **Nesting.** Nested blocks are attached as `children`, up to `maxDepth` levels below the top (default 5).
- **Rate limits.** At most `concurrency` requests run at once per level (default 3).
- **Child pages and databases** are separate documents, so their content isn't fetched. Each child page is retrieved for its icon, attached as `linkedPage`.
- **Linked pages.** For each `link to page` block, the linked page's title and icon are attached as `linkedPage`, so the link has a label. Pages the integration can't read are skipped.
- **Bookmarks, embeds and link previews.** For each one, the page behind it is fetched and its title, description, image and favicon are attached as `bookmarkPreview`, so it renders as a card. A page that's slow (over 5 seconds), unreachable or untitled renders as a one-line card with the URL, and private network addresses are never fetched. Pass `bookmarkPreviews: false` to make no requests outside Notion.

## Caching

notionic doesn't cache. Use your framework's cache: in Next.js, `export const revalidate` or `"use cache"`.

## Uploaded files

Notion's URLs for uploaded images and files expire after an hour, which is shorter than most pages are cached. Pass `transformFileUrl` to point them at a route of yours that fetches a fresh URL:

```typescript
<NotionRenderer
  blocks={blocks}
  transformFileUrl={({ blockId }) => `/api/notion/file/${blockId}`}
/>
```

The route retrieves the block with `notion.blocks.retrieve` and redirects to its file's current URL. Cache the redirect for less than the time left before the URL expires. External image URLs are never rewritten.
