Felo API PlatformFelo API Platform

Felo APIs

Chat API

Integra la ricerca conversazionale basata su IA con analisi delle query, risorse web e citazioni.

POST/v2/chat

Autenticazione

Bearer API key

Tipo di contenuto

application/json

Note sui limiti

Per API key and per user windows. Read X-RateLimit headers.

La Chat API ti permette di integrare la ricerca conversazionale basata sull'IA nelle tue applicazioni. Invia una query in linguaggio naturale e ricevi una risposta intelligente generata da risultati di ricerca web in tempo reale.

Per i rate limit, le risposte di errore e il comportamento di retry, vedi Errori, limiti e retry.

Panoramica

La Chat API esegue le seguenti operazioni:

  1. Analizza e ottimizza la tua query
  2. Cerca sul web informazioni pertinenti
  3. Classifica e filtra i risultati della ricerca
  4. Genera una risposta IA completa basata sulle fonti
  5. Restituisce la risposta con le citazioni delle fonti

Casi d'uso:

  • Assistenti IA conversazionali
  • Strumenti di ricerca e recupero di informazioni
  • Chatbot per l'assistenza clienti con conoscenza del web
  • Applicazioni didattiche
  • Strumenti di creazione di contenuti

Autenticazione

Tutte le richieste alla Chat API richiedono l'autenticazione tramite una chiave API. Includi la tua chiave API nell'header Authorization:

Authorization: Bearer YOUR_API_KEY

Vedi la guida all'autenticazione per i dettagli su come ottenere e usare la chiave API.

Uso con agenti IA

Installa il pacchetto completo felo-ai quando vuoi che un agente usi Search e ogni altra Felo Skill disponibile. L'installer individua dinamicamente ogni directory del pacchetto che contiene SKILL.md, quindi include anche le Skill future invece di un sottoinsieme fisso. Installale nella directory nativa del tuo agente: Codex usa ~/.codex/skills, Claude Code usa ~/.claude/skills, OpenClaw usa ~/.agents/skills e Hermes Agent usa ~/.hermes/skills. Vedi la guida alla configurazione manuale degli agenti IA per i passaggi sicuri di link, chiave API e verifica su ogni piattaforma.

Endpoint

POST https://openapi.felo.ai/v2/chat

Richiesta

HeaderObbligatorioDescrizione
AuthorizationSìLa tua chiave API nel formato: Bearer YOUR_API_KEY
Content-TypeSìDeve essere application/json

Parametri del corpo

ParametroTipoObbligatorioDescrizioneVincoli
querystringSìLa domanda o la query di ricerca dell'utenteDa 1 a 2000 caratteri

Richiesta di esempio

{
  "query": "What are the latest developments in quantum computing?"
}

Risposta

Risposta di successo (200 OK)

Quando la richiesta riesce, l'API restituisce una risposta JSON con la seguente struttura:

{
  "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..."
      }
    ]
  }
}

Campi della risposta

CampoTipoDescrizione
statusstring | numberStato della risposta: "ok" in caso di successo; le risposte di errore usano il codice di stato HTTP come numero
messagestring | nullMessaggio aggiuntivo (null per le richieste riuscite)
dataobjectDati della risposta (presenti solo quando lo stato è "ok")
data.idstringIdentificatore univoco di questa sessione di chat
data.message_idstringIdentificatore univoco di questo specifico messaggio
data.answerstringRisposta generata dall'IA alla query
data.query_analysisobjectAnalisi della query dell'utente
data.query_analysis.queriesstring[]Query di ricerca ottimizzate derivate dall'input dell'utente
data.resourcesarrayElenco delle fonti web usate per generare la risposta
data.resources[].linkstringURL della fonte
data.resources[].titlestringTitolo della pagina di origine
data.resources[].snippetstringEstratto pertinente della fonte

Risposta di errore

Quando si verifica un errore, l'API restituisce la struttura di errore condivisa:

{
  "status": 401,
  "code": "UNAUTHORIZED",
  "message": "API Key is required",
  "request_id": "..."
}

Campi della risposta di errore

CampoTipoDescrizione
statusnumberCodice di stato HTTP; le risposte di successo usano "ok"
codestringCodice di errore che identifica il tipo di errore
messagestringDescrizione dell'errore leggibile da una persona
request_idstringIdentificatore univoco di questa richiesta (utile per il supporto)

Codici di errore

HTTPCodiceSignificato
400Codice di convalida dell'endpointUn campo query manca, è vuoto o è troppo lungo; leggi message per il problema specifico
401UNAUTHORIZEDLa chiave API manca, non è valida o è revocata
429RATE_LIMITEDÈ stato superato un rate limit
502CHAT_FAILEDErrore interno del servizio
503SERVICE_UNAVAILABLEServizio temporaneamente non disponibile

Per la struttura condivisa, gli altri codici di errore e le regole di retry, vedi Errori, limiti e retry.

Esempi

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?"
  }'

Risposta:

{
  "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 (Browser/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)
    }
}

Rate limiting

Gli utenti con un abbonamento Felo a pagamento attivo (compresi gli abbonamenti enterprise) sono esenti da tutti i rate limit di /v2/chat, sia per l'autenticazione con chiave API sia per OAuth. Le risposte esenti non includono header X-RateLimit-*.

Gli utenti senza abbonamento a pagamento sono soggetti a rate limit su tre dimensioni. Se lo stato dell'abbonamento non può essere verificato, questi limiti si applicano comunque:

  • Per chiave API - limita le richieste effettuate con una singola chiave API entro una finestra di 1 minuto.
  • Per utente (minuto) - limita le richieste complessive di tutte le chiavi API di proprietà di un utente entro una finestra di 1 minuto.
  • Per utente (ora) - limita le richieste complessive di tutte le chiavi API di proprietà di un utente entro una finestra di 1 ora.

Il superamento di qualsiasi dimensione genera una risposta 429 Too Many Requests.

I limiti numerici esatti possono cambiare nel tempo. Leggili sempre dagli header di risposta riportati sotto, non dagli snapshot della documentazione.

Header di risposta (risposte soggette a rate limit, comprese le 429):

HeaderSignificato
X-RateLimit-LimitLimite della dimensione più restrittiva attualmente in vigore
X-RateLimit-RemainingQuota rimanente minima tra le tre dimensioni
X-RateLimit-ResetTimestamp Unix (secondi) del reset della finestra corrente

In caso di 429, in aggiunta:

HeaderSignificato
Retry-AfterRitardo di retry consigliato (secondi)

Corpo di esempio di una 429 (il message riflette i limiti configurati al momento della richiesta):

{
  "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": "..."
}

Comportamento del client: quando presenti, leggi X-RateLimit-Remaining e riduci proattivamente il ritmo. In caso di 429, attendi almeno Retry-After secondi (o fino a X-RateLimit-Reset) prima di riprovare. Non inserire nel codice valori di limite fissi.

Per la politica di retry completa e le indicazioni sul backoff, vedi Errori, limiti e retry. Se ti servono rate limit più alti, contatta [email protected].

Buone pratiche

1. Gestire gli errori con attenzione

Controlla sempre lo stato della risposta e gestisci gli errori in modo appropriato:

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. Implementare la logica di retry

Per gli errori transitori (codici di stato 5xx), implementa il backoff esponenziale:

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 backoff

3. Memorizzare le risposte in cache

Valuta la cache delle risposte per query identiche, per ridurre le chiamate API e migliorare i tempi di risposta.

4. Convalidare l'input

Convalida l'input dell'utente prima di inviarlo all'API:

  • Verifica che la query non sia vuota
  • Controlla la lunghezza della query (da 1 a 2000 caratteri)
  • Sanifica l'input per prevenire attacchi di injection

5. Monitorare l'utilizzo

Tieni traccia dell'utilizzo dell'API per restare entro i rate limit e ottimizzare i costi:

  • Registra il numero di richieste e i tempi di risposta
  • Monitora i tassi di errore
  • Imposta avvisi per comportamenti anomali

Supporto

Serve aiuto con la Chat API?

Changelog

v2.0.0 (attuale)

  • Rilascio iniziale della Chat API
  • Supporto per query in linguaggio naturale
  • Integrazione della ricerca web in tempo reale
  • Risposte generate dall'IA con citazioni delle fonti