Ir al contenido
tendencias

Guías

Cómo hacer tu primera petición a la API de IA en Python

Aprende a conectar Python con la API de OpenAI y OpenRouter: instalación en Windows, venv, streaming, control de errores y ejemplo práctico en Node.js.

Texto: 14 min de lectura

👁 112 lecturas

Tu primera llamada a un modelo de IA en Python requiere apenas diez líneas de código: instalas la librería openai, indicas la dirección del endpoint y tu clave, envías la lista de mensajes y lees la respuesta. A continuación te explico todo el proceso, desde la instalación de Python en Windows y la configuración del entorno virtual, hasta el streaming, la gestión de errores y los reintentos automáticos. He ejecutado cada script el 4 de octubre de 2026 en Windows 11 con Python 3.13.15 a través de OpenRouter, mostrando el resultado real obtenido. Este es el tercer paso de la serie sobre la API de IA desde cero.

Entorno de pruebas (4 de octubre de 2026)

  • Windows 11 Pro, Python 3.13.15, librerías: openai 3.24.0, python-dotenv 1.2.4, pydantic 2.13.5.
  • Node.js 26.10 con la librería openai 7.27.0 (versión en JavaScript al final del artículo).
  • Acceso a modelos: OpenRouter, modelo predeterminado openai/gpt-6-luna (aprox. 0,0894 EUR / 0,4472 EUR por millón de tokens; 0,10 / 0,50 USD, según el tipo de referencia del BCE del 12/10/2026: 1 EUR = 1,1181 USD, ver BCE).
  • Coste total de todas las ejecuciones realizadas para este texto: unos 0,0045 EUR (0,005 USD).

Paso 1. Instalar Python en Windows

  1. Entra en python.org/downloads y descarga el instalador oficial más reciente para Windows (Python 3.13 o posterior; la librería openai requiere al menos Python 3.10).
  2. En la primera ventana del instalador, marca la casilla Add python.exe to PATH. Sin esto, la terminal no encontrará Python al escribir su comando, lo que supone el tropiezo más habitual para principiantes.
  3. Alternativa si prefieres la consola: winget install Python.Python.3.13.
  4. Abre una nueva terminal (PowerShell o Windows Terminal) y verifica la instalación con: python --version. Deberías ver el número de versión y no una redirección a Microsoft Store.

Si en lugar de mostrar la versión se abre Microsoft Store, ve a Configuración → Aplicaciones → Configuración avanzada de aplicaciones → Alias de ejecución de aplicaciones y desactiva los alias para python.exe y python3.exe.

Paso 2. Configurar el entorno virtual (venv)

Un entorno virtual (venv) es un espacio aislado para las librerías de un proyecto concreto. Gracias a él, instalar dependencias para el proyecto A no rompe las del proyecto B. Se crea una sola vez por proyecto:

mkdir mi-api
cd mi-api
python -m venv .venv
.venv\Scripts\Activate.ps1
pip install openai python-dotenv

Una vez activado, verás el prefijo (.venv) al inicio de la línea de comandos. Si PowerShell bloquea la ejecución del script de activación, ejecuta una vez: Set-ExecutionPolicy -Scope CurrentUser RemoteSigned. En Git Bash se activa mediante source .venv/Scripts/activate, y en Linux o macOS con source .venv/bin/activate.

Guarda tu clave de acceso en un archivo .env en la raíz del proyecto (una sola línea: OPENROUTER_API_KEY=sk-or-v1-...). Para saber cómo obtenerla y protegerla, consulta la guía sobre claves API y cómo protegerlas.

Paso 3. Primera llamada al modelo

Utilizo la librería oficial de OpenAI pero configurada con el endpoint de OpenRouter. La estructura de las peticiones es idéntica, lo que te permite acceder con una sola clave a modelos de OpenAI, Anthropic, Google, DeepSeek y cientos de alternativas. Cambiar de modelo se reduce a modificar una sola línea de texto.

"""Primera peticion a un modelo de IA mediante OpenRouter (SDK openai)."""
import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()  # carga variables desde el archivo .env en el directorio actual

client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=os.environ["OPENROUTER_API_KEY"],
)

# Nota: probamos originalmente la consulta con prompts en polaco
odpowiedz = client.chat.completions.create(
    model=os.getenv("MODEL", "openai/gpt-6-luna"),
    messages=[
        {"role": "system", "content": "Respondes en espanol, de forma breve y concisa."},
        {"role": "user", "content": "Explica en dos frases que es una API."},
    ],
)

print(odpowiedz.choices[0].message.content)
print("---")
print("modelo:", odpowiedz.model)
print("tokens de entrada:", odpowiedz.usage.prompt_tokens)
print("tokens de salida:", odpowiedz.usage.completion_tokens)
szczegoly = odpowiedz.usage.completion_tokens_details
if szczegoly and szczegoly.reasoning_tokens:
    print("  de los cuales tokens de razonamiento:", szczegoly.reasoning_tokens)
koszt = getattr(odpowiedz.usage, "cost", None)  # OpenRouter anade el coste en USD
if koszt is not None:
    print(f"coste: {koszt:.6f} USD")

Desglose de lo que sucede en el código:

  • load_dotenv() lee la clave desde el archivo .env y la transfiere a las variables de entorno del sistema;
  • OpenAI(base_url=..., api_key=...) inicializa el cliente (si omites base_url, se conectará por defecto a los servidores de OpenAI);
  • messages contiene la lista de mensajes: system define las instrucciones generales y user contiene la consulta concreta (más detalles en la guía sobre system prompt, temperatura y ventana de contexto);
  • odpowiedz.choices[0].message.content contiene el texto generado;
  • usage devuelve el recuento de tokens, y OpenRouter añade el campo adicional cost con el importe facturado en dólares.

Resultado obtenido en nuestro equipo con el modelo por defecto GPT-6 Luna (la prueba se ejecutó con prompts en polaco; mostramos la salida original obtenida):

Traducción del original en polaco: "Una API es un conjunto de reglas y herramientas que permiten que diferentes programas se comuniquen entre sí. Define cómo enviar solicitudes y recibir datos o funciones de otro sistema."
---
modelo: openai/gpt-6-luna
tokens de entrada: 40
tokens de salida: 58
coste: 0.000033 USD

(Traducción del texto generado: «Una API es un conjunto de reglas y herramientas que permite a diferentes programas comunicarse entre sí. Define cómo enviar solicitudes y recibir datos o funciones de otro sistema».)

Y esta fue la misma consulta enviada a Gemini 3.8 Flash (modificando únicamente el valor de la variable MODEL, también ejecutada con el prompt original en polaco):

Traducción del original en polaco: "Una API (Interfaz de Programación de Aplicaciones) es un conjunto de reglas que permite a diferentes programas y sistemas comunicarse e intercambiar datos entre sí de forma segura. Actúa como intermediario, permitiendo que una aplicación utilice funciones o recursos de otra sin necesidad de conocer su estructura interna."
---
modelo: google/gemini-3.8-flash
tokens de entrada: 29
tokens de salida: 462
  incluidos tokens de pensamiento: 390
coste: 0.001754 USD

(Traducción del texto generado: «Una API [Interfaz de Programación de Aplicaciones] es un conjunto de reglas que permite a diferentes programas y sistemas comunicarse y compartir datos de forma segura entre sí. Actúa como intermediario, permitiendo que una aplicación utilice funciones o recursos de otra sin necesidad de conocer su estructura interna».)

Observa las métricas: Gemini necesitó 462 tokens de salida para dos frases breves, de los cuales 390 correspondieron a su proceso interno de razonamiento. Estos tokens se cobran exactamente igual que los visibles, por lo que la petición costó unos 0,00157 EUR (0,00175 USD), más de 50 veces más que con Luna. Al presupuestar tus desarrollos, evalúa siempre usage y no solo la longitud de la respuesta final. Para más detalles, consulta el artículo sobre qué son los tokens y cómo se calculan sus costes.

Paso 4. Streaming: respuestas en tiempo real

Sin streaming, debes esperar a que el modelo termine toda la generación para recibir el bloque completo. Con streaming, el texto llega fragmento a fragmento conforme se genera, igual que en la interfaz web de ChatGPT. Resulta imprescindible en aplicaciones conversacionales y donde un usuario observe la pantalla.

"""Streaming: la respuesta llega fragmento a fragmento en tiempo real."""
import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"])

# Nota: probamos originalmente solicitando nombres de blogs de cocina en polaco
strumien = client.chat.completions.create(
    model=os.getenv("MODEL", "openai/gpt-6-luna"),
    messages=[{"role": "user", "content": "Dame 5 ideas de nombres para un blog de cocina. Solo la lista."}],
    stream=True,
    stream_options={"include_usage": True},  # el ultimo fragmento incluira el recuento de tokens
)

for kawalek in strumien:
    if kawalek.choices and kawalek.choices[0].delta.content:
        print(kawalek.choices[0].delta.content, end="", flush=True)
    if kawalek.usage:  # llega al final de la transmision
        print(f"\n--- tokens: {kawalek.usage.prompt_tokens} de entrada, {kawalek.usage.completion_tokens} de salida")

Salida obtenida en nuestra prueba original (ejecutada en polaco):

Traducción del original en polaco:
1. Directo de la sartén
2. Una pizca de sabor
3. Cocina con cabeza
4. Con el tenedor en el plato
5. Puerto culinario
--- tokens: 20 de entrada, 439 de salida

(Traducción de los nombres generados en la prueba: 1. Directo de la sartén, 2. Pizca de sabor, 3. Cocina con cabeza, 4. Tenedor en plato, 5. Refugio culinario.)

Al utilizar streaming, el recuento de consumo no se envía por defecto; es necesario solicitarlo expresamente con el parámetro stream_options={"include_usage": True} para recibirlo en el último fragmento.

Paso 5. Gestión de errores y reintentos (retry)

En un entorno real, las llamadas fallan periódicamente por diversos motivos: credenciales erróneas, erratas en el identificador del modelo, saturación de límites por minuto (error 429) o caídas puntuales del proveedor (errores 5xx). El criterio de actuación es claro:

  • Errores de cliente (400, 401, 403, 404): no reintentar; repetir la petición provocará el mismo fallo, por lo que debes corregir el código.
  • Errores transitorios (429, 500, 502, 503, cortes de red): reintentar con espera exponencial (2 s, 4 s, 8 s) añadiendo una variación aleatoria (jitter) para evitar que múltiples procesos bombardeen el servidor en el mismo milisegundo.
"""Gestion de errores de API: 401, 429, 5xx y reintentos con espera exponencial."""
import os, sys, time, random
from dotenv import load_dotenv
import openai
from openai import OpenAI

load_dotenv()
client = OpenAI(
    # BASE_URL puede apuntar al servidor de pruebas de servidor_429.py
    base_url=os.getenv("BASE_URL", "https://openrouter.ai/api/v1"),
    api_key=os.environ.get("OPENROUTER_API_KEY", ""),
    max_retries=0,   # desactivamos el reintento integrado para ilustrar la logica propia
    timeout=60,      # segundos; evita esperas indefinidas ante caidas de conexion
)


def zapytaj(pytanie, model, proby=4):
    for proba in range(1, proby + 1):
        try:
            r = client.chat.completions.create(model=model, messages=[{"role": "user", "content": pytanie}])
            return r.choices[0].message.content
        except openai.AuthenticationError as e:      # 401 - clave incorrecta o revocada
            sys.exit(f"401: revisa tu clave de API ({e.message})")
        except openai.PermissionDeniedError as e:    # 403 - sin permisos (region, moderacion)
            sys.exit(f"403: acceso denegado ({e.message})")
        except openai.NotFoundError as e:            # 404 - endpoint o modelo inexistente
            sys.exit(f"404: modelo o direccion no encontrados ({e.message})")
        except openai.BadRequestError as e:          # 400 - peticion mal formada
            sys.exit(f"400: corrige la peticion ({e.message})")
        except (openai.RateLimitError, openai.InternalServerError,
                openai.APIConnectionError, openai.APITimeoutError) as e:
            # 429 (limite de peticiones), 5xx (error del servidor), perdida de conexion
            if proba == proby:
                raise
            przerwa = 2 ** proba + random.random()   # 2, 4, 8 s + fraccion aleatoria
            kod = getattr(e, "status_code", "red")
            print(f"[intento {proba}] error {kod}, esperando {przerwa:.1f} s...")
            time.sleep(przerwa)
        except openai.APIStatusError as e:           # otros codigos (ej. 402 si no hay saldo)
            sys.exit(f"{e.status_code}: {e.message}")


if __name__ == "__main__":
    model = sys.argv[1] if len(sys.argv) > 1 else os.getenv("MODEL", "openai/gpt-6-luna")
    print(zapytaj("Cuanto es 17 * 23? Devuelve unicamente el resultado.", model))

Comprobé cuatro situaciones distintas. Una petición estándar devuelve:

391

Una clave incorrecta y un modelo inexistente (observa que OpenRouter devuelve un error 400 y no 404 cuando el identificador del modelo no es válido):

401: revisa tu clave de API (Error code: 401 - {'error': {'message': 'User not found.', 'code': 401}})
400: corrige la peticion (Error code: 400 - {'error': {'message': 'openai/gpt-7-nie-istnieje is not a valid model ID', 'code': 400}})

Reproducir un error 429 de forma controlada es complejo sin saturar intencionadamente una cuenta real. Por ello, preparé un servidor simulado local que rechaza las dos primeras llamadas con código 429 y acepta la tercera. El cliente opera sin saber que se trata de un entorno emulado:

"""Servidor simulado: devuelve error 429 en las dos primeras llamadas y 200 en la tercera.
Permite verificar la logica de reintentos sin incurrir en costes de API.

Terminal 1: python testy/servidor_429.py
Terminal 2: set BASE_URL=http://127.0.0.1:8429/v1   (PowerShell: $env:BASE_URL="http://127.0.0.1:8429/v1")
            python 03_errores_y_reintentos.py
"""
import json
from http.server import BaseHTTPRequestHandler, HTTPServer

licznik = {"n": 0}


class Obsluga(BaseHTTPRequestHandler):
    def do_POST(self):
        self.rfile.read(int(self.headers.get("Content-Length", 0)))
        licznik["n"] += 1
        if licznik["n"] <= 2:
            kod, tresc = 429, {"error": {"message": "Rate limit exceeded (test)", "code": 429}}
        else:
            kod, tresc = 200, {
                "id": "test-1", "object": "chat.completion", "created": 0, "model": "test",
                "choices": [{"index": 0, "finish_reason": "stop",
                             "message": {"role": "assistant", "content": "391"}}],
                "usage": {"prompt_tokens": 10, "completion_tokens": 1, "total_tokens": 11},
            }
        body = json.dumps(tresc).encode()
        self.send_response(kod)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)
        print(f"peticion {licznik['n']} -> {kod}")


HTTPServer(("127.0.0.1", 8429), Obsluga).serve_forever()

Resultado al ejecutar el script frente al simulador:

[intento 1] error 429, esperando 2.8 s...
[intento 2] error 429, esperando 4.2 s...
391

La lógica de reintento funcionó: tras dos rechazos con pausas de 2,8 s y 4,2 s, el tercer intento completó la operación. El SDK oficial de OpenAI incluye reintentos automáticos por defecto (2 intentos mediante el parámetro max_retries), el cual desactivé en el ejemplo para ilustrar el mecanismo. En desarrollos productivos habituales suelo delegar en la librería configurando max_retries=5.

Tres recomendaciones prácticas fundamentales:

  • Define siempre un timeout. Por defecto, la librería puede esperar hasta 10 minutos. Con modelos de razonamiento avanzado 60 segundos pueden quedarse cortos en respuestas extensas, así que ajústalo según tu caso de uso.
  • Registra el coste de cada llamada. Guardar el campo usage.cost en un registro de logs aporta más claridad tras una semana de uso que cualquier estimador teórico.
  • Distingue el error 429 por saturación del 429 por falta de saldo. En la plataforma de OpenAI, agotar el límite de gasto mensual asignado devuelve también un error 429 (código project_spend_limit_exceeded), el cual ningún reintento automático podrá solucionar.

Cómo hacer lo mismo en JavaScript (Node.js)

El cliente oficial de OpenAI para Node.js comparte prácticamente la misma nomenclatura. Instalé Node.js 26.10, ejecuté npm install openai dotenv en el directorio (versiones 7.27.0 y 18.0.5) y configuré "type": "module" en el archivo package.json. El siguiente script cubre llamada estándar, streaming y captura de fallos:

// Primera peticion en Node.js mediante OpenRouter: respuesta estandar y streaming.
// Ejecutar en la carpeta js: npm install   y luego   node primera_peticion.mjs
import "dotenv/config";          // carga .env desde el directorio de trabajo
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://openrouter.ai/api/v1",
  apiKey: process.env.OPENROUTER_API_KEY,
});
const model = process.env.MODEL ?? "openai/gpt-6-luna";

// 1. Peticion estandar (probada originalmente en polaco)
const r = await client.chat.completions.create({
  model,
  messages: [
    { role: "system", content: "Respondes de forma breve." },
    { role: "user", content: "En que se diferencia JavaScript de Java? Una sola frase." },
  ],
});
console.log(r.choices[0].message.content);
console.log(`--- ${r.usage.prompt_tokens} + ${r.usage.completion_tokens} tokens, coste ${r.usage.cost} USD\n`);

// 2. Streaming (probada originalmente solicitando rios en polaco)
const strumien = await client.chat.completions.create({
  model,
  messages: [{ role: "user", content: "Nombra 3 rios. Solo los nombres separados por comas." }],
  stream: true,
});
for await (const kawalek of strumien) {
  process.stdout.write(kawalek.choices[0]?.delta?.content ?? "");
}
console.log();

// 3. Gestion de errores: modelo inexistente
try {
  await client.chat.completions.create({ model: "no/existe", messages: [{ role: "user", content: "test" }] });
} catch (e) {
  if (e instanceof OpenAI.APIError) console.log(`Error ${e.status}: ${e.message}`);
  else throw e;
}

Salida registrada en la consola durante nuestra prueba (ejecutada originalmente con prompts en polaco):

Traducción del original en polaco: "JavaScript es un lenguaje dinámico utilizado principalmente en navegadores y aplicaciones web, y Java es un lenguaje independiente y estáticamente tipado compilado para ejecutarse en la JVM."
--- 39 + 54 tokens, coste 0.0000309 USD

Vístula, Óder, Varta
Error 400: 400 inscripción corrupta en la prueba original en polaco is not a valid model ID

(Traducción del texto generado: «JavaScript es un lenguaje dinámico utilizado principalmente en navegadores y aplicaciones web, mientras que Java es un lenguaje independiente, de tipado estático y compilado para ejecutarse en la JVM». Ríos obtenidos: Vístula, Óder, Varta. Error 400: identificador de modelo no válido.)

Las diferencias frente a la sintaxis de Python son mínimas: baseURL en camelCase en lugar de base_url, uso de await antes de la llamada asíncrona y la estructura for await para iterar sobre el flujo. La clave en Node.js también debe conservarse en el archivo .env y jamás incluirse en código cliente visible en el navegador.

Scripts listos para descargar

Todos los ejemplos de este artículo forman parte del repositorio de inicio disponible en api-starter con instrucciones de configuración y un archivo .env.example. Solo necesitas clonar o descomprimir los archivos, introducir tu clave y ejecutar python 01_primera_peticion.py.

Siguientes pasos

Preguntas frecuentes

¿Es obligatorio usar OpenRouter para conectar con OpenAI?

No. Si tienes una clave directa emitida por OpenAI, elimina la línea base_url y especifica el modelo sin prefijo (por ejemplo, gpt-6-luna). Encontrarás ejemplos de conexión directa con OpenAI, Anthropic y Google en la guía sobre claves API.

Aparece el error ModuleNotFoundError: No module named ‘openai’. ¿Cómo se soluciona?

Has instalado la librería en un entorno de Python diferente al que está ejecutando el script. Activa el entorno virtual correspondiente (.venv\Scripts\Activate.ps1) y vuelve a instalar con pip, o ejecuta el script directamente mediante la ruta absoluta: .venv\Scripts\python.

¿Por qué salta KeyError: ‘OPENROUTER_API_KEY’?

El intérprete no localiza la variable. Comprueba que el archivo se llame exactamente .env (Windows suele añadir la extensión oculta .txt por error), que se encuentre en la raíz desde la que lanzas el comando y que no contenga espacios antes o después del signo igual.

¿Debo utilizar Chat Completions o Responses API?

OpenAI promueve Responses API para integraciones nuevas, pero Chat Completions sigue plenamente vigente y constituye el estándar de compatibilidad adoptado por OpenRouter, DeepSeek, Mistral y servidores locales como Ollama. Para aprender y mantener portabilidad entre proveedores, Chat Completions sigue siendo la opción más versátil.

¿Cuánto cuesta aprender a utilizar estas API?

Cantidades mínimas. Todas las pruebas realizadas para redactar esta guía supusieron un coste aproximado de medio céntimo de dólar (unos 0,0045 EUR). Además, los modelos gratuitos señalizados en OpenRouter (con la etiqueta :free) y las capas sin coste de Gemini permiten practicar sin realizar ningún desembolso.

Google · Tus fuentesBrilla más alto en tu Google - con un solo clicAñadir a fuentes preferidas →

Fuentes y fecha de verificación

Kinetyra

// read next

More on this from FULGOR

lee tambiénguíasAPI de IA: cómo funciona, claves, precios y primera peticiónAprende cómo funciona una API de IA desde cero: claves API, coste por token, tu primera llamada en Python y un paquete de 18 scripts listos para usar.

Más leídos

  1. 1Para quién es GPT-6 Astra y quién debería elegir otro modelo
  2. 2Eleven v4: test en español, precio y comparativa con v4 Turbo
  3. 3Claude Sonnet 5.5 precio: cuánto cuesta en 2026
// mapa del sitio

Esta página en otros idiomas6

fulgor en números 0textos en el archivo0noticias de los últimos 7 días0modelos de vídeo en el observatorio
🎬 Cine IA: Higgsfield AI DoP Presets de Cámara y Calculadora • Código Descuento Higgsfield AI -50% • Higgsfield vs Kling AI
× ‹ ampliación ›