Configure Harness
Chat API
Integrate AI-powered conversational search with query analysis, web resources, and citations.
/v2/chatAuthentication
Bearer API key
Content type
application/json
Rate notes
Per API key and per user windows. Read X-RateLimit headers.
The Chat API enables you to integrate AI-powered conversational search into your applications. Send a natural language query and receive an intelligent answer generated from real-time web search results.
Overview
The Chat API performs the following operations:
- Analyzes and optimizes your query
- Searches the web for relevant information
- Ranks and filters search results
- Generates a comprehensive AI answer based on the sources
- Returns the answer along with source citations
Use Cases:
- Conversational AI assistants
- Research and information retrieval tools
- Customer support chatbots with web knowledge
- Educational applications
- Content creation tools
Authentication
All requests to the Chat API require authentication using an API Key. Include your API Key in the Authorization header:
Authorization: Bearer YOUR_API_KEYSee the Authentication Guide for details on obtaining and using your API Key.
Endpoint
POST https://openapi.felo.ai/v2/chatRequest
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Your API Key in the format: Bearer YOUR_API_KEY |
Content-Type | Yes | Must be application/json |
Body Parameters
| Parameter | Type | Required | Description | Constraints |
|---|---|---|---|---|
query | string | Yes | The user's question or search query | 1-2000 characters |
Example Request
{
"query": "What are the latest developments in quantum computing?"
}Response
Success Response (200 OK)
When the request is successful, the API returns a JSON response with the following structure:
{
"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..."
}
]
}
}Response Fields
| Field | Type | Description |
|---|---|---|
status | string | Response status: "ok" for success, "error" for failure |
message | string | null | Additional message (null for successful requests) |
data | object | Response data (only present when status is "ok") |
data.id | string | Unique identifier for this chat session |
data.message_id | string | Unique identifier for this specific message |
data.answer | string | AI-generated answer to the query |
data.query_analysis | object | Analysis of the user's query |
data.query_analysis.queries | string[] | Optimized search queries derived from the user's input |
data.resources | array | List of web sources used to generate the answer |
data.resources[].link | string | URL of the source |
data.resources[].title | string | Title of the source page |
data.resources[].snippet | string | Relevant excerpt from the source |
Error Response
When an error occurs, the API returns a JSON response with error details:
{
"status": "error",
"code": "INVALID_API_KEY",
"message": "The provided API Key is invalid or has been revoked",
"request_id": "req_abc123xyz789"
}Error Response Fields
| Field | Type | Description |
|---|---|---|
status | string | Always "error" for error responses |
code | string | Error code identifying the type of error |
message | string | Human-readable error description |
request_id | string | Unique identifier for this request (useful for support) |
Error Codes
| Code | HTTP Status | Description | Solution |
|---|---|---|---|
INVALID_API_KEY | 401 | API Key is invalid, malformed, or revoked | Verify your API Key is correct and active |
MISSING_AUTHORIZATION | 401 | Authorization header is missing | Include the Authorization header in your request |
MALFORMED_AUTHORIZATION | 401 | Authorization header format is incorrect | Use format: Authorization: Bearer YOUR_API_KEY |
MISSING_PARAMETER | 400 | Required parameter is missing | Ensure query parameter is included in request body |
INVALID_PARAMETER | 400 | Parameter value is invalid | Check that query is a non-empty string (1-2000 characters) |
QUERY_TOO_LONG | 400 | Query exceeds maximum length | Shorten your query to 2000 characters or less |
RATE_LIMITED | 429 | Too many requests in a short time | Slow down your request rate or contact support |
CHAT_FAILED | 502 | Internal service error | Retry the request; contact support if the issue persists |
SERVICE_UNAVAILABLE | 503 | Service temporarily unavailable | Wait and retry; check status page for updates |
Examples
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?"
}'Response:
{
"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
The /v2/chat endpoint enforces rate limits on three dimensions:
- Per API Key - limits requests made with a single API Key within a 1-minute window.
- Per User (minute) - limits the aggregate requests across all API Keys owned by a user within a 1-minute window.
- Per User (hour) - limits the aggregate requests across all API Keys owned by a user within a 1-hour window.
Any dimension being exceeded triggers a 429 Too Many Requests response.
The exact numeric limits may change over time. Always read them from the response headers below, not from documentation snapshots.
Response headers (every response, including 429):
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Tighter of the two dimension limits currently in effect |
X-RateLimit-Remaining | Minimum remaining quota across both dimensions |
X-RateLimit-Reset | Unix timestamp (seconds) when the current window resets |
On 429, additionally:
| Header | Meaning |
|---|---|
Retry-After | Recommended retry delay (seconds) |
Example 429 body (the message reflects the current configured limits at request time):
{
"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": "..."
}Recommended client behavior:
- Read
X-RateLimit-Remainingon every success response; back off proactively as it approaches0. - On 429, wait at least
Retry-Afterseconds (or untilX-RateLimit-Reset) before retrying. - Do not hardcode limit values - they may be tuned without API contract changes.
If you need higher rate limits, contact [email protected].
Best Practices
1. Handle Errors Gracefully
Always check the response status and handle errors appropriately:
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. Implement Retry Logic
For transient errors (5xx status codes), implement exponential backoff:
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. Cache Responses
Consider caching responses for identical queries to reduce API calls and improve response times.
4. Validate Input
Validate user input before sending to the API:
- Ensure query is not empty
- Check query length (1-2000 characters)
- Sanitize input to prevent injection attacks
5. Monitor Usage
Track your API usage to stay within rate limits and optimize costs:
- Log request counts and response times
- Monitor error rates
- Set up alerts for unusual patterns
Support
Need help with the Chat API?
- Review the Getting Started Guide
- Check Authentication Documentation
- Contact support at [email protected] with your request ID
Changelog
v2.0.0 (Current)
- Initial release of Chat API
- Support for natural language queries
- Real-time web search integration
- AI-generated answers with source citations