Códigos de Estado 2xx de Éxito

Fundamentals Jan 9, 2026 HTTP
http status-codes success rest

Definición

Los códigos de estado 2xx de Éxito indican que la solicitud HTTP del cliente fue recibida, comprendida y aceptada exitosamente por el servidor. Estos códigos confirman que la operación se completó como se esperaba.

Los códigos de estado 2xx más comunes son:

  • 200 OK - La solicitud tuvo éxito, la respuesta contiene los datos solicitados
  • 201 Created - Nuevo recurso creado exitosamente
  • 202 Accepted - Solicitud aceptada para procesamiento (operaciones asíncronas)
  • 204 No Content - La solicitud tuvo éxito pero no hay contenido que devolver (ej. operaciones DELETE)
  • 206 Partial Content - El servidor está devolviendo solo parte del recurso (solicitudes de rango)

Los códigos 2xx están definidos en el RFC 7231 y son fundamentales para el diseño de APIs RESTful, indicando operaciones CRUD exitosas.

Ejemplo

200 OK - GET Exitoso:

GET /api/users/123 HTTP/1.1
Host: api.example.com
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 142

{
  "id": 123,
  "name": "Alice Smith",
  "email": "[email protected]",
  "role": "developer"
}

201 Created - POST Exitoso:

POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "name": "Bob Johnson",
  "email": "[email protected]"
}

HTTP/1.1 201 Created
Location: /api/users/124
Content-Type: application/json
Content-Length: 168

{
  "id": 124,
  "name": "Bob Johnson",
  "email": "[email protected]",
  "createdAt": "2026-01-09T10:30:00Z"
}

204 No Content - DELETE Exitoso:

DELETE /api/users/123 HTTP/1.1
Host: api.example.com
Authorization: Bearer YOUR_TOKEN

HTTP/1.1 204 No Content
Date: Thu, 09 Jan 2026 10:30:00 GMT

202 Accepted - Procesamiento Asíncrono:

POST /api/reports/generate HTTP/1.1
Host: api.example.com
Content-Type: application/json

{
  "reportType": "sales",
  "dateRange": "2025-12-01/2025-12-31"
}

HTTP/1.1 202 Accepted
Location: /api/reports/status/abc-123
Content-Type: application/json

{
  "status": "processing",
  "statusUrl": "/api/reports/status/abc-123",
  "estimatedCompletion": "2026-01-09T10:35:00Z"
}

Ejemplo de Código

JavaScript (Fetch API):

// Manejar 200 OK
const getUser = async (userId) => {
  const response = await fetch(`https://api.example.com/users/${userId}`, {
    headers: {
      'Accept': 'application/json',
      'Authorization': 'Bearer YOUR_TOKEN'
    }
  });

  if (response.status === 200) {
    return await response.json();
  }

  throw new Error(`Estado inesperado: ${response.status}`);
};

// Manejar 201 Created
const createUser = async (userData) => {
  const response = await fetch('https://api.example.com/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer YOUR_TOKEN'
    },
    body: JSON.stringify(userData)
  });

  if (response.status === 201) {
    const location = response.headers.get('Location');
    console.log('Recurso creado en:', location);
    return await response.json();
  }

  throw new Error(`Fallo al crear: ${response.status}`);
};

// Manejar 204 No Content
const deleteUser = async (userId) => {
  const response = await fetch(`https://api.example.com/users/${userId}`, {
    method: 'DELETE',
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN'
    }
  });

  if (response.status === 204) {
    console.log('Usuario eliminado exitosamente');
    return; // Sin contenido que parsear
  }

  throw new Error(`Fallo al eliminar: ${response.status}`);
};

// Manejar 202 Accepted (operaciones asíncronas)
const generateReport = async (reportData) => {
  const response = await fetch('https://api.example.com/reports/generate', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer YOUR_TOKEN'
    },
    body: JSON.stringify(reportData)
  });

  if (response.status === 202) {
    const data = await response.json();
    console.log('Generación de reporte iniciada:', data.statusUrl);

    // Consultar URL de estado hasta completar
    return await pollStatus(data.statusUrl);
  }

  throw new Error(`Fallo al iniciar reporte: ${response.status}`);
};

const pollStatus = async (statusUrl) => {
  const maxAttempts = 60;
  const delay = 2000; // 2 segundos

  for (let i = 0; i < maxAttempts; i++) {
    const response = await fetch(statusUrl, {
      headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
    });

    const data = await response.json();

    if (data.status === 'completed') {
      return data.result;
    } else if (data.status === 'failed') {
      throw new Error('La generación del reporte falló');
    }

    await new Promise(resolve => setTimeout(resolve, delay));
  }

  throw new Error('Timeout en la generación del reporte');
};

Python (librería requests):

import requests
import time

# Manejar 200 OK
def get_user(user_id):
    response = requests.get(
        f'https://api.example.com/users/{user_id}',
        headers={
            'Accept': 'application/json',
            'Authorization': 'Bearer YOUR_TOKEN'
        }
    )

    if response.status_code == 200:
        return response.json()

    raise Exception(f'Estado inesperado: {response.status_code}')

# Manejar 201 Created
def create_user(user_data):
    response = requests.post(
        'https://api.example.com/users',
        json=user_data,
        headers={'Authorization': 'Bearer YOUR_TOKEN'}
    )

    if response.status_code == 201:
        location = response.headers.get('Location')
        print(f'Recurso creado en: {location}')
        return response.json()

    raise Exception(f'Fallo al crear: {response.status_code}')

# Manejar 204 No Content
def delete_user(user_id):
    response = requests.delete(
        f'https://api.example.com/users/{user_id}',
        headers={'Authorization': 'Bearer YOUR_TOKEN'}
    )

    if response.status_code == 204:
        print('Usuario eliminado exitosamente')
        return  # Sin contenido que parsear

    raise Exception(f'Fallo al eliminar: {response.status_code}')

# Manejar 202 Accepted (operaciones asíncronas)
def generate_report(report_data):
    response = requests.post(
        'https://api.example.com/reports/generate',
        json=report_data,
        headers={'Authorization': 'Bearer YOUR_TOKEN'}
    )

    if response.status_code == 202:
        data = response.json()
        print(f"Generación de reporte iniciada: {data['statusUrl']}")

        # Consultar URL de estado hasta completar
        return poll_status(data['statusUrl'])

    raise Exception(f'Fallo al iniciar reporte: {response.status_code}')

def poll_status(status_url):
    max_attempts = 60
    delay = 2  # 2 segundos

    for i in range(max_attempts):
        response = requests.get(
            status_url,
            headers={'Authorization': 'Bearer YOUR_TOKEN'}
        )

        data = response.json()

        if data['status'] == 'completed':
            return data['result']
        elif data['status'] == 'failed':
            raise Exception('La generación del reporte falló')

        time.sleep(delay)

    raise Exception('Timeout en la generación del reporte')

Diagrama

graph TB
    subgraph "Códigos 2xx de Éxito"
        S200[200 OK
Éxito estándar] S201[201 Created
Recurso creado] S202[202 Accepted
Procesamiento asíncrono] S204[204 No Content
Éxito, sin cuerpo] S206[206 Partial Content
Solicitud de rango] end subgraph "Casos de Uso" UC1[Solicitud GET] UC2[POST crear] UC3[Trabajo asíncrono] UC4[Solicitud DELETE] UC5[Streaming de video] end UC1 --> S200 UC2 --> S201 UC3 --> S202 UC4 --> S204 UC5 --> S206 S200 --> BODY1[Respuesta incluye cuerpo] S201 --> BODY2[Respuesta incluye cuerpo
+ encabezado Location] S202 --> BODY3[Respuesta incluye URL de estado] S204 --> NOBODY[Sin cuerpo de respuesta] S206 --> PARTIAL[Cuerpo de respuesta parcial
+ encabezado Content-Range]

Analogía

Piensa en los códigos de estado 2xx como diferentes tipos de confirmaciones de éxito:

  • 200 OK → “Aquí está lo que pediste” (cajero te entrega tu cambio)
  • 201 Created → “¡Creado! Aquí está tu recibo” (obtienes un nuevo número de cuenta)
  • 202 Accepted → “Estamos trabajando en ello, vuelve más tarde” (pedido en preparación)
  • 204 No Content → “¡Listo! Nada más que decir” (suscripción cancelada, no se necesita recibo)

Buenas Prácticas

  1. Usar el Código 2xx Correcto - No usar 200 para todo; usar 201 para creación, 204 para eliminación
  2. Incluir Encabezado Location - Siempre agregar encabezado Location con 201 Created
  3. Devolver Recurso Creado - Incluir el nuevo recurso en el cuerpo de respuesta 201
  4. Usar 202 para Asíncrono - Operaciones de larga duración deben devolver 202 con URL de estado
  5. Sin Cuerpo para 204 - Nunca incluir un cuerpo de respuesta con 204 No Content
  6. Estructura 200 Consistente - Usar el mismo formato de respuesta en todos los endpoints
  7. Incluir Metadatos - Agregar timestamps, versiones o ETags en respuestas exitosas

Errores Comunes

  • 200 para Todo - Usar 200 OK para creaciones/eliminaciones en lugar de 201/204
  • 201 sin Location - No incluir encabezado Location al crear recursos
  • 204 con Cuerpo - Incluir cuerpo de respuesta con 204 No Content (viola la especificación)
  • 202 Síncrono - Usar 202 para operaciones que se completan inmediatamente
  • Sin URL de Estado - Devolver 202 sin proporcionar forma de verificar el estado
  • Formato Inconsistente - Cambiar estructura de respuesta entre endpoints similares
  • Sin Timestamps - No incluir createdAt o updatedAt en las respuestas

Estándares y RFCs

Términos Relacionados