How it works
From a request to a ranked result.
Bolt Search is a REST API in front of a search engine, with a sync worker for PostgreSQL and a web console. This page follows a request through it and says where each kind of data lives.
Architecture
Everything you or the console do goes through the same REST API. It authenticates the caller, resolves the project and passes the work to the search engine.
- Callercurl, an SDK or the web consoleAuthorization: ApiKey bsk_…HTTPS
- REST APIauthenticate the keyresolve the project, check the rolecheck credits, prefix index namesgRPC
- Search engineparses the queryBM25 ranking, aggregationsreads
- Index filesone set per indexnamed with the project prefixkept on disk
- Your PostgreSQLa table or a SELECTread-only accessrows past the cursor
- Sync workerpolls on an intervalsaves the cursorbulk index, gRPC
- Search engineparses the queryBM25 ranking, aggregationswrites
- Index filesone set per indexnamed with the project prefixkept on disk
- Account databasePostgreSQLusers, projects, memberskey hashes, analytics events
- Sync stateSQLitedata sources, jobs, cursorslogs, resync history
- Billing servicecredit balancereceives project id and countsno documents or queries
A search request, step by step
What happens between curl and the first hit.
- Your application sends
POST /v1/movies/_searchwithAuthorization: ApiKey bsk_…. The API accepts the request over HTTPS and gives it a request id. - The key is looked up. The API hashes the key with SHA-256 and finds that hash in the account database. An unknown or revoked key is answered with 401.
- The project is resolved. The key belongs to exactly one project, so the request needs no project header. The project’s slug becomes the prefix of every index name used from here on.
- The role is checked. Searching needs viewer. Indexing, deleting and sync management need editor. A key without the role gets 403.
- Credits are checked and the attempt is counted. For search, count and scroll only: the project’s credit balance is checked, and an empty one gets 402. A search, count or scroll request is counted as one, however many indexes it names; an
_msearchrequest is counted once per contained search. The counts are sent to billing about once a minute. - The request is passed to the search engine. The API changes
moviestoyour-project__moviesand sends the query to the engine over gRPC. The engine parses the query, searches the index on disk and ranks hits with BM25. - The response is returned. The API removes the project prefix from index names in the hits and returns an Elasticsearch-shaped JSON body.
- The search is recorded for analytics. In the background, without delaying the response: query text, result count and latency.
How data gets in
Two paths lead to the same index.
- REST API
PUT /v1/{index}/_doc/{id}andPOST /v1/_bulkpass through the same authentication, project and role checks, then the engine writes the documents to the index. Indexing is not metered.- PostgreSQL sync
- A sync worker reads rows from your database and sends them to the engine in bulk. It authenticates nothing on your side except the database login you give it. See PostgreSQL sync.
Both paths replace a document that has the same id, so running them together on one index is safe as long as the ids agree.
Where your data lives
Five kinds of data, each in one place.
| What | Where it is kept | Notes |
|---|---|---|
| Users, projects, members and API key hashes | Account database (PostgreSQL) | Passwords are stored as bcrypt hashes and refresh tokens as SHA-256 hashes. API keys are stored only as SHA-256 hashes. |
| Index documents and mappings | The search engine’s index files on disk | One set of files per index, named with the project prefix. Deleting an index removes them. |
| Data sources, sync jobs, cursors, logs and resync history | Sync state database (SQLite) | A data source’s password is never returned by the API. |
| Search and click events for analytics | Account database (PostgreSQL) | Query text, index, result count, latency, user id and source. Events older than the retention period are deleted; the product’s default period is 90 days. |
| Credit balance, purchases and usage | Billing service | Receives the project id, the meter name and counts. It does not receive your documents or your queries. |
How long it is kept
Retention as the product defines it. To delete a project’s data or your account, email us from the address on the account.
- Analytics events
- Deleted after the retention period. The product’s default is 90 days.
- Credits
- Prepaid credits never expire.
- Indexes and documents
- Kept until you delete them. Deleting a project removes its indexes, sync jobs and data sources.
- Sync cursor
- Kept when a job is stopped. Reset clears it, and the next start reads everything again.
- Scroll
- Stateless: a scroll id carries its own signed state, so nothing is held on the server between pages.
Metering and billing
Searching is the only thing that uses credits.
- Each authorized search, count and scroll request is one attempt, including a request that fails after it was admitted and a retry you send yourself. A search across several indexes is one attempt. An
_msearchrequest is one attempt per contained search. - One credit pays for 10 search attempts.
- Before a metered request runs, the project’s balance and state are checked. The answer is cached for about a minute. If billing cannot be reached, the last answer is used for up to 15 minutes; after that, metered requests fail with HTTP 503 rather than run unchecked.
- Counts are sent to billing about once a minute, so the balance can overshoot slightly between reports.
- Writes, indexing, sync, analytics and index administration are not metered.
See it work on your own data.