API Reference›Indexes
API Reference

Indexes

Create, list, and delete indexes on cached tables. Supports sorted indexes for range queries and BM25 full-text indexes for keyword search.

List indexes on a table

GET/v1/connections/{connection_id}/tables/{schema}/{table}/indexes

List all indexes created on a table.

Path parameters

  • connection_id string — required. The database's default_connection_id, as returned when the database is created or fetched. It is returned by Create database and Get database. Each index returned by List indexes across tables in a database also carries the connection_id of its table.
  • schema string — required. Schema name
  • table string — required. Table name

Response 200 — Indexes listed

  • indexes IndexInfoResponse[] — required
    • algorithm string,null — How this vector index organises the vectors it searches: hnsw or ivf. Absent for BM25 and sorted indexes.
    • columns string[] — required
    • created_at string — required
    • index_name string — required
    • index_type string — required
    • metric string,null — Distance metric this index was built with. Only present for vector indexes.
    • nlist integer,null — Number of clusters an ivf index was built with. This can be smaller than the nlist requested when the index was created, because the number is capped by how many vectors the clusters were fitted to. Absent for every other kind of index. Min: 0
    • probe_fraction number,null — How much of an ivf index a search reads, as a fraction greater than 0 and at most 1, when the index was created with one. When absent, the server chooses how much each search reads: a width measured on this index's own data when it was built, scaled to the number of results a search asks for, or a server default when no measurement could be made. Absent for every other kind of index.
    • source_column string,null — Source text column for an embedding-backed vector index. A query searches it via vector_distance(<source_column>, …); the indexed columns hold the generated embedding column instead. Absent for BM25, sorted, and direct (existing-column) vector indexes.
    • status IndexStatus — required. Index build status exposed to API consumers.
    • updated_at string — required
    • vector_precision string,null — How precisely this vector index stores each number of a vector. Always present for an ivf index, which stores int8 unless it was created with another precision. For an hnsw index it is present only when the index was created with an explicit precision; absent means it stores at the same precision as the column. Absent for BM25 and sorted indexes.
{
  "indexes": [
    {
      "algorithm": "string",
      "columns": [
        "string"
      ],
      "created_at": "2026-01-01T00:00:00Z",
      "index_name": "string",
      "index_type": "string",
      "metric": "string",
      "nlist": 0,
      "probe_fraction": 0,
      "source_column": "string",
      "status": "ready",
      "updated_at": "2026-01-01T00:00:00Z",
      "vector_precision": "string"
    }
  ]
}

Errors

StatusDescription
404Table not found
500Internal server error

Create an index on a table

POST/v1/connections/{connection_id}/tables/{schema}/{table}/indexes

Create a sorted, BM25 full-text, or vector index on a table. The table must have data loaded, and must not declare a sort order (sorted_by): a table with a sort order cannot be indexed. A table can hold several indexes, with two limits: it holds at most one vector index, whichever algorithm it uses; and a vector index that generates its own embeddings (created with embedding_provider_id) must be the only index on its table, so it cannot be added alongside an existing index and no further index can be added alongside it. A request refused for any of these reasons gets 400, including when async is set. The request body describes the rules in full.

Path parameters

  • connection_id string — required. The database's default_connection_id, as returned when the database is created or fetched. It is returned by Create database and Get database. Each index returned by List indexes across tables in a database also carries the connection_id of its table.
  • schema string — required. Schema name
  • table string — required. Table name

Request body

  • algorithm "hnsw" | "ivf" — How a vector index organises the vectors it searches. Omit this field for hnsw, which is the default. hnsw — builds a graph of the vectors and keeps it in memory. Searches are very fast, and the memory a search needs grows with the whole table, so a large enough table cannot be served at all. ivf — groups the vectors into clusters and reads only the clusters nearest the search. Searches are considerably slower than hnsw, and the memory a search needs follows how much of the index it reads rather than the size of the table, so a table far too large for hnsw can still be searched. It keeps a copy of the table's rows beside the vectors so a search is answered without reading the table; that copy is extra storage, and how much depends on vector_precision, which decides how compactly the copied vectors are held. Available with the l2 and cosine metrics, for columns that already hold vectors and for text columns with an embedding provider, which generates the vectors. Search an ivf index with ORDER BY vector_distance(column, '<query>') LIMIT <k> (or the matching distance function and a query vector), naming the columns to return rather than selecting *; vector_search works with hnsw indexes only. Generating embeddings for a very large table (tens of millions of rows) is not supported yet.
  • async boolean — When true, create the index as a background job and return a job ID for polling. Default: false
  • async_after_ms integer,null — If set (requires async = true), wait up to this many milliseconds for the index build to finish: if it completes in time the index is returned (201), otherwise a 202 with a job ID to poll. Must be between 1000 and the server maximum; a value out of that range, or set without async = true, is rejected with 400. Min: 1000
  • columns string[] — required. Columns to index. Required for all index types.
  • description string,null — User-facing description of the embedding (e.g., "product descriptions").
  • dimensions integer,null — Output vector dimensions. Some models support multiple dimension sizes (e.g., OpenAI text-embedding-3-small supports 512 or 1536). If omitted, the model's default dimensions are used. Min: 0
  • embedding_provider_id string,null — Embedding provider ID. When set for a vector index, the source column is treated as text and embeddings are generated automatically. The vector index is then built on the generated embedding column ({column}_embedding by default), with either algorithm. For an ivf index the metric must be l2 or cosine: a provider whose metric is dot is refused unless metric names one of those. Every later load and text search embeds with the same provider, and is refused if the provider's model or endpoint has changed since the index was created; changing its API key is fine. Dropping the index keeps the generated column as an ordinary column, which loads may leave out (its rows get no vector) and which a new embedding index can generate into again.
  • index_name string — required
  • index_type "sorted" | "bm25" | "vector" — Index type. sorted supports range queries, bm25 full-text search, and vector similarity search. Default: "sorted"
  • metric string,null — Distance metric for vector indexes: "l2", "cosine", or "dot". When omitted, defaults to "l2" for float array columns or the provider's preferred metric for text columns with auto-embedding.
  • nlist integer,null — Number of clusters an ivf index divides the vectors into. More clusters means each one holds fewer vectors, so a search of the same effort reads less data. Omit this to let the number be chosen from the table's size. Min: 1. Max: 65536
  • output_column string,null — Custom name for the generated embedding column. Defaults to {column}_embedding.
  • probe_fraction number,null — How much of an ivf index a search reads, as a fraction greater than 0 and at most 1. Higher finds more of the true nearest neighbours and takes longer. This is a fraction rather than a number of clusters on purpose: the same number of clusters is a different share of the index whenever nlist changes, and results would quietly get worse. Omit this for the server's default. Min: 0. Max: 1
  • vector_precision "float64" | "float32" | "float16" | "float8" | "int8" — How precisely a vector index stores each number of a vector. Lower precision shrinks the index so a larger table can be indexed within the same memory, and lets searches run on a smaller instance. For an ivf index it also shrinks what every search reads, because a search reads part of that stored copy. Omit this field to get each algorithm's own default: an hnsw index stores vectors at the same precision as the column, and an ivf index stores them as int8. The quality figures below come from one benchmark — 1536-dimension text embeddings, cosine distance, default search settings — and are a guide, not a guarantee. Other models, dimensions, distance metrics and data distributions behave differently, so measure on your own data before moving a production index to a lower precision. float32 — on a float64 column this halves the index. Widely used embedding models emit 32-bit values, so for those nothing is lost; vectors that genuinely carry more than 32 bits of precision will lose some. float16 — half the memory of float32. In that benchmark its results matched float32 to within 0.1 percentage points. float8 — a quarter of the memory of float32. In that benchmark it scored about 4 percentage points below float32, and raising the search effort did not close the gap, so treat the reduction as permanent for a given index. float64 — accepted only for a column that already holds double-precision values; it cannot add precision the stored data does not have. int8 — for an ivf index only, and its default. A quarter of the size of float32, which is a quarter of the bytes every search reads. On the benchmark this index was designed against, a search reading every cluster found about 99.5% of the neighbours an exact search finds, which is as accurate as int8 gets; a search at the default width reads fewer clusters and finds fewer (see probe_fraction). With cosine that accuracy holds however widely your vectors vary in magnitude; with l2 it falls as they spread — around 93% of the neighbours once the largest magnitude is about 16 times the smallest, and lower beyond that. Use float32 instead to store the column as written, at four times the size and four times the bytes per search. An ivf index accepts int8 and float32 only: it stores its copy as a table, and the remaining values have no column type to be stored in or are no smaller than int8. An hnsw index accepts everything except int8; float8 is its 8-bit option. Changing this means dropping the index and creating it again. It affects only the index: the table's own values are never altered, and text columns indexed with a generated embedding are not re-embedded.
{
  "index_name": "orders_customer_id",
  "index_type": "sorted",
  "columns": [
    "customer_id"
  ]
}

Response 201 — Index created

  • algorithm string,null — How this vector index organises the vectors it searches: hnsw or ivf. Absent for BM25 and sorted indexes.
  • columns string[] — required
  • created_at string — required
  • index_name string — required
  • index_type string — required
  • metric string,null — Distance metric this index was built with. Only present for vector indexes.
  • nlist integer,null — Number of clusters an ivf index was built with. This can be smaller than the nlist requested when the index was created, because the number is capped by how many vectors the clusters were fitted to. Absent for every other kind of index. Min: 0
  • probe_fraction number,null — How much of an ivf index a search reads, as a fraction greater than 0 and at most 1, when the index was created with one. When absent, the server chooses how much each search reads: a width measured on this index's own data when it was built, scaled to the number of results a search asks for, or a server default when no measurement could be made. Absent for every other kind of index.
  • source_column string,null — Source text column for an embedding-backed vector index. A query searches it via vector_distance(<source_column>, …); the indexed columns hold the generated embedding column instead. Absent for BM25, sorted, and direct (existing-column) vector indexes.
  • status IndexStatus — required. Index build status exposed to API consumers.
  • updated_at string — required
  • vector_precision string,null — How precisely this vector index stores each number of a vector. Always present for an ivf index, which stores int8 unless it was created with another precision. For an hnsw index it is present only when the index was created with an explicit precision; absent means it stores at the same precision as the column. Absent for BM25 and sorted indexes.
{
  "algorithm": "string",
  "columns": [
    "string"
  ],
  "created_at": "2026-01-01T00:00:00Z",
  "index_name": "string",
  "index_type": "string",
  "metric": "string",
  "nlist": 0,
  "probe_fraction": 0,
  "source_column": "string",
  "status": "ready",
  "updated_at": "2026-01-01T00:00:00Z",
  "vector_precision": "string"
}

Response 202 — Index build accepted and running in the background; poll the returned job for status

  • id string — required. Job ID for status polling.
  • status JobStatus — required. Current status of a background job. One of five values: - pending — queued, not started yet. - running — currently executing. - succeeded — finished, everything it was asked to do worked. - partially_succeeded — finished, but some of its work failed while the rest succeeded (for example some of the databases in a bulk creation). Read error_message for what was skipped. - failed — did not finish; every retry was used up. Read error_message. succeeded, partially_succeeded, and failed are final — a job in one of those will not change again, so a poll loop can stop there. pending and running mean keep polling. Treat partially_succeeded as its own outcome rather than folding it into either success or failure: the job produced a result, but not for all of its work.
  • status_url string — required. URL to poll for job status.
{
  "id": "string",
  "status": "pending",
  "status_url": "string"
}

Errors

StatusDescription
400Invalid request
404Table not found
500Internal server error

Delete an index

DELETE/v1/connections/{connection_id}/tables/{schema}/{table}/indexes/{index_name}

Delete a specific index from a table. An index cannot be deleted while a load into its table is running; that request gets 409 and can be retried once the load finishes. Deleting a vector index that generated its own embeddings keeps the generated column as an ordinary column of the table.

Path parameters

  • connection_id string — required. The database's default_connection_id, as returned when the database is created or fetched. It is returned by Create database and Get database. Each index returned by List indexes across tables in a database also carries the connection_id of its table.
  • schema string — required. Schema name
  • table string — required. Table name
  • index_name string — required. Index name

Response 204 — Index deleted

Errors

StatusDescription
404Index not found
409A load into the table is running; retry once it finishes
500Internal server error

List indexes across tables in a database

GET/v1/indexes

List all indexes in the database identified by the required X-Database-Id header, paginated. Optional filters narrow by connection, schema, table, or index type.

Query parameters

  • connection_id string — Filter to one connection
  • schema string — Filter by schema name
  • table string — Filter by table name
  • index_type string — Filter by index type
  • limit integer — Max indexes per page
  • cursor string — Pagination cursor

Headers

  • X-Database-Id string — required. Database to scope to (required)

Response 200 — Indexes listed

  • count integer — required. Min: 0
  • has_more boolean — required
  • indexes IndexEntryResponse[] — required
  • limit integer — required. Min: 0
  • next_cursor string,null
{
  "count": 0,
  "has_more": true,
  "indexes": [
    {
      "algorithm": "string",
      "columns": [
        "string"
      ],
      "created_at": "2026-01-01T00:00:00Z",
      "index_name": "string",
      "index_type": "string",
      "metric": "string",
      "nlist": 0,
      "probe_fraction": 0,
      "source_column": "string",
      "status": "ready",
      "updated_at": "2026-01-01T00:00:00Z",
      "vector_precision": "string",
      "connection_id": "string",
      "schema_name": "string",
      "table_name": "string"
    }
  ],
  "limit": 0,
  "next_cursor": "string"
}

Errors

StatusDescription
400Missing X-Database-Id or bad cursor
404Database not found
500Internal server error