Mira la lección en vídeo
Accede al vídeo completo de esta lección y a más contenido exclusivo con el Plan Plus.
Desbloquear Plan PlusCódigos de estado principales
Los códigos de estado HTTP son números de tres dígitos que el servidor envía al cliente para indicar el resultado de una petición. En Flask, estos códigos se organizan en cinco categorías principales, cada una con un propósito específico para comunicar el estado de las operaciones de tu API.
Códigos 2xx - Respuestas exitosas
Los códigos de la serie 200 indican que la petición se procesó correctamente. Son los más utilizados en APIs REST cuando las operaciones se completan sin problemas.
200 OK es el código más común y se usa cuando una operación se ejecuta correctamente:
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/usuarios/<int:user_id>')
def obtener_usuario(user_id):
usuario = {'id': user_id, 'nombre': 'Juan', 'email': 'juan@email.com'}
return jsonify(usuario), 200 # Código explícito
201 Created se utiliza específicamente cuando se crea un nuevo recurso:
@app.route('/usuarios', methods=['POST'])
def crear_usuario():
# Lógica para crear usuario
nuevo_usuario = {'id': 123, 'nombre': 'Ana', 'email': 'ana@email.com'}
return jsonify(nuevo_usuario), 201
204 No Content es útil para operaciones que se completan exitosamente pero no devuelven datos:
@app.route('/usuarios/<int:user_id>', methods=['DELETE'])
def eliminar_usuario(user_id):
# Lógica para eliminar usuario
return '', 204 # Sin contenido en la respuesta
Códigos 4xx - Errores del cliente
Los códigos de la serie 400 indican que el problema está en la petición del cliente. Son fundamentales para proporcionar retroalimentación clara sobre errores de entrada.
400 Bad Request se usa cuando los datos enviados son incorrectos o están mal formateados:
from flask import request
@app.route('/usuarios', methods=['POST'])
def crear_usuario():
data = request.get_json()
if not data or 'email' not in data:
return jsonify({'error': 'Email es requerido'}), 400
# Continuar con la creación
return jsonify({'mensaje': 'Usuario creado'}), 201
404 Not Found indica que el recurso solicitado no existe:
@app.route('/usuarios/<int:user_id>')
def obtener_usuario(user_id):
# Simular búsqueda en base de datos
usuarios = {1: 'Juan', 2: 'Ana'}
if user_id not in usuarios:
return jsonify({'error': 'Usuario no encontrado'}), 404
return jsonify({'id': user_id, 'nombre': usuarios[user_id]}), 200
409 Conflict se utiliza cuando hay un conflicto con el estado actual del recurso:
@app.route('/usuarios', methods=['POST'])
def crear_usuario():
data = request.get_json()
email = data.get('email')
# Simular verificación de email duplicado
emails_existentes = ['juan@email.com', 'ana@email.com']
if email in emails_existentes:
return jsonify({'error': 'El email ya está registrado'}), 409
return jsonify({'mensaje': 'Usuario creado'}), 201
Códigos 5xx - Errores del servidor
Los códigos de la serie 500 indican problemas internos del servidor. Aunque idealmente deberían evitarse, son importantes para manejar situaciones imprevistas.
500 Internal Server Error se usa para errores generales del servidor:
@app.route('/usuarios/<int:user_id>')
def obtener_usuario(user_id):
try:
# Operación que puede fallar
resultado = operacion_compleja(user_id)
return jsonify(resultado), 200
except Exception as e:
return jsonify({'error': 'Error interno del servidor'}), 500
503 Service Unavailable indica que el servicio no está disponible temporalmente:
@app.route('/usuarios')
def listar_usuarios():
# Verificar si el servicio está disponible
if not servicio_disponible():
return jsonify({'error': 'Servicio temporalmente no disponible'}), 503
return jsonify({'usuarios': []}), 200
Implementación práctica en Flask
Flask permite especificar códigos de estado de múltiples formas. La más directa es devolver una tupla con la respuesta y el código:
@app.route('/productos/<int:producto_id>', methods=['PUT'])
def actualizar_producto(producto_id):
data = request.get_json()
# Validar datos de entrada
if not data:
return jsonify({'error': 'Datos requeridos'}), 400
# Verificar si el producto existe
if not producto_existe(producto_id):
return jsonify({'error': 'Producto no encontrado'}), 404
# Actualizar producto
producto_actualizado = actualizar_datos(producto_id, data)
return jsonify(producto_actualizado), 200
También puedes usar el objeto Response de Flask para mayor control:
from flask import Response
@app.route('/health')
def health_check():
return Response(
response=jsonify({'status': 'healthy'}).data,
status=200,
mimetype='application/json'
)
La consistencia en el uso de códigos de estado es crucial para que los clientes de tu API puedan manejar las respuestas de manera predecible. Cada código debe utilizarse según su propósito específico, evitando usar códigos genéricos cuando existe uno más apropiado para la situación.
Cuándo usar cada código
Guarda tu progreso
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
La selección correcta del código de estado HTTP depende del contexto específico de cada operación en tu API. Cada situación requiere un análisis cuidadoso para elegir el código que mejor comunique el resultado al cliente.
Criterios para operaciones GET
En las consultas de datos, el código de estado debe reflejar si la operación de lectura fue exitosa y si se encontraron los datos solicitados.
Usa 200 OK cuando la consulta se ejecuta correctamente y devuelve datos:
@app.route('/productos')
def listar_productos():
productos = obtener_todos_los_productos()
# Siempre 200, incluso si la lista está vacía
return jsonify({'productos': productos, 'total': len(productos)}), 200
Aplica 404 Not Found únicamente cuando el recurso específico no existe, no cuando una colección está vacía:
@app.route('/categorias/<int:categoria_id>/productos')
def productos_por_categoria(categoria_id):
# Primero verificar si la categoría existe
if not categoria_existe(categoria_id):
return jsonify({'error': 'Categoría no encontrada'}), 404
# Si existe pero no tiene productos, devolver 200 con lista vacía
productos = obtener_productos_categoria(categoria_id)
return jsonify({'productos': productos}), 200
Criterios para operaciones POST
Las operaciones de creación requieren distinguir entre diferentes tipos de éxito y fallo según el resultado de la operación.
Utiliza 201 Created exclusivamente cuando se crea un nuevo recurso:
@app.route('/pedidos', methods=['POST'])
def crear_pedido():
data = request.get_json()
# Validar datos requeridos
if not data or 'cliente_id' not in data:
return jsonify({'error': 'ID del cliente es obligatorio'}), 400
# Verificar que el cliente existe
if not cliente_existe(data['cliente_id']):
return jsonify({'error': 'Cliente no encontrado'}), 404
# Crear el pedido
nuevo_pedido = crear_nuevo_pedido(data)
return jsonify(nuevo_pedido), 201
Emplea 409 Conflict cuando intentas crear algo que ya existe o viola restricciones de unicidad:
@app.route('/usuarios', methods=['POST'])
def registrar_usuario():
data = request.get_json()
# Verificar email único
if email_ya_registrado(data['email']):
return jsonify({
'error': 'Ya existe un usuario con este email',
'codigo': 'EMAIL_DUPLICADO'
}), 409
return jsonify(crear_usuario(data)), 201
Criterios para operaciones PUT y PATCH
Las operaciones de actualización deben considerar si el recurso existe y si la modificación es válida.
Usa 200 OK para actualizaciones exitosas que devuelven el recurso modificado:
@app.route('/productos/<int:producto_id>', methods=['PUT'])
def actualizar_producto_completo(producto_id):
data = request.get_json()
if not producto_existe(producto_id):
return jsonify({'error': 'Producto no encontrado'}), 404
# Actualización completa del recurso
producto_actualizado = reemplazar_producto(producto_id, data)
return jsonify(producto_actualizado), 200
Aplica 204 No Content cuando la actualización es exitosa pero no necesitas devolver datos:
@app.route('/usuarios/<int:user_id>/configuracion', methods=['PATCH'])
def actualizar_configuracion(user_id):
data = request.get_json()
if not usuario_existe(user_id):
return jsonify({'error': 'Usuario no encontrado'}), 404
# Actualización parcial sin respuesta
actualizar_configuracion_usuario(user_id, data)
return '', 204
Criterios para operaciones DELETE
Las operaciones de eliminación requieren manejar tanto el éxito como los casos donde el recurso no existe.
Utiliza 204 No Content para eliminaciones exitosas:
@app.route('/comentarios/<int:comentario_id>', methods=['DELETE'])
def eliminar_comentario(comentario_id):
if not comentario_existe(comentario_id):
return jsonify({'error': 'Comentario no encontrado'}), 404
eliminar_comentario_bd(comentario_id)
return '', 204
Considera 200 OK si necesitas confirmar la eliminación con información adicional:
@app.route('/archivos/<int:archivo_id>', methods=['DELETE'])
def eliminar_archivo(archivo_id):
archivo = obtener_archivo(archivo_id)
if not archivo:
return jsonify({'error': 'Archivo no encontrado'}), 404
eliminar_archivo_sistema(archivo_id)
return jsonify({
'mensaje': 'Archivo eliminado correctamente',
'nombre': archivo['nombre'],
'fecha_eliminacion': datetime.now().isoformat()
}), 200
Manejo de errores de validación
Los errores de validación requieren códigos específicos según el tipo de problema detectado.
Usa 400 Bad Request para errores de formato o datos faltantes:
@app.route('/facturas', methods=['POST'])
def crear_factura():
data = request.get_json()
# Validar estructura de datos
campos_requeridos = ['cliente_id', 'fecha', 'items']
campos_faltantes = [campo for campo in campos_requeridos if campo not in data]
if campos_faltantes:
return jsonify({
'error': 'Campos requeridos faltantes',
'campos': campos_faltantes
}), 400
# Validar formato de fecha
try:
datetime.fromisoformat(data['fecha'])
except ValueError:
return jsonify({'error': 'Formato de fecha inválido'}), 400
return jsonify(procesar_factura(data)), 201
Aplica 422 Unprocessable Entity para datos con formato correcto pero lógicamente inválidos:
@app.route('/reservas', methods=['POST'])
def crear_reserva():
data = request.get_json()
fecha_inicio = datetime.fromisoformat(data['fecha_inicio'])
fecha_fin = datetime.fromisoformat(data['fecha_fin'])
# Datos bien formateados pero lógicamente incorrectos
if fecha_inicio >= fecha_fin:
return jsonify({
'error': 'La fecha de inicio debe ser anterior a la fecha de fin'
}), 422
if fecha_inicio < datetime.now():
return jsonify({
'error': 'No se pueden crear reservas en el pasado'
}), 422
return jsonify(crear_nueva_reserva(data)), 201
Situaciones especiales
Algunas situaciones específicas requieren códigos menos comunes pero igualmente importantes.
Usa 429 Too Many Requests para implementar límites de velocidad:
from functools import wraps
from time import time
# Simulador simple de rate limiting
request_counts = {}
def rate_limit(max_requests=10, window=60):
def decorator(f):
@wraps(f)
def decorated_function(*args, **kwargs):
client_ip = request.remote_addr
current_time = time()
if client_ip not in request_counts:
request_counts[client_ip] = []
# Limpiar requests antiguos
request_counts[client_ip] = [
req_time for req_time in request_counts[client_ip]
if current_time - req_time < window
]
if len(request_counts[client_ip]) >= max_requests:
return jsonify({
'error': 'Demasiadas peticiones',
'retry_after': window
}), 429
request_counts[client_ip].append(current_time)
return f(*args, **kwargs)
return decorated_function
return decorator
@app.route('/api/buscar')
@rate_limit(max_requests=5, window=60)
def buscar():
return jsonify({'resultados': []})
La coherencia en la aplicación de estos criterios es fundamental para crear una API predecible. Los clientes de tu API deben poder confiar en que cada código de estado significa exactamente lo mismo en todas las operaciones, facilitando el manejo de errores y la lógica de reintentos.
Aprendizajes de esta lección de Flask
- Comprender las categorías principales de códigos de estado HTTP y su significado.
- Aprender a implementar códigos de estado HTTP en respuestas de Flask.
- Identificar cuándo usar códigos 2xx, 4xx y 5xx según el contexto de la operación.
- Aplicar criterios específicos para operaciones GET, POST, PUT, PATCH y DELETE.
- Manejar errores de validación y situaciones especiales como límites de peticiones (rate limiting).
Completa este curso de Flask y certifícate
Únete a nuestra plataforma de cursos de programación y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs