API de Notion: cómo leer páginas y tablas con Postman
1. El concepto
La API de Notion te permite leer y escribir el contenido de tu workspace desde código. A diferencia de otras APIs como la de GitHub, no existe acceso público — todo requiere autenticación, hasta la petición más simple.
Como analista, esto define el punto de partida: antes de hacer cualquier petición, necesitas crear una Integration en Notion y darle acceso explícito a las páginas que quieres consultar.
2. Qué tipo de API es Notion
Notion expone una API REST. El mismo estilo que GitHub: cada URL representa un recurso, el método HTTP define la operación, y las respuestas vienen en JSON.
La URL base de toda la API es https://api.notion.com/v1. A partir de ahí se construyen los endpoints:
https://api.notion.com/v1/search → buscar páginas y databases
https://api.notion.com/v1/blocks/{block_id}/children → leer el contenido de una página
https://api.notion.com/v1/databases/{database_id}/query → consultar una database
Una diferencia importante con GitHub: Notion exige un header adicional en cada petición que no existe en otras APIs:
Notion-Version: 2026-03-11
Este header indica qué versión del contrato de la API quieres usar. Sin él, la petición falla. Es obligatorio siempre.
3. Endpoints disponibles
La API de Notion está organizada por tipo de objeto. Cada objeto tiene sus propios endpoints y métodos:
| Objeto | Endpoint | Método | Qué hace |
|---|---|---|---|
| Search | /search |
POST |
Buscar páginas y databases accesibles |
| Pages | /pages |
POST |
Crear una página nueva |
| Pages | /pages/{id} |
GET |
Leer metadatos de una página |
| Pages | /pages/{id} |
PATCH |
Actualizar propiedades de una página |
| Databases | /databases/{id} |
GET |
Leer estructura de una database |
| Databases | /databases/{id}/query |
POST |
Consultar entradas de una database con filtros |
| Blocks | /blocks/{id}/children |
GET |
Leer el contenido de una página o bloque |
| Blocks | /blocks/{id} |
PATCH |
Editar un bloque existente |
| Blocks | /blocks/{id} |
DELETE |
Archivar un bloque |
| Users | /users/me |
GET |
Ver el usuario autenticado |
| Comments | /comments |
GET |
Leer comentarios |
En esta sesión usamos tres: POST /search, GET /blocks/{page_id}/children y GET /blocks/{table_id}/children.
Por qué algunos endpoints de lectura usan POST
En REST la convención es usar GET para leer. Pero GET transfiere sus parámetros en la URL — lo que funciona bien para identificadores simples, pero se vuelve engorroso con estructuras complejas.
Notion usa POST en los endpoints de lectura que aceptan filtros. Por ejemplo, /databases/{id}/query permite enviar esto en el body:
{
"filter": {
"property": "Estado",
"select": { "equals": "Comprado" }
},
"sorts": [
{ "property": "Precio", "direction": "descending" }
]
}
Ese mismo filtro en la URL sería ilegible e inmanejable. El body de un POST es la forma práctica de enviar parámetros de consulta complejos.
La regla en Notion:
- Lectura simple por ID →
GET - Lectura con filtros opcionales →
POST - Crear →
POST - Editar →
PATCH - Archivar →
DELETE
Headers
Los dos headers obligatorios en cada petición de Notion:
| Header | Valor |
|---|---|
Authorization |
Bearer <tu_token> |
Notion-Version |
2026-03-11 |
En Postman: Authorization se configura en la pestaña Authorization → Bearer Token. Notion-Version va en la pestaña Headers como un par clave-valor.
Una aclaración sobre el nombre: el Integration Token de Notion es un Bearer Token estático — usa el header Authorization: Bearer y se genera manualmente desde la web, sin POST de login. Postman llama al campo "Bearer Token" porque ese es el formato del header. Lo que lo distingue de un Bearer Token dinámico es que es estático: no expira automáticamente y no hay que renovarlo con ninguna petición programática.
Body
Solo en métodos que envían datos: POST, PATCH. En el caso de /search con body vacío {}, la API devuelve todo lo accesible para la Integration.
Status code
Los más importantes para trabajar con Notion:
| Código | Significado |
|---|---|
200 OK |
Petición exitosa |
400 Bad Request |
Error en el body o parámetros enviados |
401 Unauthorized |
Token inválido o ausente |
403 Forbidden |
El token existe pero no tiene acceso al recurso |
404 Not Found |
El recurso no existe o no fue compartido con la Integration |
429 Too Many Requests |
Rate limit superado (3 peticiones por segundo) |
4. Autenticación — Integration Token
Crear la Integration
Notion no usa Personal Access Tokens como GitHub. Usa Integrations — aplicaciones registradas en tu workspace que reciben un token propio.
- Ir a
https://www.notion.so/my-integrations - Clic en New integration
- Asignar un nombre descriptivo (ej:
postman-prueba-api) - Seleccionar el workspace donde va a operar
- Copiar el Internal Integration Token — solo se muestra una vez completo, aunque después es visible en la configuración
Capabilities — los scopes de Notion
Al crear o configurar la Integration, Notion pide definir qué puede hacer. Son el equivalente a los scopes de GitHub, pero presentados como checkboxes visuales:
| Grupo | Permiso | Qué permite |
|---|---|---|
| Content | Read content | Ver páginas y entradas de databases |
| Content | Update content | Editar páginas y entradas existentes |
| Content | Insert content | Crear páginas y entradas nuevas |
| Comments | Read comments | Ver comentarios en páginas y bloques |
| Comments | Insert comments | Agregar comentarios |
| Users | No user information | Sin acceso a datos de usuario |
| Users | Read user info without email | Perfiles básicos, sin email |
| Users | Read user info with email | Perfiles completos con email |
Regla práctica: pedir solo lo necesario. Para leer datos como analista: solo Read content.
Dar acceso a páginas
Aquí está la diferencia más importante con GitHub: el token existe, pero no puede leer nada hasta que explícitamente le des acceso a páginas.
Desde la configuración de la Integration, en la sección Content access, puedes seleccionar las páginas padre de tu workspace. La Integration tendrá acceso a esas páginas y todo su contenido anidado.
5. Tu workspace desde la API — /search
Con el token configurado en Postman, la primera petición útil es /search. Devuelve todas las páginas y databases que la Integration puede ver.
- Método:
POST - URL:
https://api.notion.com/v1/search - Body:
{}
La respuesta:
{
"object": "list",
"results": [ ... ],
"next_cursor": null,
"has_more": false,
"type": "page_or_data_source"
}
La estructura raíz tiene tres campos importantes para un analista:
| Campo | Qué indica |
|---|---|
results |
Array con las páginas y databases encontrados |
next_cursor |
Si hay más resultados, este valor se pasa en la siguiente petición para continuar |
has_more |
true si existen más resultados que no caben en esta respuesta |
Esto es la paginación por cursor — distinta a la de GitHub, que usaba el header Link en la respuesta. Notion devuelve la información de paginación dentro del JSON.
Dentro de cada página devuelta:
{
"object": "page",
"id": "295477bc-d0eb-80c3-...",
"created_time": "2025-10-23T01:56:00.000Z",
"last_edited_time": "2026-05-17T02:41:00.000Z",
"parent": { "type": "workspace", "workspace": true },
"in_trash": false,
"is_archived": false,
"is_locked": false,
"properties": {
"title": { "title": [{ "plain_text": "Compras" }] }
}
}
Campos útiles:
| Campo | Qué contiene |
|---|---|
id |
El identificador único de la página — se usa en peticiones siguientes |
created_time / last_edited_time |
Fechas en formato ISO 8601 |
parent.workspace: true |
La página está en el nivel raíz del workspace |
in_trash / is_archived |
Estado de la página |
properties.title |
El título de la página |
6. El contenido de una página — /blocks/{id}/children
/search solo devuelve metadatos. Para leer el contenido real de una página — sus párrafos, tablas, imágenes — se usa un endpoint diferente:
- Método:
GET - URL:
https://api.notion.com/v1/blocks/{page_id}/children
Donde {page_id} es el id obtenido en la respuesta de /search.
La respuesta es otra lista, pero ahora de bloques:
{
"object": "list",
"results": [
{ "object": "block", "type": "paragraph", ... },
{ "object": "block", "type": "table", ... },
{ "object": "block", "type": "divider", ... }
]
}
7. El modelo de bloques — la diferencia estructural de Notion
Este es el concepto más importante de la API de Notion y el que más diferencia tiene con GitHub.
En Notion, todo el contenido es un bloque. Un párrafo es un bloque. Una tabla es un bloque. Un separador, una imagen, un bloque de código — todos son bloques. Y cada bloque tiene la misma estructura base:
{
"object": "block",
"id": "...",
"type": "<tipo>",
"<tipo>": { ... } ← el contenido real va aquí, bajo la clave del mismo nombre
}
Tipos de bloque y su contenido
type |
Qué es en Notion | Dónde está el contenido |
|---|---|---|
paragraph |
Párrafo de texto | paragraph.rich_text[] |
heading_1 / heading_2 / heading_3 |
Títulos | heading_1.rich_text[] |
table |
Tabla | Solo metadatos — las filas son hijos |
table_row |
Fila de una tabla | table_row.cells[][] |
divider |
Línea horizontal --- |
divider: {} — sin contenido |
code |
Bloque de código | code.rich_text[] + code.language |
image |
Imagen | image.external.url o image.file.url |
bulleted_list_item |
Ítem de lista | bulleted_list_item.rich_text[] |
El rich_text — cómo Notion representa texto
El texto nunca es solo un string. Siempre es un array de objetos, aunque el párrafo sea texto plano sin formato:
"rich_text": [
{
"plain_text": "Para guardar fotos",
"annotations": {
"bold": false,
"italic": false,
"strikethrough": false,
"color": "default"
}
}
]
Esto permite que un solo párrafo mezcle texto normal, negrita, itálica y color en segmentos distintos — cada segmento es un elemento del array. Para extraer el texto en Python, hay que iterar sobre el array y concatenar los plain_text.
Un párrafo vacío (línea en blanco en Notion) devuelve "rich_text": [].
Tablas y recursividad — el caso más importante
Una tabla no devuelve sus filas directamente. Solo devuelve metadatos:
{
"type": "table",
"has_children": true,
"table": {
"table_width": 4,
"has_column_header": false,
"has_row_header": false
}
}
El campo has_children: true indica que hay contenido anidado. Para leerlo, se necesita otra petición usando el ID de la tabla:
GET https://api.notion.com/v1/blocks/{table_id}/children
Esa petición devuelve los table_row, donde cada fila tiene un array cells — uno por columna:
"table_row": {
"cells": [
[{ "plain_text": "Adaptador HDMI" }],
[{ "plain_text": "Para conectar el Mac a una pantalla" }],
[{ "plain_text": "1150" }],
[{ "plain_text": "Comprado" }]
]
}
Las celdas vacías son [].
El árbol de peticiones
Leer una página completa con tablas requiere tres niveles de petición encadenada:
POST /search
→ GET /blocks/{page_id}/children → obtienes los bloques de la página
→ GET /blocks/{table_id}/children → obtienes las filas de cada tabla
Esto es muy distinto a GitHub, donde una sola petición GET devolvía todos los datos del recurso. En Notion, el contenido es un árbol — y para recorrerlo hay que seguir los has_children: true con peticiones adicionales.
8. Mi conclusión
La API de Notion tiene la misma base que cualquier API REST — métodos HTTP, headers, JSON — pero introduce dos conceptos que no existen en otras APIs:
- El header
Notion-Version: obligatorio en cada petición. Sin él, la API rechaza la llamada. - El modelo de bloques: el contenido no es plano. Es un árbol. Leer una página compleja puede requerir múltiples peticiones encadenadas siguiendo los
has_children: true.
Para un analista, el flujo básico ya es útil: /search para descubrir qué páginas están disponibles, /blocks/{id}/children para leer el contenido. El siguiente paso natural es trabajar con databases — el objeto más potente de Notion para datos estructurados, con su propio endpoint de consulta con filtros y ordenamiento.
9. Recursos
- Notion API — documentación oficial
- Referencia de tipos de bloque
- Crear una Integration
- Versionado de la API
10. Pendientes
(sección interna — se elimina antes de publicar)
- [ ] Grabar video siguiendo los pasos documentados
- [ ] Revisar que todos los endpoints siguen activos antes de publicar
- [ ] Post siguiente: databases — POST /databases/{id}/query con filtros y ordenamiento
- [ ] Post siguiente: páginas dentro de páginas — recursividad real y cómo manejarla en Python
- [ ] Agregar sección de Python — extraer tabla de Notion a Excel con requests + pandas