HiAPI
Blog

Tutorial

Cómo usar grok-4.7 a través de la API de HiAPI: curl, Python y una solicitud que funciona

Guía práctica desde la primera solicitud hasta la verificación de la respuesta

Nov 3, 2026
HiAPI 博客教程封面图:多层半透明玻璃平面在浅色背景上折射出克制的冷蓝色带。

Qué necesitas antes de tu primera solicitud

Antes de enviar cualquier cosa a la API, asegúrate de tener cuatro cosas listas. Omitir cualquiera de ellas es la causa más común de fallos en la primera llamada, y a menudo el motivo del fallo no tiene nada que ver con el modelo en sí.

Una cuenta de HiAPI

Necesitas una cuenta válida de HiAPI.ai. Inicia sesión en el panel para gestionar proyectos, claves y uso. Todas las operaciones de este tutorial se realizan a través de la misma interfaz de API unificada, por lo que la clave que crees aquí sirve para todos los modelos que ofrece la plataforma.

Una clave de API

Crea una clave de API en el panel y guárdala en un lugar seguro: una variable de entorno del shell para pruebas, o un gestor de secretos para cualquier cosa que vaya a ejecutarse en producción. Nunca envíes la clave a un repositorio de código ni la pegues en una aplicación cliente.

Para pruebas locales, basta con exportarla una vez por sesión de shell:

export HIAPI_API_KEY="your-api-key-here"

La URL base

La URL base es la dirección raíz de la API de HiAPI. Todas las solicitudes de este tutorial son relativas a ella. Consulta tu panel para confirmar la URL base exacta que debe usar tu cuenta y guárdala en una variable para que los scripts y el código de la aplicación hagan referencia al mismo valor:

export HIAPI_BASE_URL="https://api.hiapi.ai"

El identificador del modelo

El identificador de modelo que se usa en este tutorial es grok-4.7. Se pasa como una cadena simple en el cuerpo de la solicitud. Si el identificador es incorrecto, normalmente recibirás un error explícito en lugar de un fallback silencioso, por lo que los errores tipográficos son fáciles de detectar.

Una vez que estos cuatro elementos (cuenta, clave, URL base e ID del modelo) están listos, tienes todo lo necesario para la forma de la solicitud. El enfoque con curl no requiere instalar ningún SDK, y el enfoque con Python solo necesita el cliente HTTP que probablemente ya tengas.

Comprobación rápida del entorno

Confirma que la clave y la URL base son visibles para el shell que vas a usar:

[ -n "$HIAPI_API_KEY" ] && echo "key set"
[ -n "$HIAPI_BASE_URL" ] && echo "base url set"

Si se imprimen ambas líneas, puedes pasar a la siguiente sección.

La forma de una solicitud a grok-4.7 en HiAPI

Todas las solicitudes de este tutorial comparten los mismos cuatro elementos: el endpoint, la cabecera de autenticación, el campo del modelo y el array messages. Entenderlos por separado hará que más adelante resulte mucho más fácil comprender el streaming y las llamadas a herramientas, ya que estas funciones solo cambian una parte de esta forma.

El endpoint

La llamada consiste en enviar una solicitud POST al endpoint de chat completions bajo la URL base. En este tutorial lo representamos así:

POST {HIAPI_BASE_URL}/v1/chat/completions

Si tu panel muestra una ruta diferente, usa la del panel; en cualquier caso, el cuerpo de la solicitud es el mismo.

La cabecera de autenticación

La autenticación se realiza mediante un bearer token en la cabecera Authorization:

Authorization: Bearer $HIAPI_API_KEY

Como es una cabecera y no un parámetro de consulta, la clave nunca aparece en la URL, en los registros de acceso ni en el historial del navegador.

El tipo de contenido

El cuerpo de la solicitud es JSON, así que envía Content-Type: application/json. Tanto curl como cualquier cliente HTTP de Python pueden configurarlo por ti, pero vale la pena escribirlo explícitamente para que el conjunto de cabeceras no sea ambiguo.

El cuerpo de la solicitud: model y messages

El cuerpo de la solicitud es un objeto JSON que contiene al menos dos campos:

  • model: la cadena del identificador del modelo, aquí grok-4.7.
  • messages: un array ordenado de objetos de mensaje. Cada objeto tiene role y content. Los roles habituales son: system para las instrucciones que enmarcan la conversación, user para la solicitud en sí, y assistant para proporcionar turnos previos del modelo cuando continúas una conversación.

El cuerpo mínimo de una solicitud es el siguiente:

{
  "model": "grok-4.7",
  "messages": [
    { "role": "system", "content": "You are a concise technical assistant." },
    { "role": "user", "content": "Explain what an idempotent HTTP method is in two sentences." }
  ]
}

Ese es todo el contrato de una llamada básica. Cualquier otra cosa (streaming, definiciones de herramientas) son campos adicionales sobre el mismo objeto, y por eso importan las secciones anteriores: una vez que esta forma es correcta, el resto es adicional.

Cómo se ve la respuesta

La respuesta es un objeto JSON. La parte que casi siempre quieres está en choices, que es un array; cada entrada tiene un message que contiene role y content. Además hay metadatos, como el modelo que procesó la solicitud y un bloque usage que describe el número de tokens. Lee choices[0].message.content y obtendrás el texto de la respuesta. Los demás campos son útiles para el registro y el seguimiento de costes, pero no son necesarios para empezar.

Una solicitud curl que funciona

Con la clave exportada y la forma de la solicitud entendida, la llamada con curl es breve. Esta es la versión que debes copiar cuando quieras aislar el problema de tu código de aplicación.

curl -sS "$HIAPI_BASE_URL/v1/chat/completions" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "grok-4.7",
    "messages": [
      { "role": "system", "content": "You are a concise technical assistant." },
      { "role": "user", "content": "In one sentence, what is a bearer token?" }
    ]
  }'

Qué hace cada flag

  • -sS mantiene la salida silenciosa, pero sigue imprimiendo errores. Sin él, una solicitud fallida podría parecer una respuesta vacía.
  • La URL se compone de la variable de la URL base más la ruta de chat completions, de modo que nunca tengas que codificar el host directamente en el script.
  • -H "Authorization: ..." envía el bearer token. La variable la expande el shell, y por eso era importante exportarla antes.
  • -H "Content-Type: application/json" indica al servidor que el cuerpo de la solicitud es JSON.
  • -d '...' es el cuerpo de la solicitud. Las comillas simples que rodean el JSON impiden que el shell interprete las llaves y comillas que contiene.

Si prefieres poner el cuerpo en un archivo, guárdalo en body.json y sustituye el argumento -d por -d @body.json. Esto suele ser más claro una vez que el array messages se alarga.

Leer el JSON en bruto

La respuesta llega a la salida estándar como un objeto JSON. Cuando quieras revisarla a simple vista, pásala por una herramienta de formateo:

curl -sS "$HIAPI_BASE_URL/v1/chat/completions" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @body.json | python -m json.tool

Deberías ver un array choices cuyo primer elemento contiene un objeto message con una cadena content: esa cadena es la respuesta del modelo. Junto a ella suele haber también un identificador de la respuesta, el nombre del modelo repetido y un objeto usage con el recuento de tokens de prompt y de completion. Anota ahora una respuesta exitosa: será la referencia con la que compararás cuando valides el cliente de Python.

Fallos habituales en esta etapa

Un 401 casi siempre significa que el bearer token falta, tiene un formato incorrecto o no se exportó realmente en el shell actual. Un 400 acompañado de un mensaje sobre el cuerpo de la solicitud suele significar que el JSON tiene un formato incorrecto: las comas finales o las comillas tipográficas pegadas desde la documentación son los culpables habituales. Corrige estos dos puntos antes de mirar cualquier otra cosa.

La misma solicitud con Python

La versión en Python hace lo mismo con el mismo cuerpo de solicitud. La ventaja de reescribirla es pasar de una prueba en el shell a un código que puedes poner en una aplicación: el cuerpo de la solicitud es una estructura de datos en lugar de una cadena entre comillas.

Usar la biblioteca estándar

Si no quieres añadir dependencias, urllib es suficiente:

import json
import os
import urllib.request

api_key = os.environ["HIAPI_API_KEY"]
base_url = os.environ["HIAPI_BASE_URL"]

payload = {
    "model": "grok-4.7",
    "messages": [
        {"role": "system", "content": "You are a concise technical assistant."},
        {"role": "user", "content": "In one sentence, what is a bearer token?"},
    ],
}

request = urllib.request.Request(
    f"{base_url}/v1/chat/completions",
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    },
    method="POST",
)

with urllib.request.urlopen(request) as response:
    body = json.loads(response.read().decode("utf-8"))

print(body["choices"][0]["message"]["content"])

Usar requests

Si requests ya está en tu entorno, la llamada es más breve:

import os
import requests

response = requests.post(
    f"{os.environ['HIAPI_BASE_URL']}/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
    json={
        "model": "grok-4.7",
        "messages": [
            {"role": "system", "content": "You are a concise technical assistant."},
            {"role": "user", "content": "In one sentence, what is a bearer token?"},
        ],
    },
    timeout=60,
)
response.raise_for_status()
print(response.json()["choices"][0]["message"]["content"])

Por qué esta versión es más segura para una aplicación

Fíjate en tres decisiones deliberadas. La clave y la URL base provienen de variables de entorno, nunca de literales en el código. El cuerpo de la solicitud es un diccionario de Python, por lo que una comilla mal colocada produce un SyntaxError inmediato en lugar de un rechazo confuso del lado del servidor. Y la llamada establece un timeout: sin él, una conexión bloqueada podría dejar colgado un proceso worker indefinidamente.

Ejecuta la versión en Python con el mismo prompt que usaste en curl. La redacción de la respuesta puede variar ligeramente entre ejecuciones (el modelo no es determinista), pero la estructura de la respuesta debe ser exactamente la misma: un array choices, un message con content y un bloque usage. Si la estructura coincide, tu cliente está conectado correctamente y el resto de secciones son mejoras opcionales.

Activar el streaming

El streaming cambia la forma en que llegan los tokens, no lo que solicitas. En lugar de esperar la respuesta completa, la recibes progresivamente, y eso es precisamente lo que hace que una interfaz de chat se sienta ágil.

El cambio en la solicitud

Añade un campo al cuerpo de la solicitud que ya tienes:

{
  "model": "grok-4.7",
  "stream": true,
  "messages": [
    { "role": "user", "content": "Count from one to five, one number per line." }
  ]
}

El endpoint, las cabeceras y el resto de messages no cambian. Ese es precisamente el sentido de aprender primero la forma básica: el streaming es un flag adicional, no una segunda API.

Qué se devuelve

Con stream activado, la respuesta ya no es un único objeto JSON. Es una secuencia de server-sent events (SSE). Cada evento transporta una línea data: cuyo payload es un fragmento JSON con la misma forma que una respuesta normal, salvo que choices[0] contiene un objeto delta en lugar de un message completo. Los primeros fragmentos pueden contener solo el rol; los intermedios llevan fragmentos de contenido; y el flujo termina con la línea centinela data: [DONE].

Como cliente, tu tarea es concatenar en orden las cadenas de delta.content y detenerte en la línea centinela. Trata cualquier fragmento sin content como una no-operación, no como un error.

Consumir los fragmentos en Python

import json
import os
import requests

with requests.post(
    f"{os.environ['HIAPI_BASE_URL']}/v1/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
    json={
        "model": "grok-4.7",
        "stream": True,
        "messages": [{"role": "user", "content": "Count from one to five."}],
    },
    stream=True,
    timeout=60,
) as response:
    response.raise_for_status()
    for line in response.iter_lines():
        if not line:
            continue
        line = line.decode("utf-8")
        if not line.startswith("data: "):
            continue
        data = line[len("data: "):]
        if data == "[DONE]":
            break
        delta = json.loads(data)["choices"][0].get("delta", {})
        text = delta.get("content")
        if text:
            print(text, end="", flush=True)

Dos detalles determinan si el streaming funciona o parece colgarse. Pasa stream=True al cliente HTTP para que no almacene en búfer todo el cuerpo de la respuesta, e itera línea por línea en lugar de leer la respuesta entera de una vez. El flush=True en print solo afecta a lo que ves en pantalla; sin él, la salida podría parecer que aparece de golpe al final, aunque los fragmentos se procesen a medida que llegan.

Validar el streaming por separado

No valides el streaming y las llamadas a herramientas en la misma prueba. Confirma primero que el texto concatenado da una respuesta razonable para un prompt simple y luego añade funcionalidades una a una. Si el flujo termina sin la línea centinela, eso es una señal para revisar la conexión, no el modelo.

La opción de llamadas a herramientas

Las llamadas a herramientas permiten que el modelo solicite funciones definidas en tu código, en lugar de limitarse a generar texto. En el lado de la solicitud se añaden exactamente igual que el streaming.

Dónde va la opción

La definición de herramientas es un array tools en el cuerpo de la solicitud. Cada entrada describe un elemento invocable: un nombre, una descripción de cuándo usarlo y una descripción mediante JSON schema de sus parámetros. La descripción es más importante de lo que parece: es la única guía de la que dispone el modelo para decidir cuándo es aplicable una herramienta.

{
  "model": "grok-4.7",
  "messages": [
    { "role": "user", "content": "What is the weather in Lisbon right now?" }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Return the current weather for a named city.",
        "parameters": {
          "type": "object",
          "properties": {
            "city": { "type": "string", "description": "City name, e.g. Lisbon" }
          },
          "required": ["city"]
        }
      }
    }
  ]
}

La forma que debes recordar es type más function, con name, description y parameters anidados. Si ya tienes definiciones de JSON schema, el bloque parameters es el lugar donde deben ir.

Cómo responde el modelo

Cuando el modelo decide que necesita una herramienta, el mensaje del assistant en choices[0] no es texto normal. Su content suele estar vacío y lleva un array tool_calls. Cada entrada tiene un identificador, un nombre de función que coincide con una de tus definiciones y una cadena arguments que contiene JSON. Debes parsear esa cadena de arguments: es una cadena, no un objeto, y tratarla como objeto es una fuente habitual de errores.

Tu código ejecuta entonces la función y envía una solicitud de seguimiento: la conversación original, el mensaje del assistant con tool_calls y un nuevo mensaje con rol tool que contiene el identificador de la llamada y el resultado. El modelo usa ese resultado para generar la respuesta final. El tool_call_id debe coincidir con el identificador del mensaje del assistant; sin él, la solicitud de seguimiento es ambigua.

Medidas de protección prácticas

  • Valida los parámetros parseados con el mismo schema que enviaste. El modelo se guía por el schema, pero no está obligado por él.
  • Trata el nombre de función devuelto como entrada no confiable y enrútalo solo a una lista explícita de funciones permitidas.
  • No actives stream y herramientas a la vez en la primera prueba. Confirma primero el ciclo completo de herramientas sin streaming y, si tu caso de uso necesita ambos, combínalos después.

Verificar la respuesta antes de lanzar a producción

Una llamada que funciona en la terminal no equivale a una integración validada. Recorre esta lista completa y luego conserva el script como prueba de humo.

Confirmar la capa de transporte

Antes de parsear nada, comprueba que el estado HTTP es 200. Un cliente que parsea primero producirá un confuso KeyError cuando el problema real es un 401. Comprueba el estado de forma explícita y, si falla, registra también el cuerpo de la respuesta: el servidor suele indicar qué salió mal.

Confirmar la estructura de la respuesta

Comprueba, no supongas. En una llamada exitosa sin streaming, verifica tres cosas: que choices existe y no está vacío, que choices[0].message.content es una cadena no vacía y que el modelo devuelto en la respuesta coincide con el que enviaste. Esta última comprobación detecta una cantidad sorprendente de errores de configuración, en los que un modelo predeterminado sustituye al que solicitaste.

Ejercitar deliberadamente las rutas de error

No esperes a que ocurra un fallo para saber cómo se comporta tu código. Ejecuta tres pruebas negativas rápidas:

  • Llama sin la cabecera de autenticación y confirma que tu código muestra un error de autenticación explícito en lugar de reintentar indefinidamente.
  • Envía un cuerpo de solicitud sin el campo model y confirma que obtienes un error de cliente en lugar de un bloqueo.
  • Envía un prompt deliberadamente largo y confirma que tu timeout y el manejo de truncamiento se comportan como esperas.

Cada una de estas pruebas debe producir un fallo concreto y registrado, no un genérico «algo salió mal».

Validar las funciones adicionales por separado

Si activaste el streaming, confirma que los fragmentos concatenados forman una respuesta coherente y que la línea centinela termina el bucle. Si activaste herramientas, confirma que el ciclo se completa: la llamada a la herramienta se parsea, la función se ejecuta, el resultado vuelve con un identificador coincidente y se produce la respuesta final en texto. Coloca cada validación en su propio script pequeño, de modo que un fallo apunte a una única funcionalidad.

Hacer que las comprobaciones sean repetibles

Reúne toda la secuencia en un archivo, haz que salga con un código distinto de cero si falla cualquier comprobación y ejecútalo contra tu configuración de staging antes de cada despliegue. Debería tardar solo unos segundos y cubrir la llamada básica, una llamada con streaming y, si usas herramientas, también un ciclo completo de herramientas. Cuando pase, podrás confiar en que los fallos en producción se deben a la solicitud que envías y no a la infraestructura subyacente.

Una vez que la comprobación pasa, ejecuta la llamada con curl una vez más. Si el JSON en bruto coincide con la salida de tu aplicación, la integración está terminada.

Artículos relacionados

Ver todos los artículos →

Empieza a crear con artefactos de IA persistentes

Consigue tu API key y llama a modelos de imagen, vídeo y audio a través de una única API basada en tasks.

Leer la documentación de la API