Search
The search API
POST /api/v1/search/query takes a question in plain language and returns ranked passages from the licensed data your organisation may search: its own data and everything partners offer on the marketplace. Retrieval is hybrid (dense, sparse and late-interaction vectors from BGE-M3), and every passage comes with its licence and provenance.
Request
POST /api/v1/search/query
Authorization: Bearer sk_live_...
Content-Type: application/json
Idempotency-Key: 7d0c2e4a-5f1b-4c8e-9a3d-2b6f1e0c9a77
{
"query": "Which notice period applies to a fixed-term contract?",
"top_k": 8,
"domain": "legal",
"use_case": "rag",
"scope": "all"
}
| Field | Type | Meaning |
|---|---|---|
query required | string, 1–4,000 characters | What to search for, in plain language. A blank query is refused with 422. |
top_k | integer, 1–50, default 8 | How many passages to return at most. You pay for the tokens in what is returned, so ask for what you will use. |
domain | string or null | A subject domain such as legal, finance or medical. It steers query expansion and, where one is configured, the domain's own index. |
content_type | string, default general | The kind of content to search; picks the index. Most searches leave it as general. |
use_case | string, default rag | What you will do with the passages: search, rag, finetune, train, export or redistribute. Only passages whose licence allows that use are returned, and the use is recorded with the search. |
collection | string or null | An index name. Needed only to check a scoped key; a scoped key may only name an index in its scopes. |
scope | string or null | Where to search: mine, marketplace, all or listing:<id>. Left out, your default applies. See Search scope. |
min_tier | string or null | Reserved. Accepted but not applied yet. |
Unknown fields are ignored, never rejected.
Filters
The filters are domain, content_type and use_case, plus what your key's scopes allow. Partners' private data is never searched, whatever the scope.
Search scope
scope narrows what a search may reach; it never widens it.
| Scope | Searches |
|---|---|
mine | Only your organisation's own data. Nothing from another organisation, not even data another organisation has shared with you. |
marketplace | Only other organisations' published data. None of your own, published or not. |
all | Both. |
listing:<id> | One listing. GET /api/v1/search/listings lists them, each with the scope value that searches only it. Until collections ship, a listing is one dataset. |
A search that names no scope uses, in order: the default_scope:<scope> its API key was created with (add it to the key's scopes); your organisation's intent, where knowledge_bank means mine; otherwise all. An existing key and organisation therefore search what they always did.
GET /api/v1/search/scope: your default scope, why it applies (default_source:key,intentordefault), your organisation'sintent, andgraph_evidence, whether the dashboard can show an evidence bundle with your answers.PUT /api/v1/search/scopewith{"intent": "knowledge_bank"},"marketplace"ornull: say what your organisation uses TRIVDA for. A key with its owndefault_scope:keeps it.GET /api/v1/search/listings?scope=marketplace&q=labour&limit=20(limit1–100): the listings a scope covers, with title, origin, who it is from, licence and size. Free.
An invalid scope is refused with 422 before anything is charged.
Response
{
"query_id": "qry_5b0e7d1c9a2f4e3b8c6d0a1f2e3d4c5b",
"latency_ms": 486,
"results": [{
"rank": 1,
"score": 0.8731,
"text": "A fixed-term employment ends when the term expires ...",
"readiness_tier": "gold",
"source_uri": "https://example.org/handbook/chapter-4.pdf",
"concept_ids": ["eurovoc:1234"],
"license": {
"spdx": "CC-BY-4.0",
"odrl": { "permission": [ ... ], "duty": [ ... ] },
"duo": null
},
"citation": {
"source_uri": "https://example.org/handbook/chapter-4.pdf",
"license_spdx": "CC-BY-4.0",
"odrl": { ... },
"dataset_id": "labour-law-handbook",
"jurisdiction": "SE",
"attribution": ["attribution"],
"pii_tier": "green"
},
"content_class": null,
"pii_tier": "green",
"origin": "marketplace",
"listing_id": "labour-law-handbook"
}],
"scope": "all",
"listings": [
{ "listing_id": "labour-law-handbook", "source": "Example Publishing", "hits": 3, "ranks": [1, 2, 5] }
],
"query_expansion": { ... },
"used": { ... },
"billing": {
"tokens": 2210, "cost_eur": 2.21, "list_cost_eur": 2.21,
"credits_charged": 2210, "free_query_used": false,
"balance_credits": 47790, "replayed": false
}
}
query_id- The id of this search. Use it to fetch the results again for free (below) and to find the search in your usage.
results[]- Up to
top_kpassages, best first. Empty when nothing licensed answers the question; you never get the nearest unlicensed match instead. rank,score- Position and relevance score (higher is better; comparable within one search, not across searches).
text- The passage itself, ready to ground an answer.
readiness_tierbronze,silverorgold: how thoroughly the source was parsed, screened and checked before it was indexed.concept_ids- Open-ontology concepts the passage mentions.
origin,listing_idownfor your organisation's data,marketplacefor another's; and the listing the passage belongs to.scope- The scope the search ran in.
listings[]- The marketplace passages grouped by listing, best rank first:
listing_id,source,hitsand theranksthey hold inresults. Your own passages are not grouped. query_expansion,used- How the query was expanded and which retrieval stages ran. For debugging; the shape may change.
billing- What this call cost. See Cost of a search.
The licence and provenance object
Every passage carries the terms it is licensed under and where it came from. Read them before you use the passage, and pass the duties on to your users.
license.spdx- The SPDX identifier of the licence (
CC-BY-4.0,CC0-1.0, ...) or aLicenseRef-id for the owner's own terms. license.odrl- The owner's machine-readable policy in ODRL: what is permitted, prohibited and required.
license.duo- Data-use ontology terms, for research data that carries them; otherwise null.
citation.source_uri- The document the passage came from, for the citation in your answer.
citation.dataset_id- The dataset that holds the passage.
citation.jurisdiction- Where the data's rights were granted, for example
SE,EUorglobal. citation.attribution- The duties your answer must honour, such as
attribution. When it listsattribution, name the source where you show the passage. pii_tier- The personal-data band of the passage,
green,yellow,redorblack.greenmeans none was found. The licence check blocksredandblackpassages by default. content_classfictionorsyntheticwhen the owner labelled the content as not factual; null otherwise. Never present such a passage as fact.
Licence checks run per passage before the results are returned, against the use_case you sent. Searches and their licence decisions are recorded in TRIVDA's tamper-evident audit ledger.
Idempotency-Key
Send an Idempotency-Key header (any string up to 255 characters; a UUID is a good choice) so that a retry after a timeout or a dropped connection is not charged twice.
- A request with a key that was used in the last 24 hours by your organisation, with the same body, returns the stored response: the same
query_idand results,billing.replayed: true, nothing charged, and the headerIdempotent-Replayed: true. - The same key with a different body is refused with
422. - If the first request with that key is still running, the second gets
409; retry after a moment. - A key longer than 255 characters is refused with
400. - Without the header, every request is a new search and is charged.
Cost of a search
The cost is in the response headers and in billing, so you can meter spend without parsing the results.
| Header | Meaning |
|---|---|
X-Query-Cost | Euros charged for this call, with 6 decimals, for example 2.210000. 0.000000 for a free search, a search with no results and a replay. |
X-Tokens-Used | Billable tokens in the returned passages. 0 on a replay. |
X-Credits-Remaining | Wallet credits left after this call. 1,000 credits = €1. |
Idempotent-Replayed | true when the response is a stored replay. Absent otherwise. |
billing.tokens- Billable tokens in the returned passages.
billing.cost_eur- What was charged. 0 when a free search was used.
billing.list_cost_eur- The price of what was returned, whether or not it was charged.
billing.credits_charged- Credits taken from the wallet.
billing.free_query_usedtruewhen this search used one of the organisation's free searches.billing.balance_credits- Credits left after the call, the same as
X-Credits-Remaining. billing.replayedtruefor an Idempotency-Key replay.
How the price is computed is on Pricing and billing.
Fetching results again
GET /api/v1/search/results/{query_id} returns a search's results again for 7 days, free. It is rate-limited like a search, and only the organisation that ran the search can fetch it.
curl -sS "$TRIVDA_URL/api/v1/search/results/qry_5b0e7d1c..." \
-H "Authorization: Bearer $TRIVDA_API_KEY"
Related endpoints
GET /api/v1/search/collections: the indexes your key can search, with passage counts.GET /api/v1/search/quota: your rate limits and what is left of them.GET /api/v1/search/listingsandGET /api/v1/search/scope: see Search scope.GET /api/v1/pricing: the current price list, no key needed.GET /api/v1/usage/queries: your recent searches with their cost.
Full schemas are in the API reference.