Kopik bases, in your code.
Every public or unlisted knowledge base can also be queried outside the website: through a REST API for your apps, or through an MCP server for your AI agents. Same base, same price, same cited answers. And your own bases, private ones included, are a free RAG backend for your agents, with nothing to host.
Overview: REST or MCP?
🤖 MCP for agents
Claude (Desktop, Code), Cursor, ChatGPT, your own agent: any MCP-compatible client. The agent discovers the tools itself, decides when to query the base and cites its sources. Nothing to code: a URL and a key are all you need.
🧩 REST for your apps
Your website, back office, chatbot or scripts. One HTTP call returns a written answer with its passages, or just the passages if you'd rather feed them to your own model.
Bases are listed in the catalogue. Each base has an identifier, its slug, visible in its URL: kopik.io/b/<slug>. Its page also provides ready-to-copy snippets.
Ask in any language: answers come back in the language of the question, whatever the language of the base's documents. Before searching, a language model expands the question into keywords and synonyms in the documents' language; the full-text search itself is tuned to that language (language field, also shown on each base's page). The 8 most relevant passages are kept, and answers are written only from them.
Your own private RAG. Make a base private in your dashboard and query it with your own API key, over REST or MCP: only you can reach it, and your questions to your own bases are free (fair use: 200 a day). Upload documents, get an endpoint. No vector database, ingestion pipeline or server to run.
Authentication
Create an API key from your dashboard. It starts with kpk_ and is shown only once: treat it like a password. Keep it server-side, never in code that runs in a browser. Send it in the Authorization header:
Authorization: Bearer kpk_…The x-api-key: kpk_… header is also accepted. Keys are stored hashed: if you lose one, revoke it and create another. The catalogue (listing and reading bases) is public and needs no key; only questions do. A revoked key stops working immediately. Your key also opens your own private bases, so keep it secret.
Billing
- Each base has its own price per question, set by its creator and given in
priceCents(euro cents). - The price is charged to your Kopik credit, topped up by card from the dashboard. The same credit works on the website, the API and MCP.
- A question with no result is not charged: if the base has nothing relevant, or the answer fails for a technical reason, the charge is cancelled.
- Cap the price with
maxPriceCents: if the base costs more, the call is refused (409,price_above_max) and nothing is charged. Recommended for agents running in a loop, since a creator can change their price at any time. - Insufficient credit →
402error with codeinsufficient_credit; nothing is charged. - The 1 free question a month applies to the website only: the API and MCP are always billed.
- Your own bases are free, through every channel: public, unlisted or private (fair use: 200 questions a day).
- The
answerandpassagesmodes cost the same. - Credit is topped up by card (packs of €10, €25, €50 or €100), never expires and is non-refundable.
REST API
Base URL: https://kopik.io/api/v1. JSON bodies and responses (UTF-8).
/bases
Public · freeLists public, non-empty bases, most queried first. Optional parameters: topic, q (keyword matched against title and description), limit (100 max). Unlisted bases are not included, but can still be queried by their slug.
Topics: legal, hr, tax, finance, environment, health, aviation, agriculture, industry, real-estate, tech, marketing, education, other.
curl "https://kopik.io/api/v1/bases?topic=legal&q=leave"{
"bases": [
{
"slug": "employment-law-leave-x7k2",
"title": "Employment law: Leave",
"description": "Paid leave, family-event leave, public holidays…",
"topic": "legal",
"topicLabel": "Legal",
"language": "english",
"priceCents": 10,
"documents": 12,
"passages": 840,
"questions": 1375,
"url": "https://kopik.io/b/employment-law-leave-x7k2",
"updatedAt": "2026-09-20T08:14:00.000Z"
}
]
}/bases/{slug}
Public · freeDetails of a public or unlisted base, including its document language. A private base returns 404 here, but its owner can still query it with their key.
curl https://kopik.io/api/v1/bases/employment-law-leave-x7k2{
"slug": "employment-law-leave-x7k2",
"title": "Employment law: Leave",
"description": "Paid leave, family-event leave, public holidays…",
"topic": "legal",
"topicLabel": "Legal",
"language": "english",
"priceCents": 10,
"documents": 12,
"passages": 840,
"questions": 1375,
"url": "https://kopik.io/b/employment-law-leave-x7k2",
"updatedAt": "2026-09-20T08:14:00.000Z"
}/bases/{slug}/query
🔑 Key required · billedAsks the base a question. Works on public and unlisted bases, and on your own private bases. Body: question (3 to 2,000 characters), optional maxPriceCents (price ceiling in euro cents) and mode:
"answer"(default): a written answer drawn from the base, with[n]references to the passages;"passages": only the most relevant passages (answerisnull), so you can reason with your own model.
curl -X POST https://kopik.io/api/v1/bases/employment-law-leave-x7k2/query \
-H "Authorization: Bearer kpk_…" \
-H "Content-Type: application/json" \
-d '{"question": "How many days of leave do I get for my wedding?", "mode": "answer", "maxPriceCents": 20}'{
"id": "cm1x0q2ab0001kopik",
"answer": "An employee is entitled to **4 days of leave** for their wedding or civil partnership [1]. This leave does not reduce their pay [2].",
"passages": [
{
"n": 1,
"document": "Employment code (family-event leave).pdf",
"text": "On presentation of supporting documents, the employee is entitled to leave: 1° For their wedding or civil partnership: four days…",
"score": 0.82
},
{
"n": 2,
"document": "Employment code (family-event leave).pdf",
"text": "These days of absence do not lead to any reduction in pay…",
"score": 0.61
}
],
"costCents": 10,
"balanceCents": 2490
}curl -X POST https://kopik.io/api/v1/bases/employment-law-leave-x7k2/query \
-H "Authorization: Bearer kpk_…" \
-H "Content-Type: application/json" \
-d '{"question": "wedding leave", "mode": "passages"}'{
"id": "cm1x0q9cd0002kopik",
"answer": null,
"passages": [
{ "n": 1, "document": "…", "text": "…", "score": 0.82 }
],
"costCents": 10,
"balanceCents": 2480
}If nothing is found, passages is empty, costCents is 0 and, in answer mode, the answer says so in plain words. balanceCents is your balance after the question.
Errors
Every error returns an HTTP status and a JSON body with a human-readable message and a stable, machine-readable code:
{
"error": "Insufficient credit: this base costs €0.10 per question. Top up at kopik.io/dashboard.",
"code": "insufficient_credit"
}Branch on code, never on the message: messages may be reworded, codes never change.
| code | HTTP | Meaning | What to do |
|---|---|---|---|
| invalid_json | 400 | The body is not valid JSON. | Send a JSON body with Content-Type: application/json. |
| question_too_short | 400 | Empty question, or under 3 characters. | Ask a real question. |
| question_too_long | 400 | Question over 2,000 characters. | Shorten the question. |
| api_key_missing | 401 | No API key in the request. | Add Authorization: Bearer kpk_…. |
| api_key_invalid | 401 | Unknown or revoked key. | Check the key, or create a new one in the dashboard. |
| insufficient_credit | 402 | Your credit doesn't cover the base's price. | Top up from the dashboard. |
| base_not_found | 404 | No base with this slug, or the base is private. | Check the slug (visible in the base's URL). |
| base_empty | 409 | The base has no documents yet. | Try again once its creator has added documents. |
| price_above_max | 409 | The base costs more than your `maxPriceCents`. Nothing is charged. | Raise your ceiling if the new price suits you, or pick another base. |
| refused | 422 | The question cannot be answered. | Rephrase the question. |
| rate_limited | 429 | Too many requests (see Limits). | Wait (honour Retry-After when present), then retry with backoff. |
| unexpected | 500 | Unexpected error on our side. | Retry; nothing is charged on failure. |
| answer_failed | 502 | The answer could not be generated. | Retry; you were not charged. |
| not_configured | 503 | The answer engine is temporarily unavailable. | Retry later. |
Error message language
Messages are in English by default. Send x-kopik-locale: fr (or en) to choose; without it, an Accept-Language header starting with fr gets French. The code is the same in every language.
curl -X POST https://kopik.io/api/v1/bases/employment-law-leave-x7k2/query \
-H "Authorization: Bearer kpk_…" \
-H "x-kopik-locale: en" \
-H "Content-Type: application/json" \
-d '{"question": "How many days of leave do I get for my wedding?"}'MCP server
Kopik exposes an MCP server (“Streamable HTTP” transport, stateless) that works with Claude, Cursor, ChatGPT and any MCP client. Tool names, descriptions and results are in English. Two URLs, depending on what you want to give your agent:
The whole catalogue
https://kopik.io/api/mcp
The agent finds the right base itself.
list_bases{ topic?, query? } (free)ask_base{ base, question }search_base{ base, question }
A single base
https://kopik.io/api/mcp?base=<slug>
Pre-scoped tools: the agent can only query this base. Each base's page shows its own URL. Works for your private bases too, with your key.
ask_base{ question }search_base{ question }
Tools
list_bases{ topic?, query? }- Lists public bases, filterable by topic or keyword. Free. Catalogue URL only.
ask_base{ base, question, maxPriceCents? }- Written answer followed by numbered source passages.
search_base{ base, question, maxPriceCents? }- Relevant passages only, without a written answer. Same price as ask_base.
On a single-base URL, base is omitted: ask_base { question } and search_base { question }. The optional maxPriceCents refuses the call, without charge, if the base costs more.
Each call is billed at the base's price, exactly like the API, and ends with the cost and remaining balance. The key goes in the Authorization: Bearer kpk_… header; only list_bases works without one. JSON-RPC batches are limited to 10 requests.
Prompt-injection protection. Text that comes from a base is returned between <kopik-untrusted> tags, preceded by a note telling the agent to treat it strictly as reference data and never to follow instructions it contains.
Claude Code
claude mcp add --transport http kopik https://kopik.io/api/mcp --header "Authorization: Bearer kpk_…"claude mcp add --transport http kopik-employment-law-leave-x7k2 "https://kopik.io/api/mcp?base=employment-law-leave-x7k2" --header "Authorization: Bearer kpk_…"Keep the quotes around the URL: in zsh (the default shell on macOS), an unquoted ? is a filename pattern and the command fails.
Other clients (Cursor, Claude Desktop, …)
Most clients read an mcpServers config file (in Cursor: .cursor/mcp.json). The common format, with a URL and headers:
{
"mcpServers": {
"kopik": {
"url": "https://kopik.io/api/mcp",
"headers": { "Authorization": "Bearer kpk_…" }
},
"kopik-employment-law-leave-x7k2": {
"url": "https://kopik.io/api/mcp?base=employment-law-leave-x7k2",
"headers": { "Authorization": "Bearer kpk_…" }
}
}
}Limits & safety
Limits protect the service and your credit. Going over one returns 429 with code rate_limited.
| What | Limit |
|---|---|
| Questions per user (all channels) | 20 per minute · 1,000 per day |
| Your questions to your own bases | Free · fair use of 200 per day |
| Requests per IP address (REST and MCP) | 120 per minute, then 429 with Retry-After: 60 |
| MCP JSON-RPC batch | 10 requests max |
| Knowledge bases per account | 20 |
| Per base | 1,000 documents · 20 million characters |
| Per document | 4 MB per file · 2 million characters · 1,500 PDF pages |
| Uploads | 120 per hour |
Safety for agents: set maxPriceCents so a price change never costs a looping agent more than planned; branch on the error code; and treat base content as data (over MCP it arrives wrapped in <kopik-untrusted> tags). Answers are written from the base's passages only, but they can still be wrong: they aren't legal, medical or financial advice.
Examples
JavaScript (fetch, Node 18+)
const res = await fetch("https://kopik.io/api/v1/bases/employment-law-leave-x7k2/query", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KOPIK_API_KEY}`,
"Content-Type": "application/json",
"x-kopik-locale": "en",
},
body: JSON.stringify({
question: "How many days of leave do I get for my wedding?",
mode: "answer",
}),
});
const data = await res.json();
if (!res.ok) {
// data.code is stable: branch on it (e.g. "insufficient_credit", "rate_limited").
throw new Error(`Kopik ${res.status} ${data.code}: ${data.error}`);
}
console.log(data.answer);
for (const p of data.passages) console.log(`[${p.n}] ${p.document}`);
console.log(`Cost: €${(data.costCents / 100).toFixed(2)} · balance: €${(data.balanceCents / 100).toFixed(2)}`);Python (requests)
import os
import requests
res = requests.post(
"https://kopik.io/api/v1/bases/employment-law-leave-x7k2/query",
headers={
"Authorization": f"Bearer {os.environ['KOPIK_API_KEY']}",
"x-kopik-locale": "en",
},
json={"question": "How many days of leave do I get for my wedding?", "mode": "answer"},
timeout=60,
)
data = res.json()
if not res.ok:
# data["code"] is stable: branch on it (e.g. "insufficient_credit", "rate_limited").
raise RuntimeError(f"Kopik {res.status_code} {data['code']}: {data['error']}")
print(data["answer"])
for p in data["passages"]:
print(f"[{p['n']}] {p['document']}")
print(f"Cost: €{data['costCents'] / 100:.2f} · balance: €{data['balanceCents'] / 100:.2f}")An answer can take about twenty seconds: use a timeout of at least 60 seconds, and retry after a pause on 429 (rate_limited).
Ready to plug in a base?
Create a key and top up your credit, or start with a private base of your own, free to query.