Semantic search
Run semantic search over your indexed transcriptions using hybrid retrieval — dense embeddings (OpenAI text-embedding-3-large) combined with sparse BM25 vectors fused via Reciprocal Rank Fusion (RRF). Each hit returns the matched text excerpt, precise timestamps, and source media metadata.
Before searching, transcriptions must be indexed via an indexer job
Typical search flow
- Complete a transcription job (
serviceType: transcription), note thetextSourceId(aliastranscriptionId) fromresultSummary. - Submit an indexer job (
serviceType: indexer) withinput.textSourceIds(one to 50txt_…IDs). Unknown or empty transcripts are rejected at create (notFound/validationError) — see Create an indexer job. Wait forstatus: completed. - Optional:
GET /v1/catalog/indexed-transcriptionsto list what is in the vector index for the project. - Call
POST /v1/catalog/searchwith your query and project scope.
Requires ai:search on the project API key. Each search consumes one AI search credit (1,000/month on Free). Indexer jobs require job:create and consume one indexing credit per indexed text source (txt_…) (100/month on Free).
Vectors are stored in the project's active Qdrant cluster (platform default or BYO). Configure the cluster in the dashboard or see Vector store. Listed transcripts reflect the live index on that cluster.
Responses use camelCase field names (same as the rest of the public API). The catalog worker returns snake_case internally; the Node API normalizes catalog payloads before they reach clients.
List indexed transcriptions
/v1/catalog/indexed-transcriptionsWhen to use
- Search inventory — show what is searchable before running queries.
- Automation — reconcile index state after bulk indexer jobs or before deleting stale entries.
- Does not return chunk text — use search for excerpts or retrieve the transcript for full segments.
Requires permission
ai:search
Large collections may set truncated: true when the scan hits an internal point limit — treat the list as partial and paginate by re-indexing or filtering in your app if needed.
items[] fields:
| Field | Type | Description |
|---|---|---|
transcriptionId | string | Text source present in the index (txt_…; alias of textSourceId). |
chunkCount | integer | Number of vector chunks indexed for this transcription. |
latestJobId | string | null | Most recent indexer job id that wrote vectors for this transcript. |
displayName | string | null | Display name copied from the transcript at index time. |
customerJobRef | string | null | Caller reference from indexer userMetadata.customerJobRef. |
Query parameters
- chunkTypestring, optional
- Optional filter. Currently only
transcriptionchunks are indexed.
Returns
collection, total, truncated, pointsScanned, and items[] with transcriptionId, chunkCount, optional latestJobId, displayName, and customerJobRef.Remove from search index
/v1/catalog/indexed-transcriptions/{transcriptionId}When to use
- Opt out of search — remove a transcript from semantic search without deleting the transcript record.
- Before re-index — optional cleanup; re-indexing also replaces vectors on success.
- Idempotent for missing vectors — returns
deletedCount: 0when nothing was indexed.
Requires permission
ai:search
Parameters
- transcriptionIdstring
- Transcription to remove from the index (txt_…).
Query parameters
- chunkTypestring, optional
- Optional chunk-type filter (same as list).
Returns
transcriptionId, collection, and deletedCount (number of points removed).Search transcriptions
/v1/catalog/searchWhen to use
- Natural-language queries — find moments across many transcriptions without knowing exact wording (
explain cache invalidation,mention of revenue target). - Deep-link to the moment — each result includes
start/endseconds andstartTimestamp/endTimestampfor video seek. - Optional reranking — set
rerank: truewhen the catalog service is configured with a reranker (RERANKER=bgeorflashrankorcohere) for higher-precision ordering at the cost of extra latency.
Requires permission
ai:search
Prefer groups[] for UI that shows one card per transcript with multiple moments underneath. Flat results[] mirrors the same hits in score order. The Node API adds groups[] and groupCount without removing results[].
Groups are ordered by topScore (best hit in that transcript). Hits inside a group are ordered by score descending. Group key is transcriptionId + displayName.
Resolve the parent transcription job with GET /v1/catalog/indexed-transcriptions → latestJobId, or GET /v1/transcriptions/{transcriptionId} → jobId. Use transcriptionId plus start for media seek — not the job id in search payloads.
Chunk vectors store only per-hit metadata (display name, source URL, segment window, etc.). Transcription-level fields such as duration, engine, or full segment lists are not duplicated on every vector — fetch the transcript API when you need them.
results[] and groups[].hits[] share the same hit fields:
| Field | Type | Description |
|---|---|---|
text | string | Matched chunk text excerpt. |
score | number | Relevance score from RRF fusion (or reranker when rerank: true). |
transcriptionId | string | null | Source transcription identifier. |
projectId | string | null | Project the chunk belongs to. |
workspaceId | string | null | Owning workspace. |
displayName | string | null | Transcript display name at index time. |
chunkType | string | null | Chunk type (currently transcription). |
chunkIndex | integer | null | Zero-based chunk position within the transcription. |
startSegmentId | string | null | First transcription segment id in this chunk. |
endSegmentId | string | null | Last transcription segment id in this chunk. |
start | number | null | Chunk start time in seconds. |
end | number | null | Chunk end time in seconds. |
startTimestamp | string | null | Human-readable start time (M:SS or H:MM:SS). |
endTimestamp | string | null | Human-readable end time. |
sourceUrl | string | null | Media URL stamped at index time (when available). |
language | string | null | Language stamped at index time (when available). |
customerJobRef | string | null | Indexer job reference from userMetadata.customerJobRef. |
Each group also includes transcriptionId, displayName, and topScore. groups[].hits[] includes the core seek fields above (text, score, segment window, times, chunkIndex, customerJobRef). Flat results[] includes every column in the table (for example chunkType, startTimestamp, sourceUrl, projectId).
With reranking and chunk type filter:
{
"query": "discussion about architecture decisions",
"limit": 5,
"rerank": true,
"chunkType": "transcription"
}The reranked field in the response is true only when rerank: true was sent and a reranker is actually configured on the server. It is safe to always send rerank: true — the server degrades gracefully to RRF ordering when no reranker is available.
Parameters
- querystring
- Free-text search query. 1–2000 characters.
- limitinteger, optional
- Max results to return. 1–50, default 10.
- rerankboolean, optional
- Apply a second-stage reranker for higher-precision ordering. Only has an effect when the catalog service is configured with
RERANKER=bgeorRERANKER=flashrankorRERANKER=cohere. Defaultfalse. - chunkTypestring, optional
- Optional filter on chunk type. Currently only
transcriptionis produced by the indexer. Omit to search all types. - projectIdstring, optional
- Project scope override. Defaults to the project bound to your API key via
X-Project-Idheader. Ignored when the key already scopes to a project.
Returns
query, collection, total, reranked, flat results[], plus groupCount and groups[] (hits grouped per transcript). All fields are camelCase in the public API.