DeepSeek Chat Completions vs Responses API: cuál elegir

Usa Chat Completions si tu integración ya trabaja con una lista de mensajes y cubre tus necesidades. Considera Responses API de DeepSeek cuando tu cliente requiera ese formato o necesites elementos de salida tipados, eventos de streaming detallados y funciones específicas como JSON Schema.

Ambas permiten llamar a modelos de DeepSeek. La diferencia está en el formato de los datos y en las funciones compatibles. Un punto decisivo: Responses en DeepSeek requiere que tu aplicación vuelva a enviar el historial; cambiar de endpoint no añade memoria automática a la conversación.

Esta comparación se refiere a la API oficial alojada en DeepSeek. Las pasarelas de terceros y los servidores locales pueden implementar contratos diferentes.

Qué interfaz de DeepSeek conviene usar

Tu situaciónOpción recomendadaMotivo práctico
Tu aplicación ya usa messages y funciona correctamente.Chat Completions.Puedes conservar la estructura de solicitudes, respuestas y herramientas que ya tienes.
Tu cliente exige Responses o procesa elementos de salida por tipo.Responses.Permite trabajar con mensajes, llamadas a funciones y otros elementos dentro de output.
Necesitas una salida ajustada a JSON Schema.Responses.Su referencia documenta text.format con json_schema.
Necesitas búsqueda web integrada en el servidor.Responses con deepseek-v4-pro.Es la combinación documentada para la herramienta integrada web_search.
Solo necesitas generar texto en un proyecto nuevo.La interfaz que tu cliente y tu aplicación manejen con menos adaptaciones.Evalúa también el formato de las respuestas, el historial y las herramientas que utilizarás después.

Estas recomendaciones se basan en las capacidades y restricciones que DeepSeek publica para Responses. En particular, una integración basada en Codex debe seguir la configuración específica indicada por DeepSeek; que un cliente use Responses no significa que todas sus funciones estén disponibles con cualquier proveedor.

Diferencias entre Chat Completions y Responses

Las dos interfaces usan https://api.deepseek.com como URL base del cliente. El SDK añade el endpoint correspondiente. Usa una clave de DeepSeek y comprueba el identificador del modelo en la lista oficial de modelos; las referencias actuales incluyen deepseek-flash y deepseek-v4-pro.

ElementoChat CompletionsResponses
Endpoint HTTPPOST /chat/completionsPOST /responses
Método del SDK de Pythonclient.chat.completions.create()client.responses.create()
Entradamessagesinput: texto o lista de elementos
Instrucciones del sistemaMensaje con role: systeminstructions
Texto mediante el SDKchoices[0].message.contentoutput_text
Límite de generaciónmax_tokensmax_output_tokens
Estado de finalizaciónchoices[0].finish_reasonstatus; detalles en error o incomplete_details
Consumo de entrada y salidausage.prompt_tokens y usage.completion_tokensusage.input_tokens y usage.output_tokens
Continuación de una conversaciónVuelves a enviar el contexto en messages.Vuelves a enviar el contexto en input.

Puedes contrastar los campos en las referencias de Chat Completions y Responses. En Responses, output_text es una comodidad del SDK: si llamas por HTTP y lees el JSON directamente, procesa los elementos de output según su tipo.

Documentación de DeepSeek Chat Completions API con el endpoint /chat/completions y el campo messages.

Por eso, una migración requiere adaptar tanto la solicitud como la lectura del resultado. Cambiar únicamente el nombre del método deja pendientes el historial, las herramientas y el tratamiento del streaming.

Ejemplo en Python: la misma tarea con ambas interfaces

Este ejemplo resume un texto de prueba. Mantiene el mismo modelo, las mismas instrucciones y un límite de 256 tokens de salida. También desactiva el razonamiento en ambos casos para comparar la estructura del código. Las respuestas y el consumo pueden variar entre ejecuciones.

Ejemplo oficial en Python de DeepSeek Responses API con client.responses.create, input y response.output_text.

Prepara el cliente y la clave

Necesitas Python 3.10 o superior, de acuerdo con los requisitos actuales del SDK oficial de OpenAI para Python, y una clave de tu cuenta en la plataforma oficial de DeepSeek. Dentro de tu entorno de Python, instala o actualiza el cliente:

python -m pip install --upgrade openai

Define la variable DEEPSEEK_API_KEY en la misma terminal donde ejecutarás el archivo. En macOS o Linux:

export DEEPSEEK_API_KEY="TU_CLAVE_DEEPSEEK"

En Windows PowerShell:

$env:DEEPSEEK_API_KEY="TU_CLAVE_DEEPSEEK"

Sustituye el valor de ejemplo por tu clave. El archivo Python la leerá desde el entorno; no la incluyas en el código ni en un repositorio. El nombre de la biblioteca openai identifica al cliente utilizado, mientras que base_url determina el servicio al que se envían las solicitudes.

Guarda y ejecuta el ejemplo

Guarda este código como comparar_deepseek.py. Cada ejecución selecciona una interfaz y envía una solicitud:

"""Envía una sola solicitud de resumen a DeepSeek con la interfaz elegida."""

import argparse
import os
import sys

from openai import APIError, APIStatusError, OpenAI

MODEL = "deepseek-flash"
INSTRUCTIONS = "Responde en español latinoamericano neutro y no añadas datos."
PROMPT = (
    "Resume en una frase: El equipo revisó 20 solicitudes. Aprobó 15 y dejó "
    "5 pendientes por falta de información."
)


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("api", choices=("chat", "responses"))
    args = parser.parse_args()
    api_key = os.environ.get("DEEPSEEK_API_KEY", "").strip()
    if not api_key:
        print("Falta la variable DEEPSEEK_API_KEY.", file=sys.stderr)
        return 2

    try:
        with OpenAI(
            api_key=api_key,
            base_url="https://api.deepseek.com",
            timeout=60.0,  # Elección para esta demostración.
            max_retries=0,  # Una solicitud, sin reintentos automáticos.
        ) as client:
            if args.api == "chat":
                result = client.chat.completions.create(
                    model=MODEL,
                    messages=[
                        {"role": "system", "content": INSTRUCTIONS},
                        {"role": "user", "content": PROMPT},
                    ],
                    max_tokens=256,
                    extra_body={"thinking": {"type": "disabled"}},
                )
                choice = result.choices[0] if result.choices else None
                text = choice.message.content if choice else None
                completed = choice is not None and choice.finish_reason == "stop"
                finish_state = choice.finish_reason if choice else "sin_resultado"
                usage_fields = ("prompt_tokens", "completion_tokens")
            else:
                result = client.responses.create(
                    model=MODEL,
                    instructions=INSTRUCTIONS,
                    input=PROMPT,
                    max_output_tokens=256,
                    reasoning={"effort": "none"},
                )
                text = result.output_text
                completed = result.status == "completed"
                finish_state = result.status
                usage_fields = ("input_tokens", "output_tokens")
    except APIStatusError as error:
        print(f"Error HTTP: {error.status_code}.", file=sys.stderr)
        return 1
    except APIError as error:
        print(f"Error del SDK: {type(error).__name__}.", file=sys.stderr)
        return 1

    print(f"Finalización: {finish_state}")
    if not completed or not isinstance(text, str) or not text.strip():
        print("Respuesta incompleta o sin texto utilizable.", file=sys.stderr)
        return 1
    print(text.strip())
    usage = getattr(result, "usage", None)
    for label, field in zip(("entrada", "salida"), usage_fields):
        value = getattr(usage, field, None)
        if value is not None:
            print(f"Tokens de {label}: {value}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Ejecuta la ruta que quieras comprobar:

python comparar_deepseek.py chat
python comparar_deepseek.py responses

Si ejecutas los dos comandos, realizarás dos solicitudes independientes. El programa muestra la finalización, el resumen y los contadores de tokens disponibles. Comprueba que el resumen conserve los datos del texto original; una respuesta completada también necesita revisión de contenido.

El límite de 256 tokens, el timeout de 60 segundos y la ausencia de reintentos automáticos son decisiones de esta demostración. Ajusta esas políticas a tu aplicación. El timeout del cliente no representa una garantía sobre la duración total de la operación.

DeepSeek documenta el razonamiento activado por defecto. En este ejemplo, Chat Completions lo desactiva mediante thinking.type, mientras Responses usa reasoning.effort="none". Si lo activas, revisa los controles y las reglas del modo de razonamiento de cada interfaz.

Cómo conservar el historial de conversación

En Chat Completions, tu aplicación incorpora los mensajes anteriores a messages. La guía oficial de conversaciones de varios turnos explica este funcionamiento sin estado. Responses sigue el mismo principio: el contexto necesario debe viajar en la nueva solicitud.

Para continuar el ejemplo anterior, una pregunta como “¿Cuántas solicitudes quedaron pendientes?” necesita el texto que contenía los datos. En una conversación solo de texto, sin herramientas y con razonamiento desactivado, el flujo es:

  1. Conserva el mensaje inicial del usuario y la respuesta del asistente.
  2. Añade la nueva pregunta al historial.
  3. Envía esa secuencia en messages o input, según la interfaz.
  4. Incluye de nuevo las instrucciones de sistema que deban seguir vigentes.

En Responses de DeepSeek, previous_response_id y conversation no recuperan el turno anterior. store tampoco habilita esa persistencia. Esta limitación está descrita en su referencia de Responses.

Documentación de DeepSeek Responses API sobre el envío del historial completo de la conversación en input.

Cuando agregues herramientas, conserva también las llamadas y sus resultados, emparejados con sus identificadores. Guardar únicamente output_text puede perder información necesaria para continuar el proceso. En Chat Completions, si envías tools con el modo de razonamiento, la guía de thinking exige devolver el reasoning_content de los turnos anteriores; eliminarlo puede provocar HTTP 400.

Que la API sea sin estado y devuelva store: false describe la continuidad de la conversación. Por sí solo, ese comportamiento no demuestra una política de retención cero para todos los datos.

Streaming: cómo leer el texto y confirmar que terminó

Con stream=True, la aplicación recibe contenido a medida que se genera. La diferencia práctica está en el lector que procesa esos fragmentos y reconoce la finalización.

Qué revisarChat CompletionsResponses
Fragmentos de textochoices[].delta.contentEventos response.output_text.delta; texto en event.delta
Finalizaciónfinish_reason en el último fragmento; cierre SSE con [DONE]response.completed, response.incomplete o response.failed
Consumousage en el último fragmento de DeepSeekusage en el objeto de respuesta del evento final
Lectura defensivaComprueba que exista una opción y que el fragmento contenga texto.Comprueba el tipo de evento antes de leer sus campos.

DeepSeek documenta un detalle relevante para Chat Completions: el último fragmento transporta el consumo junto con una opción cuyo finish_reason ya está definido. No emite un fragmento adicional dedicado únicamente a usage con choices vacío. Revisa este contrato en la referencia de streaming de Chat Completions.

Responses utiliza los eventos finales documentados para Responses y no envía data: [DONE]. Además, stream_options no está soportado en esa interfaz; trasladarlo desde Chat Completions no configura el consumo del streaming.

Documentación de streaming de DeepSeek Responses API con eventos de finalización y un ejemplo para recibir texto incremental.

En tu aplicación, distingue el texto provisional del resultado terminado. Marca la operación como completada solo cuando recibas la señal correspondiente. Si llega response.incomplete, consulta incomplete_details; si llega response.failed, revisa error. Si la conexión se interrumpe sin señal final, conserva ese estado como interrupción. La ausencia de usage no demuestra consumo cero.

En un flujo con herramientas, incluso una respuesta marcada como completada puede contener una llamada que tu aplicación deba ejecutar. Comprueba el tipo de salida que necesita tu siguiente paso.

Herramientas, JSON Schema e imágenes: diferencias que cambian la elección

Llamadas a funciones y resultados

Las dos interfaces admiten llamadas a funciones. Tu aplicación recibe los argumentos, los valida, ejecuta la función autorizada y devuelve el resultado. El modelo no ejecuta por sí mismo el código de esa función, como explica la guía oficial de Tool Calls.

PasoChat CompletionsResponses
Declarar la funciónname y parameters dentro de function.name y parameters directamente en el objeto de herramienta.
Recibir la llamadamessage.tool_calls.Elemento de tipo function_call en output.
Devolver el resultadoMensaje role: tool con tool_call_id.Elemento function_call_output con call_id.

Al migrar, conserva la relación entre cada llamada y su resultado. En Responses, utiliza call_id para esa relación; no lo sustituyas por el id de otro elemento. Si pueden llegar varias llamadas, procesa y vincula cada resultado de forma independiente.

Algunos agentes insertan en el historial llamadas a herramientas que no fueron generadas por el modelo, junto con sus resultados. La guía de Tool Calls documenta este mecanismo para Responses, pero no para Chat Completions. Si tu orquestador necesita crear esas llamadas, comprueba esta capacidad antes de elegir la interfaz.

JSON válido y salida con JSON Schema

Chat Completions documenta response_format={"type":"json_object"}. En este modo debes pedir JSON en las instrucciones, proporcionar un formato claro y dejar un presupuesto suficiente de salida. La guía de JSON Output también advierte que puede aparecer contenido formado solo por espacios.

Responses documenta text.format con json_schema. Para extraer los datos del texto de prueba, define este diccionario de Python antes de main():

FORMATO_TEXTO = {
    "format": {
        "type": "json_schema",
        "name": "resumen_solicitudes",
        "schema": {
            "type": "object",
            "properties": {
                "revisadas": {
                    "type": "integer"
                },
                "aprobadas": {
                    "type": "integer"
                },
                "pendientes": {
                    "type": "integer"
                }
            },
            "required": [
                "revisadas",
                "aprobadas",
                "pendientes"
            ],
            "additionalProperties": False
        }
    }
}

Añade text=FORMATO_TEXTO a la llamada de Responses. En PROMPT, cambia “Resume en una frase” por “Extrae revisadas, aprobadas y pendientes de este texto”, conservando los datos de prueba. El diccionario configura el formato; la instrucción define la tarea. La referencia de Responses exige name y schema para json_schema.

Antes de utilizar los datos, comprueba el estado de finalización y que el texto tenga contenido. Después analiza el JSON y valida los tipos, los campos y las reglas de tu aplicación. En este caso, verifica además que los totales correspondan al texto de entrada. json.loads() comprueba que el texto se pueda interpretar como JSON; la validación del esquema y del contenido requiere comprobaciones adicionales.

La salida JSON del mensaje y los esquemas de argumentos de una función son mecanismos distintos. El modo strict de Tool Calls tiene sus propias condiciones; no lo confundas con el formato de salida de Responses.

Búsqueda web y herramientas integradas

En Responses, web_search está documentada para deepseek-v4-pro y se ignora con deepseek-flash. Entre las herramientas custom, solo se admite apply_patch. Otros tipos integrados, como file_search, code_interpreter, computer_use y mcp, se ignoran según la tabla de compatibilidad de herramientas.

Tabla de compatibilidad de herramientas en DeepSeek Responses API y restricciones de web_search según el modelo.

Si tu aplicación usa Chat Completions, puedes implementar una función de búsqueda y devolver sus resultados al modelo. La disponibilidad de la búsqueda integrada en Responses debe distinguirse de los endpoints independientes Search & Fetch. Elige el mecanismo que encaje con el control y los datos que necesita tu aplicación.

Imágenes: revisa el modelo además del endpoint

DeepSeek documenta entrada de imágenes con deepseek-flash en ambas interfaces. Chat Completions usa bloques de imagen dentro de los mensajes; Responses utiliza contenido input_image. Sigue la guía oficial de visión para preparar el contenido y comprobar sus límites.

Una imagen subida y referenciada mediante file_id no implica compatibilidad general con documentos PDF ni con cualquier tipo de archivo. Antes de migrar una entrada multimodal, verifica por separado el modelo, el formato del contenido y el tipo de archivo admitido.

Parámetros que debes revisar antes de migrar

DeepSeek advierte que los parámetros no soportados de Responses pueden ignorarse sin producir un error. Por ello, HTTP 200 no confirma que cada opción enviada haya tenido efecto. Contrasta la solicitud con la matriz oficial de compatibilidad.

Opción o comportamientoQué debes comprobar
Persistencia y segundo planoprevious_response_id, conversation, store y background no habilitan esas funciones.
Recorte automáticotruncation no está soportado. Gestiona el tamaño del contexto en tu aplicación.
Control de llamadas a herramientasparallel_tool_calls y max_tool_calls se ignoran. Aplica los límites necesarios en tu orquestador.
Estilo y resúmenes de razonamientotext.verbosity no tiene efecto; reasoning.summary no genera un resumen.

Revisa también la jerarquía de instrucciones: DeepSeek trata el rol developer de Responses como user. Para instrucciones de sistema, utiliza instructions o un mensaje de sistema admitido. Una traducción mecánica de roles puede cambiar cómo se interpreta tu solicitud.

Esta tolerancia no se aplica a todos los errores. Un valor inválido en un campo soportado o una estructura mal formada puede producir un rechazo explícito. Valida el comportamiento que necesita tu aplicación, además de la respuesta HTTP.

Costos y rendimiento: qué comparar realmente

La tarifa oficial de DeepSeek distingue modelos y categorías de tokens, además de las condiciones de caché y horario aplicables. El nombre del endpoint, por sí solo, no demuestra que una integración vaya a ser más barata, rápida o precisa.

Para comparar tu caso de uso, mantén constantes el modelo, las instrucciones, la tarea, el modo de razonamiento y el límite de salida. Registra el consumo real y el resultado de cada solicitud. Si usas herramientas de búsqueda u otras operaciones adicionales, revisa también su consumo y las condiciones correspondientes.

Al cambiar de interfaz, actualiza el registro de métricas con los nombres de campos de usage que aparecen en la tabla inicial. Un contador ausente debe registrarse como dato no disponible, no como cero.

La caché de contexto reutiliza prefijos compatibles bajo sus reglas de funcionamiento. Eso no sustituye el envío del historial: la solicitud sigue teniendo que contener el contexto que el modelo necesita.

Checklist para migrar de Chat Completions a Responses

Antes de cambiar una integración, comprueba estos puntos con casos representativos de tu aplicación:

  • Configuración: usa la clave de DeepSeek, la URL base correcta y un modelo compatible.
  • Solicitud: transforma messages en input y coloca las instrucciones de sistema donde corresponda.
  • Generación: adapta el límite de salida y configura explícitamente el razonamiento.
  • Lectura: procesa output según su tipo y comprueba el estado final antes de aceptar el resultado.
  • Continuidad: conserva el historial necesario y las relaciones entre llamadas a herramientas y resultados.
  • Streaming: maneja finalización correcta, respuesta incompleta, fallo e interrupción de la conexión.
  • Métricas: comprueba los campos de consumo y los efectos reales de las opciones que necesitas.

Cómo reconocer los fallos de migración más comunes

SíntomaQué revisar primero
El segundo turno pierde los datos anteriores.Inspecciona el input enviado. Debe incluir el historial necesario, sin depender de previous_response_id.
Llega texto, pero la interfaz queda esperando.Comprueba que el lector de Responses reconoce sus eventos finales y no espera [DONE].
La respuesta llega, pero tu código no encuentra el texto.Revisa si sigues leyendo choices después de migrar a Responses, o si la salida contiene una llamada a herramienta.
Una opción parece no hacer nada.Consulta si figura como ignorada o sin efecto en la tabla oficial de compatibilidad.
Aparece HTTP 400 o 422 después del cambio.Revisa los nombres, valores y estructuras de los campos enviados, el contexto y los identificadores de herramientas.

Los errores de autenticación, saldo y límites requieren otro diagnóstico: DeepSeek documenta 401 para autenticación, 402 para saldo insuficiente y 429 para límites de solicitudes o concurrencia. Consulta su referencia de códigos de error cuando el problema sea del servicio o de la cuenta.

Verificación documental: 10 de noviembre de 2026. Los ejemplos se revisaron con las referencias oficiales indicadas. El archivo Python se comprobó localmente a nivel de sintaxis; no se ejecutaron solicitudes autenticadas ni pruebas comparativas de rendimiento.