Felo API PlatformFelo API Platform

Felo APIs

Chat API

Integreer AI-gedreven conversationeel zoeken met query-analyse, webbronnen en citaties.

POST/v2/chat

Authenticatie

Bearer API key

Inhoudstype

application/json

Toelichting op limieten

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

Met de Chat API kun je AI-gedreven conversationeel zoeken in je applicaties integreren. Je stuurt een vraag in natuurlijke taal en ontvangt een intelligent antwoord dat is gegenereerd op basis van realtime zoekresultaten van het web.

Zie Fouten, limieten en nieuwe pogingen voor rate limits, foutresponsen en het gedrag bij nieuwe pogingen.

Overzicht

De Chat API voert de volgende stappen uit:

  1. Je query analyseren en optimaliseren
  2. Het web doorzoeken op relevante informatie
  3. Zoekresultaten rangschikken en filteren
  4. Een uitgebreid AI-antwoord genereren op basis van de bronnen
  5. Het antwoord retourneren samen met bronverwijzingen

Gebruiksscenario's:

  • Conversationele AI-assistenten
  • Tools voor onderzoek en informatieopvraging
  • Klantenservicechatbots met webkennis
  • Educatieve applicaties
  • Tools voor contentcreatie

Authenticatie

Alle verzoeken aan de Chat API vereisen authenticatie met een API-sleutel. Neem je API-sleutel op in de Authorization-header:

Authorization: Bearer YOUR_API_KEY

Zie de authenticatiehandleiding voor informatie over het verkrijgen en gebruiken van je API-sleutel.

Gebruik met AI-agents

Installeer het volledige pakket felo-ai als je wilt dat een agent Search en alle andere beschikbare Felo Skills gebruikt. Het installatieprogramma ontdekt dynamisch elke pakketmap die SKILL.md bevat, zodat ook toekomstige Skills worden meegenomen in plaats van een vaste subset. Installeer ze in de eigen map van je agent: Codex gebruikt ~/.codex/skills, Claude Code gebruikt ~/.claude/skills, OpenClaw gebruikt ~/.agents/skills en Hermes Agent gebruikt ~/.hermes/skills. Zie de handleiding voor handmatige AI-agentconfiguratie voor de veilige koppeling, de API-sleutel en de verificatiestappen per platform.

Endpoint

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

Verzoek

Headers

HeaderVerplichtBeschrijving
AuthorizationJaJe API-sleutel in de vorm: Bearer YOUR_API_KEY
Content-TypeJaMoet application/json zijn

Bodyparameters

ParameterTypeVerplichtBeschrijvingBeperkingen
querystringJaDe vraag of zoekopdracht van de gebruiker1-2000 tekens

Voorbeeldverzoek

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

Respons

Succesrespons (200 OK)

Als het verzoek slaagt, retourneert de API een JSON-respons met de volgende structuur:

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

Responsvelden

VeldTypeBeschrijving
statusstring | numberResponsstatus: "ok" bij succes; foutresponsen gebruiken de HTTP-statuscode als getal
messagestring | nullAanvullend bericht (null bij geslaagde verzoeken)
dataobjectResponsgegevens (alleen aanwezig wanneer status "ok" is)
data.idstringUnieke identificatie van deze chatsessie
data.message_idstringUnieke identificatie van dit specifieke bericht
data.answerstringDoor AI gegenereerd antwoord op de query
data.query_analysisobjectAnalyse van de query van de gebruiker
data.query_analysis.queriesstring[]Geoptimaliseerde zoekopdrachten die uit de invoer van de gebruiker zijn afgeleid
data.resourcesarrayLijst met webbronnen die zijn gebruikt om het antwoord te genereren
data.resources[].linkstringURL van de bron
data.resources[].titlestringTitel van de bronpagina
data.resources[].snippetstringRelevant fragment uit de bron

Foutrespons

Als er een fout optreedt, retourneert de API het gedeelde foutformaat:

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

Velden van de foutrespons

VeldTypeBeschrijving
statusnumberHTTP-statuscode; succesresponsen gebruiken "ok"
codestringFoutcode die het type fout aangeeft
messagestringLeesbare beschrijving van de fout
request_idstringUnieke identificatie van dit verzoek (handig voor support)

Foutcodes

HTTPCodeBetekenis
400Validatiecode van het endpointEen query-veld ontbreekt, is leeg of te lang; lees message voor het specifieke probleem
401UNAUTHORIZEDAPI-sleutel ontbreekt, is ongeldig of ingetrokken
429RATE_LIMITEDEen rate limit is overschreden
502CHAT_FAILEDInterne fout in de dienst
503SERVICE_UNAVAILABLEDienst tijdelijk niet beschikbaar

Zie Fouten, limieten en nieuwe pogingen voor het gedeelde foutformaat, andere foutcodes en de regels voor nieuwe pogingen.

Voorbeelden

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

Respons:

{
  "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 limits

Gebruikers met een actief betaald Felo-abonnement (inclusief enterprise-abonnementen) zijn vrijgesteld van alle rate limits voor /v2/chat, zowel bij authenticatie met een API-sleutel als met OAuth. Vrijgestelde responsen bevatten geen X-RateLimit-*-headers.

Gebruikers zonder betaald abonnement vallen onder rate limits op drie dimensies. Als de abonnementsstatus niet kan worden geverifieerd, blijven deze limieten van toepassing:

  • Per API-sleutel - beperkt het aantal verzoeken met één API-sleutel binnen een venster van 1 minuut.
  • Per gebruiker (minuut) - beperkt het totale aantal verzoeken over alle API-sleutels van een gebruiker binnen een venster van 1 minuut.
  • Per gebruiker (uur) - beperkt het totale aantal verzoeken over alle API-sleutels van een gebruiker binnen een venster van 1 uur.

Als een van de dimensies wordt overschreden, volgt een 429 Too Many Requests-respons.

De exacte numerieke limieten kunnen in de loop van de tijd veranderen. Lees ze altijd uit de onderstaande responsheaders, niet uit momentopnames in de documentatie.

Responsheaders (responsen waarop rate limiting van toepassing is, inclusief 429):

HeaderBetekenis
X-RateLimit-LimitLimiet van de strengste dimensie die op dat moment geldt
X-RateLimit-RemainingKleinste resterende quotum over de drie dimensies
X-RateLimit-ResetUnix-tijdstempel (seconden) waarop het huidige venster opnieuw begint

Bij een 429 bovendien:

HeaderBetekenis
Retry-AfterAanbevolen wachttijd voor een nieuwe poging (seconden)

Voorbeeld van een 429-body (de message weerspiegelt de ingestelde limieten op het moment van het verzoek):

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

Clientgedrag: lees, wanneer aanwezig, X-RateLimit-Remaining en wacht proactief. Wacht bij een 429 minstens Retry-After seconden (of tot X-RateLimit-Reset) voordat je het opnieuw probeert. Hardcode limietwaarden niet.

Zie Fouten, limieten en nieuwe pogingen voor het volledige beleid voor nieuwe pogingen en richtlijnen voor backoff. Als je hogere rate limits nodig hebt, neem dan contact op met [email protected].

Best practices

1. Ga zorgvuldig met fouten om

Controleer altijd de responsstatus en handel fouten correct af:

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. Implementeer logica voor nieuwe pogingen

Gebruik exponentiële backoff voor tijdelijke fouten (5xx-statuscodes):

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. Cache responsen

Overweeg om responsen voor identieke query's te cachen om het aantal API-aanroepen te beperken en de responstijd te verbeteren.

4. Valideer invoer

Valideer de invoer van de gebruiker voordat je deze naar de API stuurt:

  • Zorg dat de query niet leeg is
  • Controleer de lengte van de query (1-2000 tekens)
  • Schoon de invoer op om injectieaanvallen te voorkomen

5. Houd je gebruik in de gaten

Volg je API-gebruik om binnen de rate limits te blijven en kosten te optimaliseren:

  • Log het aantal verzoeken en de responstijden
  • Houd foutpercentages in de gaten
  • Stel waarschuwingen in voor ongebruikelijke patronen

Support

Hulp nodig bij de Chat API?

Changelog

v2.0.0 (huidige versie)

  • Eerste release van de Chat API
  • Ondersteuning voor query's in natuurlijke taal
  • Integratie met realtime zoeken op het web
  • Door AI gegenereerde antwoorden met bronverwijzingen