Diseño de API para la batería móvil: reducir la chattiness
Diseño de API para la vida de batería en móviles: aprende batching, cabeceras de cache y recorte de payloads para reducir despertares de radio, acelerar pantallas y disminuir consumo.

Por qué las APIs “chatter” agotan la batería en móviles
Una API “charlatana” hace que una app dispare muchas peticiones pequeñas para construir una sola pantalla. Cada petición parece barata en papel, pero en un teléfono se suman rápido.
El mayor golpe a la batería suele venir de la radio de red. Los chips de celular y Wi‑Fi pasan a estados de alta potencia para enviar y recibir datos. Cuando una app lanza muchas requests cercanas en el tiempo, la radio se despierta constantemente y permanece activa más tiempo. Incluso si cada respuesta es diminuta, los despertares repetidos cuestan energía real.
También hay trabajo de CPU. Cada request significa construir cabeceras, hacer trabajo TLS, parsear JSON, actualizar caches y ejecutar código de la app para fusionar resultados. Si las conexiones se caen, el trabajo de configuración se repite.
La chattiness también empobrece la experiencia: en lugar de una carga predecible, la pantalla espera una cadena de llamadas. Obtienes spinners más largos, renders parciales que saltan y más timeouts cuando la red es débil. Las actualizaciones en segundo plano empeoran igual: más reintentos, más despertares, más consumo.
Una forma práctica de pensar en el diseño de API para la vida de batería móvil es simple: mostrar la misma UI con menos viajes de ida y vuelta, menos bytes y menos trabajo en background.
Puedes medir el éxito con algunos cambios concretos:
- Menos llamadas API por carga de pantalla
- Menos bytes descargados por pantalla
- Mejor mediana y peor caso de tiempo hasta interactivo en celular
- Menos fetches y reintentos en background para el mismo resultado
Cuando eso mejora, la capacidad de respuesta y la batería suelen mejorar juntas.
Qué medir antes de cambiar nada
Antes de hacer batching o ajustar el cache, obtén una imagen clara de lo que hace hoy la app. Las victorias rápidas suelen venir de arreglar unos pocos offenders repetidos, pero solo los encuentras si mides pantallas y flujos reales (no solo una ejecución de camino feliz).
Para un puñado de pantallas de alto tráfico, registra lo básico: cuántas requests ocurren por carga, qué endpoints se llaman, bytes enviados y recibidos (cabeceras más cuerpo), tasas de reintento y timeout, y cuánto tarda hasta que la UI se siente usable. Si puedes, separa tasas de error por tipo de red (Wi‑Fi vs celular). Esas divisiones a menudo revelan problemas que pasarías por alto en una conexión estable.
Separa tráfico en primer plano del de background. Una pantalla puede parecer tranquila, pero el teléfono puede estar ocupado con refresh en segundo plano, sincronizaciones por push, subidas de analytics o llamadas de prefetch “por si acaso”. Mide por separado para no optimizar lo equivocado.
Los hotspots tienden a agruparse en unos pocos momentos: lanzamiento de la app, pantalla principal/feed, pantallas de detalle que generan muchas llamadas y pull‑to‑refresh. Elige una de estas y mídela end‑to‑end.
Define un presupuesto base para que el equipo acuerde qué significa “bueno”. Por ejemplo: “Una carga en frío de la pantalla de seguimiento de pedido no debería hacer más de 6 requests ni descargar más de 250 KB antes de mostrar el estado.”
Si quieres un par de KPIs simples para empezar, usa (1) requests por carga de pantalla y (2) tasa de reintentos. Reducir reintentos suele ahorrar más batería de lo que imaginas, porque los reintentos repetidos mantienen la radio activa más tiempo.
Paso a paso: reducir requests con batching
Las APIs chatter son fáciles de crear por accidente. Cada widget carga “su” dato, y una pantalla dispara una docena de llamadas. La solución suele ser simple: identifica qué siempre se carga junto y devuélvelo en menos llamadas.
Empieza mapeando una pantalla de alto tráfico (inicio, bandeja de entrada, lista de pedidos). Anota lo que aparece above the fold y traza cada elemento de UI hasta la request que lo alimenta. A menudo encontrarás duplicados (el mismo perfil de usuario solicitado dos veces) y llamadas que siempre viajan juntas (perfil más permisos más conteo de no leídos).
Luego agrupa llamadas que ocurren de forma fiable juntas. Generalmente tienes dos opciones:
- Crear un endpoint específico para esa pantalla (a menudo lo más estable)
- Añadir un endpoint batch que acepte una lista pequeña y predecible de recursos
Mantén los batches basados en la pantalla y acotados para que sean fáciles de cachear, monitorear y depurar.
Algunas reglas evitan que el batching se convierta en un lío. Devuelve solo lo que la pantalla necesita ahora, no objetos completos “por si acaso”. Si algunas piezas son opcionales, decláralo para que la UI pueda renderizar las partes importantes rápido. Diseña también la respuesta para que fallos parciales no obliguen a reintentar todo. Es mucho más barato reintentar solo lo que falló que reenviar todo el batch.
Cabeceras de cache que ahorran batería (no solo ancho de banda)
El cache es una característica de batería. Cada request despierta la radio, mantiene la CPU ocupada y dispara parseo y lógica de app. Buenas cabeceras de cache convierten muchas actualizaciones en comprobaciones livianas.
El mayor ahorro viene de las peticiones condicionales. En lugar de volver a descargar lo mismo, la app pregunta “¿esto cambió?”. Si no, el servidor responde con un pequeño 304 Not Modified.
Usa ETag en respuestas que representen una versión de un recurso, y que el cliente envíe If-None-Match en la siguiente petición. Si generar un ETag es difícil, Last-Modified con If-Modified-Since funciona bien para recursos simples con “updated at”.
Para datos que cambian raramente, haz explícita la decisión de cache. Cache-Control debe coincidir con la realidad. Un max-age corto para perfiles de usuario puede ser aceptable, mientras que config de la app, listas de referencia y feature flags pueden tener duraciones más largas.
En el lado de la app, trata el 304 como una ruta rápida. No parsees JSON (no lo hay), no reconstruyas modelos y no hagas parpadeos en la UI. Mantén lo que ya está en pantalla y solo actualiza indicadores.
Ejemplo: una pantalla de seguimiento de pedido consulta estado cada 15 segundos. Si la respuesta incluye un ETag, la mayoría de las comprobaciones pueden devolver 304. La app conserva el último estado y solo hace trabajo real cuando el estado cambia (por ejemplo, “Empacado” a “Enviado”).
Sé intencional con datos sensibles. Cachear no es automáticamente inseguro, pero necesita reglas claras. Evita cachear respuestas con detalles personales o tokens a menos que los requisitos del producto lo permitan. Usa vidas cortas para datos específicos de usuario y evita que caches compartidos almacenen respuestas privadas (usa Cache-Control: private cuando haga falta).
Payloads más inteligentes: enviar menos, parsear menos
Cada byte extra cuesta más que solo ancho de banda en móvil. Mantiene la radio despierta más tiempo y hace que la app gaste CPU decodificando JSON y actualizando modelos. Si buscas reducir la chattiness de la API, recortar payloads suele ser la victoria más rápida.
Empieza auditando payloads por pantalla. Elige una pantalla (por ejemplo, el feed de inicio), registra tamaños de respuesta y revisa campo por campo: ¿el cliente renderiza esto o solo lo usa para decidir qué mostrar? Si no, elimínalo de ese endpoint.
Para listas, una forma “summary” pequeña suele bastar. Una fila de lista normalmente necesita id, título, estado y timestamp. No necesita descripciones completas, notas largas ni objetos profundamente anidados. Obtén detalles solo cuando el usuario abra un item.
Alos cambios que suelen reducir payloads rápidamente:
- Preferir ids o códigos enum cortos en vez de cadenas largas repetidas
- No reenviar valores por defecto obvios que el cliente pueda asumir
- Evitar repetir el mismo objeto anidado en cada elemento de la lista
- Mantener nombres de campo y tipos estables para que el cliente no ramifique ni re‑parsee
La compresión puede ayudar, especialmente en redes lentas, pero prueba el intercambio de CPU en dispositivos reales. Gzip o Brotli a menudo reducen mucho el tamaño transferido, pero teléfonos antiguos pueden gastar tiempo notable en descomprimir respuestas muy grandes. La mejor ganancia sigue siendo enviar menos datos desde el principio.
Trata las formas de respuesta como un contrato. Cuando nombres de campo y tipos se mantienen consistentes, la app necesita menos fallback y menos código defensivo, lo que ayuda a mantener la UI suave y el consumo de batería bajo.
Diseñar para redes pobres y menos reintentos
Una app móvil no consume batería solo cuando envía requests; también cuando falla, reintenta, despierta la radio de nuevo y repite trabajo. Si una API asume Wi‑Fi perfecto, los usuarios reales en 4G inestable lo pagan.
Facilita pedir menos datos. Prefiere filtros y paginación en el servidor sobre “descargar todo y filtrar en el teléfono”. Si una pantalla necesita los últimos 20 eventos para un usuario, soporta esa consulta exacta para que la app no traiga cientos de filas para desechar la mayoría.
Soporta sync por deltas para que la app pueda preguntar “¿qué cambió desde la última vez?”. Esto puede ser tan simple como devolver un timestamp o un número de versión creciente, y permitir que el cliente pida solo actualizaciones y borrados desde ese punto.
Los reintentos son inevitables, así que haz que las actualizaciones sean idempotentes. Un reintento no debería cobrar de más, enviar duplicados ni crear entradas dobles. Llaves de idempotencia para operaciones de creación y semánticas de actualización que establezcan estado (en vez de “sumar uno”) ayudan mucho.
Cuando ocurran reintentos, evita tormentas de reintentos. Usa backoff exponencial con jitter para que miles de dispositivos no golpeen tus servidores al mismo tiempo y para que el teléfono no se despierte cada segundo.
Devuelve códigos de error claros que ayuden a la app a decidir qué hacer. Un 401 debe provocar reauth, un 404 normalmente debe detener reintentos, un 409 puede requerir refrescar el estado, y un 429 o 503 debe activar backoff.
Comportamientos del cliente que multiplican los costos de la API
Incluso con una API bien diseñada, el cliente puede multiplicar silenciosamente el trabajo de red. En móvil, esos despertares extra y el tiempo de radio a menudo cuestan más batería que los bytes mismos.
Cachea datos que cambian rara vez. Fotos de perfil, feature flags y datos de referencia (países, estados, categorías) no deberían solicitarse en cada visita de pantalla. Dales vidas sensatas, guárdalos en disco y refresca solo cuando sea necesario. Si la API soporta validación (ETag o Last-Modified), una rápida revalidación suele ser mucho más barata que una descarga completa.
Otro problema común son las requests duplicadas en vuelo. Dos partes de la app piden el mismo recurso al mismo tiempo (por ejemplo, un header y una pantalla de ajustes piden el perfil). Sin coalescencia, envías dos llamadas, parseas dos respuestas y actualizas estado dos veces. Trata una llamada de red como fuente única de la verdad y permite que múltiples consumidores esperen por ella.
El refresh en segundo plano debe ser intencional. Si no cambia lo que el usuario verá pronto, o no va a disparar una notificación, generalmente puede esperar. Evita ejecutar lógica de refresh en cada reanudación de la app. Añade un breve cooldown y comprueba cuándo se actualizó por última vez el dato.
Si construyes backends con AppMaster, es más fácil soportar estos comportamientos del cliente modelando endpoints en torno a pantallas, manteniendo esquemas de respuesta consistentes y añadiendo cabeceras de cache de forma controlada para que los clientes puedan permanecer callados la mayor parte del tiempo.
FAQ
Un buen punto de partida es fijar un presupuesto por pantalla y luego medir sesiones reales. Muchos equipos comienzan con algo como 4–8 requests para una carga fría en celular y luego lo ajustan al eliminar los mayores culpables. El número correcto es el que alcanza de forma fiable tu objetivo de tiempo hasta interactivo sin provocar reintentos o periodos prolongados de radio activo.
El batching suele ayudar cuando varias llamadas siempre ocurren juntas, pero puede perjudicar si la petición agrupa demasiado trabajo o devuelve una respuesta gigante. Mantén las respuestas de los batches “con forma de pantalla” y acotadas para que una sola request no se convierta en un punto único de fallo. Si un endpoint batcheado se queda colgado o devuelve muchos datos no usados, separa de nuevo en unas pocas llamadas enfocadas.
Empieza con ETag y peticiones condicionales usando If-None-Match, porque pueden convertir muchas actualizaciones en pequeñas respuestas 304 Not Modified. Añade Cache-Control que refleje con qué frecuencia cambia realmente el dato, para que el cliente evite trabajo de red innecesario. Si ETag es difícil, Last-Modified con If-Modified-Since es un buen recurso para recursos basados en “actualizado en”.
Usa ETag cuando quieras una comprobación de “versión” fiable para un recurso, especialmente si los cambios no se mapean bien a una marca de tiempo. Usa Last-Modified cuando el servidor tenga un tiempo de actualización claro y te baste la granularidad de timestamp. Si solo puedes implementar una, ETag suele ser la opción más precisa para evitar descargas innecesarias.
Instrumenta por pantalla y por sesión, no solo por endpoint. Registra conteos de requests, bytes (cabeceras más cuerpo), reintentos, timeouts y tiempo hasta interactivo; separa tráfico en primer plano y en background para no optimizar el tráfico equivocado. Normalmente verás que unas pocas pantallas o flujos generan la mayoría de los wakeups repetidos.
Diseña la respuesta del batch para que cada sub‑resultado pueda fallar o tener éxito independientemente, e incluye detalles de error suficientes para que el cliente reintente solo lo que falló. Evita que el cliente vuelva a pedir todo el batch porque una parte falló. Esto reduce tráfico duplicado y evita wakeups extra en conexiones inestables.
Recorta las respuestas a lo que la pantalla renderiza ahora y usa formas resumidas para las listas. Mueve campos pesados o raramente usados a un endpoint de detalle que cargue solo cuando el usuario abra el elemento. Esto reduce bytes en el aire y el parsing de JSON y las actualizaciones de modelo, que pueden ser un coste significativo de CPU y batería en los teléfonos.
Usa backoff exponencial con jitter y limita la ventana total de reintentos para que el teléfono no despierte cada pocos segundos. Haz las operaciones de escritura idempotentes para que un reintento no duplique acciones ni cargos. Además, devuelve códigos de estado claros para que el cliente deje de reintentar cuando el error no se vaya a arreglar solo.
Los intervalos de polling cortos mantienen ocupado el radio y la CPU incluso cuando no hay cambios. Si debes hacer polling, aumenta los intervalos cuando las respuestas no cambian y pausa el polling en segundo plano. Cuando sea posible, pasa a actualizaciones impulsadas por eventos para que la app solo se despierte cuando haya algo nuevo que mostrar.
En AppMaster puedes crear endpoints orientados a la pantalla y mantener esquemas de respuesta consistentes, lo que facilita el batching y el ajuste de payloads. También puedes implementar comportamientos amigables con el cache añadiendo lógica de versionado y devolviendo cabeceras que soporten peticiones condicionales, de modo que los clientes obtengan respuestas rápidas de “sin cambios”. Un enfoque práctico es empezar con una pantalla de alto tráfico, publicar un endpoint batcheado y añadir ETag en sus GETs clave, luego medir la caída en requests y reintentos.


