Saltar al contenido principal
SocialCutter

IA y agentes

Guarda en Notion las medidas generadas con SocialCutter

Genera cada formato con SocialCutter y llévalo a Notion con una URL externa o la File Upload API: límites de alojamiento, versiones y snippet en Python.

  • Notion
  • File Upload API
  • base de datos
  • imagen externa
  • Python
  • SocialCutter
  • automatización

La frontera: SocialCutter genera, Notion guarda

SocialCutter recibe una imagen, la recorta de forma centrada a las medidas exactas de cada destino y devuelve una URL por salida. El recorte es centrado y sin detección de sujeto: no hay ningún análisis de contenido que decida qué parte sobra. Tampoco publica ni escribe en Notion.

Así que la integración tiene dos mitades bien separadas: generar los ficheros con la medida correcta y decidir dónde los guardas. Esta guía cubre la segunda con la API de Notion.

Por qué pre-generar antes de llevarlo a Notion

Notion escala las imágenes para que encajen en la caja donde las pones; no las recorta a una proporción concreta. Si subes el mismo maestro a un bloque vertical, a una portada ancha y a la miniatura de una tabla, el resultado depende por completo del contenedor.

Hueco en NotionDestino SocialCutterMedida
Portada de página o cabecera del postfacebook post1200x630 (1.91:1)
Imagen cuadrada de una galeríainstagram post1080x1080 (1:1)
Bloque vertical, story o reelinstagram story1080x1920 (9:16)
Miniatura apaisada de tabla o tarjetatwitter post1200x675 (16:9)
Cabecera ancha de páginatwitter header1500x500 (3:1)

Pre-generar los cinco son 5 usos y sale en una sola petición, en lugar de rehacer la imagen cada vez que cambia la plantilla de la página.

Las dos vías para que una imagen acabe en Notion

VíaObjetoQué guarda NotionCuándo usarla
URL externaexternalSolo la URL, sin copia del ficheroLa imagen vive en tu CDN o en SocialCutter y no necesita permisos
File Upload APIfile_uploadUna copia en el almacenamiento del workspaceLa imagen tiene que quedar dentro de Notion

Los ficheros que arrastras a mano en la interfaz son de tipo file y también consumen almacenamiento del workspace, que va contra la cuota de tu plan de Notion.

Vía 1: la URL de salida como imagen externa

Es el camino corto: escribes la URL en una propiedad de tipo URL de una base de datos o la usas en un bloque de imagen.

curl -s -X PATCH "https://api.notion.com/v1/blocks/$PAGE_ID/children" \
  -H "Authorization: Bearer $NOTION_TOKEN" \
  -H "Notion-Version: 2022-06-28" \
  -H "Content-Type: application/json" \
  -d "{\"children\":[{\"object\":\"block\",\"type\":\"image\",\"image\":{\"type\":\"external\",\"external\":{\"url\":\"$OUTPUT_URL\"}}}]}"

Ventaja: cero bytes en el almacenamiento de Notion y la misma URL sirve para el CMS, la red social o el correo. Coste: si la URL caduca o se retira el maestro, la imagen desaparece de la página.

Vía 2: la File Upload API

Tres pasos, tal y como los documenta Notion:

  1. POST /v1/file_uploads crea el objeto en estado pending y devuelve un upload_url. Esto permite reservar el hueco antes de tener el fichero en la mano.
  2. Envía el contenido a ese upload_url con Content-Type: multipart/form-data y el fichero en el campo file.
  3. Usa el id del fichero subido en un bloque image de tipo file_upload o en una propiedad de ficheros.

El modo single_part (el de por defecto) admite hasta 20 MB. Por encima hay que usar multi_part, que trocea en partes de 5 a 20 MB y llega hasta 5 GB en workspaces de pago. Como SocialCutter acepta maestros de 5 MB como máximo, las salidas caben siempre en la vía simple. El fichero hay que adjuntarlo dentro de la hora siguiente a crearlo o caduca.

Snippet en Python

import os
import requests

SC = "https://api.socialcutter.theboomer.dev"
NOTION = "https://api.notion.com/v1"
NOTION_VERSION = os.environ.get("NOTION_VERSION", "2022-06-28")

sc = requests.Session()
sc.headers["X-API-Key"] = os.environ["SOCIALCUTTER_API_KEY"]

nt = requests.Session()
nt.headers.update({
    "Authorization": f"Bearer {os.environ['NOTION_TOKEN']}",
    "Notion-Version": NOTION_VERSION,
})

# 1. Una peticion a SocialCutter, una URL por destino
resp = sc.post(f"{SC}/api/v1/images/process", json={
    "source": {"type": "url", "value": "https://example.com/maestro.jpg"},
    "destinations": [
        {"platform": "instagram", "format": "post"},
        {"platform": "instagram", "format": "story"},
        {"platform": "facebook", "format": "post"},
    ],
}, timeout=60)
resp.raise_for_status()
salidas = resp.json()["outputs"]

# 2. Una pagina en una base de datos con una propiedad URL por destino
props = {"Nombre": {"title": [{"text": {"content": "Campana de septiembre"}}]}}
for out in salidas:
    props[f"{out['platform']}-{out['format']}"] = {"url": out["url"]}

pagina = nt.post(f"{NOTION}/pages", json={
    "parent": {"database_id": os.environ["NOTION_DB_ID"]},
    "properties": props,
}, timeout=30)
pagina.raise_for_status()
print(pagina.json()["id"])

# 3. Alternativa: subir el fichero y adjuntarlo como imagen de la pagina
def subir_a_notion(url, nombre, content_type):
    up = nt.post(f"{NOTION}/file_uploads", json={
        "mode": "single_part", "filename": nombre, "content_type": content_type,
    }, timeout=30)
    up.raise_for_status()
    fichero = sc.get(url, timeout=60).content
    envio = requests.post(up.json()["upload_url"], headers={
        "Authorization": nt.headers["Authorization"],
        "Notion-Version": NOTION_VERSION,
    }, files={"file": (nombre, fichero, content_type)}, timeout=120)
    envio.raise_for_status()
    return up.json()["id"]

primer = salidas[0]
fid = subir_a_notion(primer["url"], f"{primer['platform']}-{primer['format']}.webp", "image/webp")

nt.patch(f"{NOTION}/blocks/{pagina.json()['id']}/children", json={
    "children": [{"object": "block", "type": "image",
                  "image": {"type": "file_upload", "file_upload": {"id": fid}}}],
}, timeout=30).raise_for_status()

Los nombres de las propiedades dependen de tu base de datos: la propiedad de título y las propiedades de tipo URL hay que crearlas antes, o usar los identificadores de campo en lugar de los nombres.

Los límites de alojamiento de Notion

  • Notion no es un CDN. Las imágenes que subes cuentan en el almacenamiento de tu workspace, que va por plan.
  • Una imagen external no se copia: Notion guarda la referencia y la carga desde fuera cada vez.
  • Las URLs de los ficheros alojados por Notion son temporales (una hora). No las guardes en caché ni las incrustes en otra web.
  • Las URLs de petición admiten hasta 2000 caracteres, de modo que una URL de salida con firma larga entra sin problema.
  • Si la imagen tiene que sobrevivir a la retirada del maestro, sube el fichero con la File Upload API en lugar de enlazarlo.

Las versiones de la API de Notion

El encabezado Notion-Version es obligatorio en todas las llamadas y es lo que fija el contrato. La API evoluciona: los ejemplos que sirve la documentación usan 2022-06-28, y las versiones más recientes introducen el data source como destino al crear una página dentro de una base de datos, en lugar del database_id. Si fijas una versión y la subes sin revisar el cuerpo de la petición, la creación de páginas es lo primero que rompe.

Fija la versión en una variable de entorno, como en el snippet, y consulta la referencia antes de migrar: https://developers.notion.com/reference/file-upload.

Los límites de peticiones son de Notion, no de SocialCutter: 180 peticiones por minuto por conexión en los planes estándar (una media de 3 por segundo) y 600 por minuto en Business y Enterprise. Al pasarte responde 429 con el código rate_limited, así que respeta el valor de Retry-After en lugar de reintentar en bucle.

Coste

  • 1 uso por destino (plataforma y formato) por petición a SocialCutter. Los destinos repetidos no se cobran dos veces y los procesamientos fallidos se devuelven.
  • Planes: 0 EUR (3 usos/día), 3 EUR (10/día), 9 EUR (30/día) y 29 EUR (100/día), todos con API y MCP.
  • La API de Notion no se factura por llamada: cuenta contra los límites de tu plan de Notion.

Errores típicos

CódigoOrigenSignificado
400SocialCutterPayload inválido: plataforma o formato desconocido
401SocialCutterFalta la cabecera X-API-Key o la clave no vale
413SocialCutterEl maestro supera 5 MB
429SocialCutterCuota agotada: consulta GET /api/v1/wallet
400 validation_errorNotionCuerpo mal formado, versión incompatible o un parámetro fuera de límite
401 unauthorizedNotionEl token de la integración no es válido
403 restricted_resourceNotionLa integración no tiene acceso a esa página o base de datos: hay que compartirla
404 object_not_foundNotionEl identificador de página, bloque o base de datos no existe
429 rate_limitedNotionDemasiadas peticiones: espera lo que marque Retry-After

Siguientes pasos

Preguntas frecuentes

¿SocialCutter sube las imágenes a Notion?

No. Genera los ficheros con las medidas exactas de cada destino y devuelve una URL por salida. Subir a Notion es del llamante: con una URL externa o con la File Upload API.

¿Notion copia el fichero si uso una URL?

No. Un fichero de tipo external es solo la referencia a esa URL y Notion no descarga nada, así que si la URL deja de responder la imagen desaparece de la página aunque el bloque siga ahí.

¿Cuánto duran las URLs de los ficheros que aloja Notion?

Son temporales: la referencia oficial indica una validez de una hora y recomienda no cachearlas. Vuelve a pedir el objeto del fichero para refrescar la URL en lugar de tratarla como un CDN.

¿Qué valor pongo en Notion-Version?

Uno fijo, el que corresponda a tu integración, y lo cambias cuando migres. El encabezado es obligatorio y la API cambia entre versiones: en las recientes la pertenencia a una base de datos pasa por el data source.

¿Cuánto cuesta preparar los formatos de una página?

1 uso por destino, es decir por cada combinación de plataforma y formato. Los planes son 0, 3, 9 y 29 EUR al mes e incluyen acceso a la API y al servidor MCP.