Felo APIs
Chat API
Додайте розмовний пошук AI з аналізом запитів, вебджерелами та цитатами.
/v2/chatАвтентифікація
Bearer API key
Тип вмісту
application/json
Примітки щодо обмежень
Per API key and per user windows. Read X-RateLimit headers.
Chat API дає змогу інтегрувати розмовний пошук на основі ШІ у ваші застосунки. Надішліть запит природною мовою та отримайте розумну відповідь, згенеровану з результатів вебпошуку в реальному часі.
Про обмеження швидкості, відповіді з помилками та поведінку повторних спроб див. Помилки, обмеження та повторні спроби.
Огляд
Chat API виконує такі операції:
- Аналізує та оптимізує ваш запит
- Шукає у вебі релевантну інформацію
- Ранжує та фільтрує результати пошуку
- Генерує ґрунтовну відповідь ШІ на основі джерел
- Повертає відповідь разом із цитуванням джерел
Варіанти використання:
- Розмовні помічники ШІ
- Інструменти досліджень та пошуку інформації
- Чатботи підтримки клієнтів із вебзнаннями
- Освітні застосунки
- Інструменти створення вмісту
Автентифікація
Усі запити до Chat API потребують автентифікації за допомогою API-ключа. Додайте свій API-ключ у заголовок Authorization:
Authorization: Bearer YOUR_API_KEYДокладніше про отримання та використання API-ключа див. у посібнику з автентифікації.
Використання з агентами ШІ
Установіть повний пакет felo-ai, коли хочете, щоб агент використовував Search та кожен інший доступний Felo Skill. Інсталятор динамічно знаходить кожен каталог пакета, що містить SKILL.md, тому він включає майбутні Skills, а не фіксований підмножину. Установіть їх у рідний каталог для вашого агента: Codex використовує ~/.codex/skills, Claude Code — ~/.claude/skills, OpenClaw — ~/.agents/skills, а Hermes Agent — ~/.hermes/skills. Кроки безпечного зв'язування, налаштування API-ключа та перевірки для кожної платформи див. у посібнику з ручного налаштування агента ШІ.
Endpoint
POST https://openapi.felo.ai/v2/chatЗапит
Заголовки
| Заголовок | Обов'язковий | Опис |
|---|---|---|
Authorization | Так | Ваш API-ключ у форматі: Bearer YOUR_API_KEY |
Content-Type | Так | Має бути application/json |
Параметри тіла
| Параметр | Тип | Обов'язковий | Опис | Обмеження |
|---|---|---|---|---|
query | string | Так | Запитання користувача або пошуковий запит | 1-2000 символів |
Приклад запиту
{
"query": "What are the latest developments in quantum computing?"
}Відповідь
Успішна відповідь (200 OK)
Коли запит успішний, API повертає відповідь JSON із такою структурою:
{
"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..."
}
]
}
}Поля відповіді
| Поле | Тип | Опис |
|---|---|---|
status | string | number | Статус відповіді: "ok" у разі успіху; відповіді з помилками використовують код стану HTTP як число |
message | string | null | Додаткове повідомлення (null для успішних запитів) |
data | object | Дані відповіді (присутні лише коли статус "ok") |
data.id | string | Унікальний ідентифікатор цього сеансу чату |
data.message_id | string | Унікальний ідентифікатор цього конкретного повідомлення |
data.answer | string | Відповідь, згенерована ШІ на запит |
data.query_analysis | object | Аналіз запиту користувача |
data.query_analysis.queries | string[] | Оптимізовані пошукові запити, отримані з вхідних даних користувача |
data.resources | array | Список вебджерел, використаних для генерації відповіді |
data.resources[].link | string | URL-адреса джерела |
data.resources[].title | string | Заголовок сторінки джерела |
data.resources[].snippet | string | Релевантний уривок із джерела |
Відповідь з помилкою
Коли виникає помилка, API повертає спільну структуру помилки:
{
"status": 401,
"code": "UNAUTHORIZED",
"message": "API Key is required",
"request_id": "..."
}Поля відповіді з помилкою
| Поле | Тип | Опис |
|---|---|---|
status | number | Код стану HTTP; успішні відповіді використовують "ok" |
code | string | Код помилки, що ідентифікує тип помилки |
message | string | Опис помилки для людини |
request_id | string | Унікальний ідентифікатор цього запиту (корисний для підтримки) |
Коди помилок
| HTTP | Код | Значення |
|---|---|---|
| 400 | Код перевірки endpoint | Поле query відсутнє, порожнє або задовге; прочитайте message, щоб дізнатися конкретну проблему |
| 401 | UNAUTHORIZED | API-ключ відсутній, недійсний або відкликаний |
| 429 | RATE_LIMITED | Перевищено обмеження швидкості |
| 502 | CHAT_FAILED | Внутрішня помилка сервісу |
| 503 | SERVICE_UNAVAILABLE | Сервіс тимчасово недоступний |
Спільну структуру, інші коди помилок і правила повторних спроб див. у Помилки, обмеження та повторні спроби.
Приклади
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?"
}'Відповідь:
{
"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)
}
}Обмеження швидкості
Користувачі з чинною платною підпискою Felo (зокрема корпоративною) звільнені від усіх обмежень швидкості /v2/chat як для автентифікації за API-ключем, так і для OAuth. Звільнені відповіді не містять заголовків X-RateLimit-*.
Користувачі без платної підписки підпадають під обмеження швидкості за трьома вимірами. Якщо статус підписки неможливо перевірити, ці обмеження все одно застосовуються:
- Для кожного API-ключа — обмежує запити, зроблені з одним API-ключем, у вікні 1 хвилина.
- Для кожного користувача (хвилина) — обмежує сукупні запити за всіма API-ключами, якими володіє користувач, у вікні 1 хвилина.
- Для кожного користувача (година) — обмежує сукупні запити за всіма API-ключами, якими володіє користувач, у вікні 1 година.
Перевищення будь-якого виміру спричиняє відповідь 429 Too Many Requests.
Точні числові ліміти можуть змінюватися з часом. Завжди читайте їх із наведених нижче заголовків відповіді, а не із знімків документації.
Заголовки відповіді (відповіді, що підпадають під обмеження швидкості, зокрема 429):
| Заголовок | Значення |
|---|---|
X-RateLimit-Limit | Ліміт найжорсткішого виміру, що наразі діє |
X-RateLimit-Remaining | Мінімальна залишкова квота за трьома вимірами |
X-RateLimit-Reset | Часова позначка Unix (секунди), коли скидається поточне вікно |
Для 429 додатково:
| Заголовок | Значення |
|---|---|
Retry-After | Рекомендована затримка повторної спроби (секунди) |
Приклад тіла 429 (message відображає поточні налаштовані ліміти на момент запиту):
{
"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": "..."
}Поведінка клієнта: коли він присутній, читайте X-RateLimit-Remaining і завчасно збільшуйте інтервал. Для 429 зачекайте щонайменше Retry-After секунд (або до X-RateLimit-Reset), перш ніж повторити спробу. Не вшивайте значення лімітів жорстко.
Повну політику повторних спроб і рекомендації щодо збільшення інтервалу див. у Помилки, обмеження та повторні спроби. Якщо вам потрібні вищі обмеження швидкості, зверніться до [email protected].
Рекомендації
1. Коректно обробляйте помилки
Завжди перевіряйте статус відповіді та належним чином обробляйте помилки:
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. Реалізуйте логіку повторних спроб
Для тимчасових помилок (коди стану 5xx) реалізуйте експоненційне збільшення інтервалу:
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. Кешуйте відповіді
Розгляньте кешування відповідей на ідентичні запити, щоб зменшити кількість викликів API та скоротити час відповіді.
4. Перевіряйте вхідні дані
Перевіряйте вхідні дані користувача, перш ніж надсилати їх до API:
- Переконайтеся, що запит не порожній
- Перевірте довжину запиту (1-2000 символів)
- Очищуйте вхідні дані, щоб запобігти атакам ін'єкції
5. Відстежуйте використання
Відстежуйте використання API, щоб дотримуватися обмежень швидкості та оптимізувати витрати:
- Записуйте в журнал кількість запитів і час відповіді
- Відстежуйте частоту помилок
- Налаштуйте сповіщення про нетипові шаблони
Підтримка
Потрібна допомога з Chat API?
- Перегляньте посібник із початку роботи
- Перевірте документацію з автентифікації
- Зверніться до підтримки за адресою [email protected], вказавши свій request ID
Журнал змін
v2.0.0 (поточна)
- Початковий випуск Chat API
- Підтримка запитів природною мовою
- Інтеграція вебпошуку в реальному часі
- Відповіді, згенеровані ШІ, з цитуванням джерел