Make real queries against https://kendr.org.
This guide is the fastest path from zero to a working Kendr integration. It covers the base domain, the supported auth modes, the request body for POST /api/v1/query, and working curl, JavaScript, Python, .NET, Java, and Go examples for catalog reads, credits, API keys, and live query execution.
Integration rules
| Item | Value |
|---|---|
| Base domain | https://kendr.org |
| Public discovery | GET /api/catalog and GET /api/openapi.json |
| Query execution | POST /api/v1/query |
| Primary server auth | Authorization: Bearer kndr_live_... or X-API-Key: kndr_live_... |
| Customer state | GET /api/me/dashboard returns user, packages, api_keys, purchases, ledger, and surfaces |
Quickstart flow
Auth headers accepted by the query route
| Mode | Header | When to use it |
|---|---|---|
| API key | Authorization: Bearer kndr_live_... | Best for server-to-server integrations and background jobs. |
| API key | X-API-Key: kndr_live_... | Alternative header when bearer auth is not convenient. |
| App session | X-Kendr-Session: SESSION_TOKEN | Best when your app just authenticated the user through Kendr. |
| OAuth bearer | Authorization: Bearer kndr_oat_... | Best for Kendr Desktop or CLI flows using PKCE or device code with the app scope. |
| Browser cookie | kendr_session cookie | Works for browser-origin requests after /api/auth/login. |
Query request body
| Field | Required | Meaning |
|---|---|---|
| surface | Yes | The Kendr surface key, such as google_search, google_images, google_maps, google_flights, or google_hotels. |
| query | Yes | The primary query string. |
| params | No | An object for optional values such as gl, hl, page, location, or travel fields. |
| Top-level extras | No | Optional params can also be sent beside surface and query. Kendr merges them with params. |
{
"surface": "google_search",
"query": "best llm observability tools",
"params": {
"gl": "us",
"hl": "en",
"page": 1
}
}
Query examples
Kendr is a plain HTTPS and JSON API. The same request contract works from JavaScript, Python, .NET, Java, and Go, so you can use whichever runtime already exists in your stack.
API key auth
curl https://kendr.org/api/v1/query \
-X POST \
-H "Authorization: Bearer $KENDR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"surface": "google_search",
"query": "best llm observability tools",
"params": {
"gl": "us",
"hl": "en",
"page": 1
}
}'
App session auth
curl https://kendr.org/api/v1/query \
-X POST \
-H "X-Kendr-Session: $KENDR_SESSION" \
-H "Content-Type: application/json" \
-d '{
"surface": "google_maps",
"query": "coworking spaces in austin",
"params": {
"gl": "us",
"hl": "en"
}
}'
Kendr currently ships helper clients for JavaScript and Python. Use the tabs to switch between Curl, JavaScript, Python, .NET, Java, and Go for the same query contract.
Curl request with an API key
curl https://kendr.org/api/v1/query \
-X POST \
-H "Authorization: Bearer $KENDR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"surface": "google_search",
"query": "best llm observability tools",
"params": {
"gl": "us",
"hl": "en",
"page": 1
}
}'
Server-side fetch request to POST /api/v1/query
const response = await fetch('https://kendr.org/api/v1/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.KENDR_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
surface: 'google_search',
query: 'best llm observability tools',
params: { gl: 'us', hl: 'en', page: 1 }
})
});
const payload = await response.json();
console.log(payload.data, payload.remaining_credits);
Python requests example against the same endpoint
import os
import requests
response = requests.post(
'https://kendr.org/api/v1/query',
headers={
'Authorization': f"Bearer {os.environ['KENDR_API_KEY']}",
'Content-Type': 'application/json',
},
json={
'surface': 'google_search',
'query': 'best llm observability tools',
'params': {'gl': 'us', 'hl': 'en', 'page': 1},
},
timeout=30,
)
payload = response.json()
print(payload['data'])
print(payload['remaining_credits'])
.NET HttpClient example
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("KENDR_API_KEY"));
var body = new
{
surface = "google_search",
query = "best llm observability tools",
@params = new
{
gl = "us",
hl = "en",
page = 1
}
};
using var response = await client.PostAsync(
"https://kendr.org/api/v1/query",
new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json")
);
response.EnsureSuccessStatusCode();
using var stream = await response.Content.ReadAsStreamAsync();
var payload = await JsonSerializer.DeserializeAsync(stream);
Console.WriteLine(payload.GetProperty("data").ToString());
Console.WriteLine(payload.GetProperty("remaining_credits").GetInt32());
Java HttpClient example
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
String requestBody = """
{
"surface": "google_search",
"query": "best llm observability tools",
"params": {
"gl": "us",
"hl": "en",
"page": 1
}
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://kendr.org/api/v1/query"))
.header("Authorization", "Bearer " + System.getenv("KENDR_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Go net/http example
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
body := map[string]any{
"surface": "google_search",
"query": "best llm observability tools",
"params": map[string]any{
"gl": "us",
"hl": "en",
"page": 1,
},
}
rawBody, err := json.Marshal(body)
if err != nil {
panic(err)
}
req, err := http.NewRequest(http.MethodPost, "https://kendr.org/api/v1/query", bytes.NewReader(rawBody))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("KENDR_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var payload map[string]any
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
panic(err)
}
fmt.Println(payload["data"])
fmt.Println(payload["remaining_credits"])
}
Official helper clients
Kendr currently publishes helper clients for JavaScript and Python. Use these when you want a thinner call surface around the raw HTTP routes, or call the HTTPS endpoints directly from any other runtime.
JavaScript SDK
import { KendrClient } from './sdk/javascript/index.js';
const client = new KendrClient({
apiKey: process.env.KENDR_API_KEY,
baseUrl: 'https://kendr.org'
});
const response = await client.query({
surface: 'google_search',
query: 'best llm observability tools',
params: { gl: 'us', hl: 'en', page: 1 }
});
console.log(response.data);
Python SDK
from kendr import KendrClient
client = KendrClient(
api_key='YOUR_KENDR_API_KEY',
base_url='https://kendr.org',
)
response = client.query(
surface='google_search',
query='best llm observability tools',
params={'gl': 'us', 'hl': 'en', 'page': 1},
)
print(response['data'])
Fetch surfaces, packages, and current credits
Use the public catalog to discover what is available, then use the customer dashboard to read the live wallet balance and package state for the authenticated user.
Public catalog
curl https://kendr.org/api/catalog
Dashboard balance
curl https://kendr.org/api/me/dashboard \ -H "X-Kendr-Session: $KENDR_SESSION"
The dashboard response includes user.credit_balance, packages, api_keys, purchases, ledger, and surfaces.
Create and use API keys
API keys are customer-scoped credentials. They are created after a customer logs in through a browser session, app session, or OAuth bearer token, and the raw key is returned only once.
Create a key
curl https://kendr.org/api/me/api-keys \
-X POST \
-H "X-Kendr-Session: $KENDR_SESSION" \
-H "Content-Type: application/json" \
-d '{
"label": "Production"
}'
List keys
curl https://kendr.org/api/me/api-keys \ -H "X-Kendr-Session: $KENDR_SESSION"
The create response includes raw_token once. Persist it immediately and use the returned api_key.id later for revocation.
Successful response and errors
Successful query response
{
"ok": true,
"surface": "google_search",
"provider": "searchapi",
"credits_charged": 1,
"remaining_credits": 249,
"data": {
"...": "provider payload"
},
"provider_attempts": []
}
Errors to handle
- 400: invalid payload, missing surface, unsupported surface, or invalid JSON.
- 401: no valid API key, session, or OAuth bearer token was supplied.
- 402: the wallet does not have enough credits for the requested surface.
- 502: every configured provider failed.
Kendr charges credits after a provider succeeds. Failed fallback attempts are exposed in provider_attempts for debugging but do not directly consume credits.