Felo APIs
Chat API
Integra búsqueda conversacional con IA, con análisis de consultas, recursos web y citas.
/v2/chatAutenticación
Bearer API key
Tipo de contenido
application/json
Notas sobre límites
Per API key and per user windows. Read X-RateLimit headers.
La Chat API te permite integrar búsqueda conversacional impulsada por IA en tus aplicaciones. Envía una consulta en lenguaje natural y recibe una respuesta inteligente generada a partir de resultados de búsqueda web en tiempo real.
Para conocer los límites de solicitudes, las respuestas de error y el comportamiento de reintento, consulta Errores, límites y reintentos.
Descripción general
La Chat API realiza las siguientes operaciones:
- Analiza y optimiza tu consulta
- Busca en la web información relevante
- Clasifica y filtra los resultados de búsqueda
- Genera una respuesta completa de IA basada en las fuentes
- Devuelve la respuesta junto con las citas de las fuentes
Casos de uso:
- Asistentes de IA conversacionales
- Herramientas de investigación y recuperación de información
- Chatbots de atención al cliente con conocimiento web
- Aplicaciones educativas
- Herramientas de creación de contenido
Autenticación
Todas las solicitudes a la Chat API requieren autenticación mediante una clave de API. Incluye tu clave de API en el encabezado Authorization:
Authorization: Bearer YOUR_API_KEYConsulta la guía de autenticación para obtener detalles sobre cómo obtener y usar tu clave de API.
Uso con agentes de IA
Instala el paquete completo felo-ai cuando quieras que un agente use Search y todas las demás Felo Skills disponibles. El instalador descubre dinámicamente todos los directorios del paquete que contengan SKILL.md, por lo que incluye las Skills futuras en lugar de un subconjunto fijo. Instálalas en el directorio nativo de tu agente: Codex usa ~/.codex/skills, Claude Code usa ~/.claude/skills, OpenClaw usa ~/.agents/skills y Hermes Agent usa ~/.hermes/skills. Consulta la guía de configuración manual de agentes de IA para conocer los pasos de enlace seguro, clave de API y verificación de cada plataforma.
Endpoint
POST https://openapi.felo.ai/v2/chatSolicitud
Encabezados
| Encabezado | Obligatorio | Descripción |
|---|---|---|
Authorization | Sí | Tu clave de API con el formato: Bearer YOUR_API_KEY |
Content-Type | Sí | Debe ser application/json |
Parámetros del cuerpo
| Parámetro | Tipo | Obligatorio | Descripción | Restricciones |
|---|---|---|---|---|
query | string | Sí | La pregunta del usuario o la consulta de búsqueda | De 1 a 2000 caracteres |
Solicitud de ejemplo
{
"query": "What are the latest developments in quantum computing?"
}Respuesta
Respuesta correcta (200 OK)
Cuando la solicitud se completa correctamente, la API devuelve una respuesta JSON con la siguiente estructura:
{
"status": "ok",
"message": null,
"data": {
"id": "HabCj883yHLSXc8mWqu4Eq",
"message_id": "18ea8517-5559-4f48-a355-1f8a79e73b71",
"answer": "Recent developments in quantum computing include...",
"query_analysis": {
"queries": [
"quantum computing latest developments 2025",
"quantum computing breakthroughs"
]
},
"resources": [
{
"link": "https://example.com/quantum-news",
"title": "Latest Quantum Computing Breakthroughs",
"snippet": "Scientists have achieved a major milestone..."
},
{
"link": "https://example.com/quantum-research",
"title": "Quantum Computing Research 2025",
"snippet": "New quantum algorithms show promise..."
}
]
}
}Campos de la respuesta
| Campo | Tipo | Descripción |
|---|---|---|
status | string | number | Estado de la respuesta: "ok" en caso de éxito; las respuestas de error usan el código de estado HTTP como número |
message | string | null | Mensaje adicional (null en las solicitudes correctas) |
data | object | Datos de la respuesta (solo presentes cuando el estado es "ok") |
data.id | string | Identificador único de esta sesión de chat |
data.message_id | string | Identificador único de este mensaje concreto |
data.answer | string | Respuesta generada por IA a la consulta |
data.query_analysis | object | Análisis de la consulta del usuario |
data.query_analysis.queries | string[] | Consultas de búsqueda optimizadas derivadas de la entrada del usuario |
data.resources | array | Lista de fuentes web usadas para generar la respuesta |
data.resources[].link | string | URL de la fuente |
data.resources[].title | string | Título de la página de la fuente |
data.resources[].snippet | string | Extracto relevante de la fuente |
Respuesta de error
Cuando se produce un error, la API devuelve el sobre de error compartido:
{
"status": 401,
"code": "UNAUTHORIZED",
"message": "API Key is required",
"request_id": "..."
}Campos de la respuesta de error
| Campo | Tipo | Descripción |
|---|---|---|
status | number | Código de estado HTTP; las respuestas correctas usan "ok" |
code | string | Código de error que identifica el tipo de error |
message | string | Descripción del error legible por personas |
request_id | string | Identificador único de esta solicitud (útil para soporte) |
Códigos de error
| HTTP | Código | Significado |
|---|---|---|
| 400 | Código de validación del endpoint | Falta el campo query, está vacío o es demasiado largo; lee message para conocer el problema concreto |
| 401 | UNAUTHORIZED | La clave de API falta, no es válida o está revocada |
| 429 | RATE_LIMITED | Se superó un límite de solicitudes |
| 502 | CHAT_FAILED | Error interno del servicio |
| 503 | SERVICE_UNAVAILABLE | Servicio temporalmente no disponible |
Para consultar el sobre compartido, otros códigos de error y las reglas de reintento, consulta Errores, límites y reintentos.
Ejemplos
cURL
curl -X POST https://openapi.felo.ai/v2/chat \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "What are the benefits of renewable energy?"
}'Respuesta:
{
"status": "ok",
"message": null,
"data": {
"id": "HabCj883yHLSXc8mWqu4Eq",
"message_id": "18ea8517-5559-4f48-a355-1f8a79e73b71",
"answer": "Renewable energy offers numerous benefits including reduced greenhouse gas emissions, decreased air pollution, energy independence, job creation, and long-term cost savings. Solar, wind, and hydroelectric power are sustainable alternatives to fossil fuels that help combat climate change while providing reliable electricity.",
"query_analysis": {
"queries": [
"renewable energy benefits",
"advantages of renewable energy sources"
]
},
"resources": [
{
"link": "https://example.com/renewable-benefits",
"title": "Top Benefits of Renewable Energy",
"snippet": "Renewable energy sources provide clean, sustainable power..."
}
]
}
}Python
import requests
import os
# Get API key from environment variable
api_key = os.environ.get('FELO_API_KEY')
url = 'https://openapi.felo.ai/v2/chat'
headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}
data = {
'query': 'What are the benefits of renewable energy?'
}
try:
response = requests.post(url, headers=headers, json=data)
response.raise_for_status() # Raise exception for HTTP errors
result = response.json()
if result['status'] == 'ok':
print('Answer:', result['data']['answer'])
print('\nSources:')
for resource in result['data']['resources']:
print(f"- {resource['title']}: {resource['link']}")
else:
print(f"Error: {result['code']} - {result['message']}")
except requests.exceptions.RequestException as e:
print(f'Request failed: {e}')JavaScript (Node.js)
const fetch = require('node-fetch');
const apiKey = process.env.FELO_API_KEY;
const url = 'https://openapi.felo.ai/v2/chat';
const headers = {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
};
const body = JSON.stringify({
query: 'What are the benefits of renewable energy?'
});
fetch(url, { method: 'POST', headers, body })
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
if (data.status === 'ok') {
console.log('Answer:', data.data.answer);
console.log('\nSources:');
data.data.resources.forEach(resource => {
console.log(`- ${resource.title}: ${resource.link}`);
});
} else {
console.error(`Error: ${data.code} - ${data.message}`);
}
})
.catch(error => {
console.error('Request failed:', error);
});JavaScript (navegador / Fetch API)
const apiKey = 'YOUR_API_KEY'; // In production, get this from your backend
fetch('https://openapi.felo.ai/v2/chat', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: 'What are the benefits of renewable energy?'
})
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
if (data.status === 'ok') {
console.log('Answer:', data.data.answer);
// Display sources
const sourcesList = data.data.resources.map(resource =>
`<li><a href="${resource.link}">${resource.title}</a></li>`
).join('');
document.getElementById('answer').innerHTML = data.data.answer;
document.getElementById('sources').innerHTML = sourcesList;
} else {
console.error(`Error: ${data.code} - ${data.message}`);
}
})
.catch(error => {
console.error('Request failed:', error);
});Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
type ChatRequest struct {
Query string `json:"query"`
}
type ChatResponse struct {
Status string `json:"status"`
Message string `json:"message,omitempty"`
Data struct {
ID string `json:"id"`
MessageID string `json:"message_id"`
Answer string `json:"answer"`
QueryAnalysis struct {
Queries []string `json:"queries"`
} `json:"query_analysis"`
Resources []struct {
Link string `json:"link"`
Title string `json:"title"`
Snippet string `json:"snippet"`
} `json:"resources"`
} `json:"data,omitempty"`
Code string `json:"code,omitempty"`
RequestID string `json:"request_id,omitempty"`
}
func main() {
apiKey := os.Getenv("FELO_API_KEY")
url := "https://openapi.felo.ai/v2/chat"
requestBody := ChatRequest{
Query: "What are the benefits of renewable energy?",
}
jsonData, err := json.Marshal(requestBody)
if err != nil {
fmt.Printf("Error marshaling request: %v\n", err)
return
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
return
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Error making request: %v\n", err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading response: %v\n", err)
return
}
var chatResp ChatResponse
err = json.Unmarshal(body, &chatResp)
if err != nil {
fmt.Printf("Error unmarshaling response: %v\n", err)
return
}
if chatResp.Status == "ok" {
fmt.Println("Answer:", chatResp.Data.Answer)
fmt.Println("\nSources:")
for _, resource := range chatResp.Data.Resources {
fmt.Printf("- %s: %s\n", resource.Title, resource.Link)
}
} else {
fmt.Printf("Error: %s - %s\n", chatResp.Code, chatResp.Message)
}
}Límites de solicitudes
Los usuarios con una suscripción de pago de Felo vigente (incluidas las suscripciones empresariales) están exentos de todos los límites de solicitudes de /v2/chat, tanto para la autenticación con clave de API como con OAuth. Las respuestas exentas no incluyen encabezados X-RateLimit-*.
Los usuarios sin suscripción de pago están sujetos a límites de solicitudes en tres dimensiones. Si no se puede verificar el estado de la suscripción, estos límites siguen aplicándose:
- Por clave de API: limita las solicitudes realizadas con una sola clave de API dentro de una ventana de 1 minuto.
- Por usuario (minuto): limita las solicitudes agregadas de todas las claves de API propiedad de un usuario dentro de una ventana de 1 minuto.
- Por usuario (hora): limita las solicitudes agregadas de todas las claves de API propiedad de un usuario dentro de una ventana de 1 hora.
Superar cualquiera de las dimensiones genera una respuesta 429 Too Many Requests.
Los límites numéricos exactos pueden cambiar con el tiempo. Léelos siempre en los encabezados de respuesta que se indican a continuación, no en instantáneas de la documentación.
Encabezados de respuesta (respuestas sujetas a límites de solicitudes, incluido 429):
| Encabezado | Significado |
|---|---|
X-RateLimit-Limit | Límite de la dimensión más restrictiva actualmente en vigor |
X-RateLimit-Remaining | Cuota restante mínima entre las tres dimensiones |
X-RateLimit-Reset | Marca de tiempo Unix (segundos) en la que se restablece la ventana actual |
En un 429, además:
| Encabezado | Significado |
|---|---|
Retry-After | Retraso recomendado antes de reintentar (segundos) |
Ejemplo de cuerpo 429 (el message refleja los límites configurados en el momento de la solicitud):
{
"status": 429,
"code": "RATE_LIMITED",
"message": "Too many requests. Limit: <N>/min per API key, <M>/min per user, <H>/hour per user.",
"request_id": "..."
}Comportamiento del cliente: cuando esté presente, lee X-RateLimit-Remaining y reduce el ritmo de forma proactiva. En un 429, espera al menos Retry-After segundos (o hasta X-RateLimit-Reset) antes de reintentar. No codifiques los valores de los límites directamente.
Para consultar la política completa de reintentos y las indicaciones sobre el retroceso exponencial, consulta Errores, límites y reintentos. Si necesitas límites de solicitudes más altos, escribe a [email protected].
Prácticas recomendadas
1. Gestiona los errores con elegancia
Comprueba siempre el estado de la respuesta y gestiona los errores como corresponda:
if result['status'] == 'ok':
# Process successful response
answer = result['data']['answer']
else:
# Handle error
error_code = result['code']
error_message = result['message']
# Log error, show user-friendly message, etc.2. Implementa lógica de reintentos
Para los errores transitorios (códigos de estado 5xx), implementa retroceso exponencial:
import time
max_retries = 3
retry_delay = 1 # seconds
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=data)
if response.status_code < 500:
break # Success or client error, don't retry
if attempt < max_retries - 1:
time.sleep(retry_delay * (2 ** attempt)) # Exponential backoff3. Almacena en caché las respuestas
Considera almacenar en caché las respuestas de consultas idénticas para reducir las llamadas a la API y mejorar los tiempos de respuesta.
4. Valida la entrada
Valida la entrada del usuario antes de enviarla a la API:
- Asegúrate de que la consulta no esté vacía
- Comprueba la longitud de la consulta (de 1 a 2000 caracteres)
- Sanea la entrada para evitar ataques de inyección
5. Supervisa el uso
Haz seguimiento del uso de tu API para mantenerte dentro de los límites de solicitudes y optimizar los costes:
- Registra el número de solicitudes y los tiempos de respuesta
- Supervisa las tasas de error
- Configura alertas para patrones inusuales
Soporte
¿Necesitas ayuda con la Chat API?
- Consulta la guía de primeros pasos
- Consulta la documentación de autenticación
- Escribe a [email protected] e incluye tu request ID
Registro de cambios
v2.0.0 (actual)
- Lanzamiento inicial de la Chat API
- Compatibilidad con consultas en lenguaje natural
- Integración de búsqueda web en tiempo real
- Respuestas generadas por IA con citas de las fuentes