---
title: "Cómo encontrar cuellos de botella en API más rápido con reportes de pruebas de carga estructurados"
description: "Convierte métricas k6 y requests con tag en una narrativa de cuello de botella—cliente vs red vs app vs datos—sin adivinar desde un solo gráfico."
publishDate: "2026-06-02"
draft: false
keyword: "análisis cuellos botella api"
intent: "BOFU"
cta: "Usa los reportes de Performate para aislar cuellos de botella más rápido y alinear al equipo en correcciones accionables."
tags: ["rendimiento-api", "bottlenecks", "reportes", "pruebas-de-carga"]
translationOf: api-bottleneck-analysis
---

Tu dashboard muestra `p99` al alza y throughput plano, y tres ingenieros señalan tres subsistemas distintos.

Un cuello de botella es el **recurso más lento limitado en el path que tu escenario realmente ejercita**—no el servicio con más CPU en abstracto. Las pruebas de carga muestran síntomas; **métricas k6 etiquetadas más un orden de triage estructurado** convierten esos síntomas en «revisa el pool», «arregla el índice» o «el escenario está mal».

En esta guía verás cómo validar primero la fidelidad del escenario, separar delay cliente vs servidor con tags k6, rankear rutas por contribución a la cola, mapear firmas de saturación a subsistemas y exportar evidencia accionable, sin repetir corridas a ciegas.

## Por qué un solo gráfico no nombra el cuello de botella

`http_req_duration` agrega cada ruta, clase de status y reintento. Bajo carga, modos de fallo se superponen:

- **Errores al alza, latencia estable** suele ser auth, validación, cuotas o feature flags—no saturación de CPU.
- **Errores estables, latencia explosiva** suele ser pools de threads/conexiones o backpressure en colas.
- **Errores planos, colas crecientes en una ruta** suele ser hot path, índice faltante o dependencia downstream.

El workbook SRE de Google encuadra SLIs en lo que percibe el usuario ([implementing SLOs](https://sre.google/workbook/implementing-slos/)); k6 da la capa de medición cuando las requests llevan tag de ruta y dependencia ([métricas](https://k6.io/docs/using-k6/metrics/)).

Antes del diagnóstico profundo, confirma que el escenario no miente—[errores comunes en pruebas de carga](/es/blog/errores-comunes-pruebas-de-carga). Alinea fidelidad del entorno y [cómo leer reportes de pruebas de carga](/es/blog/como-leer-reportes-pruebas-de-carga) cuando los tags estén listos.

## Implementación práctica en k6: tags, umbrales y ranking por ruta

Instrumenta cada familia de requests con tags para partir `http_req_duration` y `http_req_failed` por ruta.

**Script de ejemplo (ilustrativo—no listo para producción).** Paths y SLO ficticios.

**Qué demuestra este ejemplo:**

- **Varias rutas en un escenario** con funciones `exec` separadas y claves de tag consistentes.
- **Umbrales por ruta** para que un health check rápido no oculte la cola de checkout.
- **Checks** que separan 4xx (cliente/config) de 5xx (servidor/saturación) al extender el script.

```javascript
import http from 'k6/http';
import { check, sleep } from 'k6';

const BASE = __ENV.API_BASE || 'https://staging.example.com';
const headers = {
  'Content-Type': 'application/json',
  Authorization: `Bearer ${__ENV.TOKEN}`,
};

export const options = {
  scenarios: {
    api_mix: {
      executor: 'ramping-arrival-rate',
      startRate: 5,
      timeUnit: '1s',
      preAllocatedVUs: 30,
      maxVUs: 120,
      stages: [
        { duration: '2m', target: 20 },
        { duration: '5m', target: 40 },
        { duration: '2m', target: 0 },
      ],
      exec: 'mixedJourney',
    },
  },
  thresholds: {
    'http_req_duration{route:search}': ['p(95)<400', 'p(99)<700'],
    'http_req_duration{route:checkout}': ['p(95)<900', 'p(99)<1400'],
    'http_req_failed{route:checkout}': ['rate<0.02'],
    http_req_failed: ['rate<0.01'],
  },
};

export function mixedJourney() {
  const search = http.get(`${BASE}/v1/search?q=load`, { headers, tags: { route: 'search' } });
  check(search, { 'search 2xx': (r) => r.status >= 200 && r.status < 300 });

  const cart = http.post(`${BASE}/v1/cart/items`, JSON.stringify({ sku: 'A1', qty: 1 }), {
    headers,
    tags: { route: 'cart' },
  });
  check(cart, { 'cart 2xx': (r) => r.status >= 200 && r.status < 300 });

  const checkout = http.post(`${BASE}/v1/checkout`, JSON.stringify({ cartId: 'c-1' }), {
    headers,
    tags: { route: 'checkout' },
  });
  check(checkout, { 'checkout 2xx': (r) => r.status >= 200 && r.status < 300 });
  sleep(0.4);
}
```

**Patrones que funcionan**

- Ordenar rutas etiquetadas por `p95`/`p99`—si una domina la cola, acotas código y dependencias ([p95 vs p99](/es/blog/p95-vs-p99-latencia)).
- Comparar tiempos de iteración k6 con trazas de servidor cuando la política lo permita.
- Documentar el executor (`constant-vus` vs arrival-rate) para reproducir el “cuello” ([escenarios](https://k6.io/docs/using-k6/scenarios/)).
- Exportar JSON de resumen con git SHA y parámetros del escenario en cada ticket.

**Antipatrones a evitar**

- Declarar “la base está lenta” desde una línea global sin tags de ruta.
- Rampar VUs cuando el producto mide requests por segundo—saturación falsa en el cliente.
- Abrir tickets de infra sin ADN del escenario—reruns incorrectos.

**Pro tip (comando de ejemplo):** stats de cola en el resumen CLI para war rooms.

```bash
k6 run api-mix.js --summary-trend-stats="p(95),p(99),max"
```

**Qué demuestra este comando:** tendencias de percentiles y máximo por grupo de tags muestran qué ruta rompió la cola antes de abrir APM.

## Marco de decisión: síntoma → capa probable

| Ventana de señal | Suele implicar | Siguiente chequeo |
|:---|:---|:---|
| Subida gradual de latencia, pocos errores | Agotamiento de pool, GC, profundidad de cola | Métricas de pool, thread dumps, lag de broker |
| Caída abrupta de latencia | Circuit breaker, throttling, deploy | Logs de gateway, contadores de rate limit |
| Picos periódicos | Cron, eviction de caché, batch | Anotar schedules; segmentar intervalos k6 |
| Errores arriba, latencia plana | Auth, validación, cuota | Desglose de status por tag de ruta |
| Una ruta domina `p99` | Handler caliente, N+1, índice faltante | Trace de esa ruta; [pruebas de contrato vs rendimiento](/es/blog/pruebas-contrato-vs-rendimiento) |

**Detén y corrige el escenario si** la forma de tráfico, think time o estado de caché no refleja producción.

**Escala a datos si** solo lecturas profundas o rutas de paginación divergen.

## Observabilidad, documentación y próximos pasos

Antes del próximo war room de rendimiento:

- [ ] Etiquetar cada familia de requests con `route` (y `dependency` al llamar downstreams).
- [ ] Registrar executor, duración, objetivos RPS/VU y fingerprint de entorno.
- [ ] Rankear rutas por contribución a `p99`; adjuntar snapshots de las tres primeras a tickets.
- [ ] Correlacionar timestamps de picos k6 con APM y slow queries.
- [ ] Especificar el siguiente experimento por ticket (índice, tamaño de pool, TTL)—no “investigar lentitud”.
- [ ] Re-ejecutar con el mismo ADN del escenario tras cada fix.

## Cómo Performate acelera el triage de cuellos de botella

Hojas de cálculo distintas tras cada corrida frenan decisiones. Un export compartido con desglose por ruta alinea ingeniería y producto.

**Ejemplo: triage de un journey search → cart → checkout**

1. **Importar la colección Postman** que refleja el path real. *Problema resuelto:* una definición de journey en lugar de tres scripts huérfanos.
2. **Crear un escenario ramping arrival-rate** con la forma RPS del pico de la semana pasada. *Problema resuelto:* saturación honesta sin adivinar VUs ([cuántos usuarios virtuales](/es/blog/cuantos-usuarios-virtuales-k6)).
3. **Poner tags por request** en el panel: `route:search`, `route:cart`, `route:checkout`. *Problema resuelto:* cortes del reporte alineados al modelo de umbrales k6.
4. **Ejecutar y abrir la vista de comparación**—filtrar por tag y ordenar por `p99`. *Problema resuelto:* la cola de checkout visible aunque search se vea bien.
5. **Adjuntar umbrales por ruta** para que las regresiones fallen fuerte en checkout antes de que search derive. *Problema resuelto:* gates alineados a SLIs visibles al usuario.
6. **Exportar resumen + script k6** para smoke en CI tras los fixes.

Ese flujo cumple el `cta`: aislar cuellos más rápido y alinear al equipo en correcciones con el mismo reporte estructurado.

## Cierre

El **análisis de cuellos de botella en API** es triage ordenado: valida el escenario, etiqueta rutas, rankea colas, mapea firmas a subsistemas y ticketea evidencia. El recurso más lento está en el path que ejercitaste; hazlo medible.

Ejecuta esta semana tu journey mixto con tags de ruta y resúmenes por percentil—anota qué ruta única posee el `p99` que nombra tu SLO.

[Try Performate free](https://performate.app) | [Book a demo](/demo) | [k6 results output](https://k6.io/docs/getting-started/results-output/)
