Lección 1.2 · 25 min · Gratis

Tu primera llamada a la Agent API

Lucía ya entiende por qué un modelo que busca es distinto de uno que solo recuerda. Ahora quiere ver uno funcionando con una pregunta de su trabajo real: qué se ha publicado esta semana sobre la miel mexicana. En esta lección creas la clave, el proyecto y la primera llamada, y aprendes a leer todo lo que regresa la API, no solo el texto.

Al final tendrás un módulo faro.py que vas a reutilizar y ampliar durante el curso.

Al terminar podrás:

  • crear una clave en la consola de Perplexity y guardarla fuera del código;
  • montar un proyecto de Python con uv y el SDK perplexityai;
  • hacer una llamada a responses.create con un preset;
  • leer output_text, recorrer output, revisar status y consultar usage;
  • usar el SDK de OpenAI contra la Agent API si tu proyecto ya depende de él.

Paso 1: cuenta, créditos y clave

Entra a console.perplexity.ai y crea una cuenta. La consola organiza el trabajo en proyectos; cada proyecto tiene sus claves, su consumo y su facturación. Ve a la página API Keys del proyecto y genera una clave nueva.

La API se cobra por uso: no hay suscripción, pero necesitas saldo. En la sección de facturación (Billing) puedes comprar créditos y, si quieres, activar la recarga automática. Para este curso basta con una cantidad pequeña; en la lección 4.1 vemos cómo estimar el gasto antes de llamar.

Un detalle útil: según la guía de preguntas frecuentes, un error 401 no solo aparece con una clave equivocada o borrada, también cuando la cuenta se quedó sin créditos. Si de pronto todo falla con 401, revisa el saldo antes de cambiar la clave.

Tres reglas que no se negocian:

  1. La clave nunca va dentro de un archivo .py.
  2. La clave nunca se sube a Git, ni se pega en un chat, ni aparece en una captura de pantalla.
  3. Si sospechas que se filtró, la revocas en la consola y generas otra.

Paso 2: el proyecto con uv

uv es un gestor de proyectos y paquetes de Python. Con uv instalado, en una terminal:

uv init faro-investigacion
cd faro-investigacion
uv add perplexityai python-dotenv pydantic

Ojo con el nombre: el paquete se instala como perplexityai, pero se importa como perplexity. En PyPI, la versión vigente en octubre de 2026 (0.43.8) pide Python 3.10 o superior. pydantic lo usaremos para validar JSON en la lección 2.3. Si prefieres pip, pip install perplexityai hace lo mismo.

Paso 3: la clave en .env

Dentro de faro-investigacion crea un archivo .env:

PERPLEXITY_API_KEY=pplx-tu-clave

Y agrégalo a .gitignore:

.env

El cliente Perplexity() busca la clave en la variable de entorno PERPLEXITY_API_KEY cuando no le pasas api_key. Con python-dotenv cargamos esa variable desde el archivo.

Paso 4: la primera llamada

Crea primera_llamada.py:

# primera_llamada.py
from dotenv import load_dotenv
from perplexity import Perplexity

PRESET = "fast"

load_dotenv()
cliente = Perplexity()  # lee PERPLEXITY_API_KEY

respuesta = cliente.responses.create(
    preset=PRESET,
    input=(
        "¿Qué noticias se publicaron esta semana sobre la exportación de miel "
        "mexicana? Responde en español, en cinco viñetas como máximo."
    ),
)

print(respuesta.output_text)
print("---")
print("id:", respuesta.id)
print("estado:", respuesta.status)
print("modelo que respondió:", respuesta.model)

Ejecútalo:

uv run primera_llamada.py

Fíjate en lo que no escribiste: no elegiste modelo, no activaste la búsqueda, no explicaste cómo citar. Todo eso lo trae el preset fast. Un preset es una configuración que Perplexity mantiene: un modelo, unas instrucciones de sistema, unas herramientas (en este caso, web_search) y unos límites. En la lección 1.3 los comparamos todos.

El campo model de la respuesta te dice qué modelo usó el preset en ese momento. No lo copies en tu código como si fuera fijo: Perplexity actualiza los presets cuando sus evaluaciones encuentran una configuración mejor.

Paso 5: entiende la respuesta

output_text es un atajo cómodo: junta todo el texto que el modelo escribió. Pero la respuesta trae mucho más.

output es la traza de lo que pasó

respuesta.output es una lista de elementos con un campo type. En una llamada con búsqueda verás al menos dos:

  • un elemento search_results, con la lista results de páginas consultadas (cada una con id, title, url, snippet, date, last_updated y source);
  • un elemento message, con el texto de la respuesta dentro de content.

Según la configuración puede haber otros, como los resultados de fetch_url cuando el modelo abre una página completa. Recorre la lista para ver qué ocurrió:

for elemento in respuesta.output or []:
    print("tipo:", elemento.type)
    if elemento.type == "search_results":
        for r in elemento.results:
            print(f"  [{r.id}] {r.title} ({r.date}) {r.url}")

Este recorrido es la base de todo lo que haremos con fuentes en el módulo 2. El texto te dice qué afirma el modelo; search_results te dice qué tenía enfrente cuando lo afirmó.

status dice si terminó bien

Los valores posibles son completed, failed, incomplete, in_progress, queued y cancelled. En una llamada normal (sin segundo plano) esperas completed. Si es failed, revisa respuesta.error. Si es incomplete, la respuesta se detuvo antes de terminar, por ejemplo por un tope de tokens: no la uses como si estuviera completa.

usage dice cuánto consumiste

respuesta.usage incluye input_tokens, output_tokens y total_tokens, detalles de caché en input_tokens_details, el desglose de herramientas en tool_calls_details y, cuando está disponible, un objeto cost con input_cost, output_cost, tool_calls_cost y total_cost en dólares. Ese último campo es oro: te ahorra calcular a mano con la tabla de precios.

uso = respuesta.usage
print("tokens de entrada:", uso.input_tokens)
print("tokens de salida:", uso.output_tokens)
costo = getattr(uso, "cost", None)
if costo is not None:
    print(f"costo total: {costo.total_cost} {getattr(costo, 'currency', 'USD')}")

Usamos getattr porque el desglose de costo puede no venir en todas las respuestas. Tu código no debe romperse por eso.

Si algo sale mal

El SDK lanza excepciones específicas, todas accesibles desde el módulo perplexity:

  • AuthenticationError (401): clave incorrecta, revocada o cuenta sin créditos.
  • RateLimitError (429): superaste el límite de peticiones de tu nivel de uso. Espera y reintenta con pausas crecientes.
  • APIConnectionError: problema de red; no llegaste al servidor.
  • APIStatusError: cualquier otro error HTTP (400 por un parámetro mal escrito, 5xx por fallas del servicio). Tiene status_code y response.
import perplexity

try:
    respuesta = cliente.responses.create(preset=PRESET, input="Hola")
except perplexity.AuthenticationError:
    print("Revisa tu clave y tu saldo en la consola.")
except perplexity.RateLimitError:
    print("Demasiadas peticiones: espera unos segundos y reintenta.")
except perplexity.APIConnectionError:
    print("No hubo conexión con la API.")
except perplexity.APIStatusError as e:
    print("Error de la API:", e.status_code)

El orden importa: AuthenticationError y RateLimitError son casos particulares de APIStatusError, así que van antes. En la lección 4.1 agregamos reintentos automáticos.

Una versión reutilizable

Crea faro.py. Este módulo crece durante el curso:

# faro.py
import perplexity
from dotenv import load_dotenv
from perplexity import Perplexity

PRESET = "fast"

load_dotenv()
cliente = Perplexity()


def resultados_de(respuesta) -> list:
    """Devuelve los resultados de búsqueda de todos los elementos search_results."""
    vistos = {}
    for elemento in respuesta.output or []:
        if getattr(elemento, "type", None) == "search_results":
            for r in elemento.results:
                vistos.setdefault(r.url, r)
    return list(vistos.values())


def costo_de(respuesta) -> float | None:
    costo = getattr(respuesta.usage, "cost", None) if respuesta.usage else None
    return getattr(costo, "total_cost", None) if costo else None


def preguntar(pregunta: str, preset: str = PRESET) -> str:
    respuesta = cliente.responses.create(preset=preset, input=pregunta)
    if respuesta.status != "completed":
        print(f"Aviso: la respuesta terminó con estado {respuesta.status}")
    print(f"[{respuesta.id}] modelo={respuesta.model} costo={costo_de(respuesta)}")
    for r in resultados_de(respuesta):
        print(f"  fuente: {r.title} | {r.url}")
    return respuesta.output_text


if __name__ == "__main__":
    try:
        print(preguntar("¿Qué dijo esta semana la prensa mexicana sobre el precio de la miel?"))
    except perplexity.APIStatusError as e:
        print("Error de la API:", e.status_code)

resultados_de elimina duplicados por URL, porque una misma página puede aparecer en más de una ronda de búsqueda.

La alternativa con el SDK de OpenAI

La Agent API sigue el formato de la Responses API de OpenAI. Si tu proyecto ya usa el paquete openai, basta con cambiar la URL base a https://api.perplexity.ai/v1 y usar tu clave de Perplexity. El SDK de OpenAI envía la petición a /v1/responses, que Perplexity acepta como alias de /v1/agent.

# con_openai.py
import os

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
cliente = OpenAI(
    api_key=os.environ["PERPLEXITY_API_KEY"],
    base_url="https://api.perplexity.ai/v1",
)

respuesta = cliente.responses.create(
    input="Resume en tres viñetas las noticias recientes sobre miel en México.",
    extra_body={"preset": "fast"},  # el SDK de OpenAI no conoce este campo
)
print(respuesta.output_text)

El parámetro preset no existe en el SDK de OpenAI, por eso va en extra_body. La documentación de Perplexity recomienda su propio SDK para proyectos nuevos; el de OpenAI tiene sentido cuando quieres cambiar de proveedor con el menor cambio posible. Para usarlo, instala el paquete con uv add openai.

Práctica

Dedica 25 minutos y no más de 10 llamadas.

  1. Crea tu clave, guárdala en .env, confirma que .env está en .gitignore y ejecuta primera_llamada.py.
  2. Agrega el recorrido de output y anota qué tipos de elemento aparecen y cuántos resultados de búsqueda trae.
  3. Imprime respuesta.usage completo. Anota tokens de entrada, de salida y el costo total.
  4. Ejecuta la misma pregunta dos veces seguidas. Compara las URLs de search_results: ¿son las mismas? ¿El texto cambió?
  5. Con faro.py, haz tres preguntas de la práctica de la lección 1.1. Para cada una, abre al menos una fuente y comprueba si dice lo que la respuesta afirma.

Resumen

  • La clave se genera en la consola de Perplexity, se guarda en PERPLEXITY_API_KEY y nunca entra al código ni a Git.
  • El paquete se instala como perplexityai y se importa como perplexity; el cliente es Perplexity().
  • responses.create(preset="fast", input=...) activa búsqueda, modelo e instrucciones de citas en una sola línea.
  • output_text junta el texto; output es la traza con search_results y message; status y usage te dicen si terminó bien y cuánto costó.
  • El SDK de OpenAI funciona contra https://api.perplexity.ai/v1, con preset dentro de extra_body.

Quiz

1. ¿Cómo se importa el SDK oficial después de instalar perplexityai?
2. Tu llamada devolvió status igual a incomplete. ¿Qué haces con el texto?
3. ¿Dónde están las páginas que el modelo consultó?
4. Todas tus llamadas empiezan a fallar con 401 aunque la clave no cambió. ¿Qué revisas primero?

Esta lección es gratuita. El curso completo incluye todos los módulos, quizzes, plantillas y un proyecto final con certificado. Ver precios