Cada red social cuenta los caracteres en una unidad distinta y tu .length no da una
Un equipo que publica en trece redes levantó los límites reales de cada API: la mayoría mide UTF-16, Threads mide grafemas y Bluesky exige pasar dos cuentas a la vez.

La cadena del emoji de familia mide 11 unidades UTF-16 en JavaScript, 7 puntos de código, 1 grafema y 25 bytes en el cable. Cada red social elige una de esas cifras para decidir si tu publicación entra o no. Un equipo que montó una API de publicación contra trece redes tomó los números de las APIs reales, no de la documentación, y el resultado es que .length acierta lo suficiente como para que dejes de cuestionarlo, hasta que topas con las dos redes donde falla.
Tres unidades, y los puntos de código no son ninguna
La mayoría de las redes cuenta unidades UTF-16, que es justo lo que devuelve .length: Facebook 63.206, la descripción de YouTube 5.000, WhatsApp y Telegram 4.096, Slack 4.000, LinkedIn 3.000, Instagram y TikTok 2.200, Discord 2.000, X 280. Threads se queda en 500 grafemas y Bluesky maneja dos límites a la vez, 300 grafemas y 3.000 bytes. Los puntos de código no sirven para nada en este terreno: repartir una cadena con el operador de propagación da 7 en el emoji de familia, no 1.
Hay un detalle que se escapa si te fías de la interfaz. La app de TikTok deja teclear 4.000 caracteres, pero su API de publicación rechaza a partir de 2.200. El número que manda es el del endpoint que llamas de verdad, nunca el que enseña la interfaz de la red.
Bluesky cuenta dos veces y Threads hubo que medirlo
Bluesky es la única donde conviven dos límites simultáneos: 300 grafemas y 3.000 bytes, y hay que pasar los dos. .length falla en ambos, y en direcciones opuestas. Con 121 emojis de familia cuenta 1.331 y los rechaza, cuando en realidad son 121 grafemas y caben de sobra. Pero esos mismos 121 emojis pesan 3.025 bytes, así que se salen del techo de bytes. Un texto largo en japonés hace lo mismo, tres bytes por carácter. La validación tiene que ser doble comprobación, y el aviso al usuario debe decir cuál de los dos límites rompió: un "demasiado largo" junto a un contador que marca 180 caracteres es un error que no se puede resolver.
Threads dice "500 caracteres" y añade que los emoji cuentan como bytes UTF-8. Dos lecturas plausibles que se contradicen, así que lanzaron dos sondas contra una cuenta real. 400 emojis sonrientes (400 grafemas, 800 unidades UTF-16, 1.600 bytes) publican. 400 eces con acento publican también. Las dos superan 500 en alguna de las otras unidades, así que ni UTF-16 ni bytes pueden ser la regla: cuenta grafemas. Antes medían con .length, lo que rechazaba publicaciones de 300 emoji que la red habría aceptado sin problema.
Telegram cambia el límite y Slack no falla, que es peor
En Telegram el mismo campo tiene dos números. 4.096 sin adjunto, porque el texto sale como mensaje suelto. 1.024 en cuanto hay una foto o un vídeo, porque entonces el texto es el pie de sendPhoto. En una herramienta de publicación eso no es un caso raro, es el caso normal: alguien escribe 2.000 caracteres, arrastra una imagen y la publicación que era válida hace un segundo revienta con un 400.
Slack no devuelve error, y eso es peor. Su límite son 4.000 caracteres para chat.postMessage y, si te pasas, trunca el mensaje o lo parte en varios. Tu panel muestra una publicación, el canal tiene dos y no ha fallado nada. Encima el texto hay que escaparlo, porque &, < y > abren entidades propias de Slack, y escapar cambia la longitud: un & se convierte en &, cinco caracteres donde había uno. Un corte plano en el carácter 4.000 publicó una vez un &am bien visible.
El error caro no es simétrico. Una publicación que rechaza la API devuelve un mensaje legible; una que rechaza tu propio formulario deja al usuario mirando un contador que dice 300 de 500 y un aviso de que se pasa. Por eso conviene tener una función por unidad, grafemas con Intl.Segmenter y bytes con TextEncoder o Buffer.byteLength, y consultar la que use el endpoint concreto antes de dar por buena una publicación.
