Reference›CLI Reference

CLI Reference

Install

macOS, Linux

curl -fsSL https://hotdata.dev/install | sh

Uses Homebrew (brew install hotdata-dev/tap/cli) when brew is present, otherwise installs a prebuilt binary into ~/.hotdata/cli. Set HOTDATA_NO_BREW=1 to skip Homebrew.

From source (requires Rust)

cargo install --path .

Or download a binary from Releases. Update with hotdata manage upgrade. In an interactive terminal the CLI checks for a new release before API commands and offers to upgrade; set HOTDATA_NO_UPDATE_CHECK=1 to skip the check.

Connect

Authenticate via browser:

hotdata auth login

This launches a browser window where you can sign in and authorize the CLI. To create a new account:

hotdata auth register        # GitHub OAuth; add --email for email + password

Check status or sign out:

hotdata auth status
hotdata auth logout

Alternatively, pass an API key directly:

hotdata <command> --api-key <api_key>

Or set the HOTDATA_API_KEY environment variable (also loaded from .env files):

export HOTDATA_API_KEY=<api_key>
hotdata <command>

API key priority (lowest to highest): config file → HOTDATA_API_KEY env var → --api-key flag.

Command reference

The full command surface. The top level has eight groups — auth, workspaces, databases, query, jobs, ingest, search, and manage. Run hotdata <command> --help for the complete flags on any of them.

CommandDescription
auth loginLog in via browser
auth registerCreate a new account via browser (GitHub OAuth; --email for email + password)
auth logoutRemove authentication for a profile
auth statusShow authentication status
workspaces listList all workspaces
workspaces useSet the default workspace
databases listList instant databases in the workspace
databases countCount instant databases in the workspace
databases showShow details for an instant database
databases createCreate a new instant database
databases forkFork a database into a new, independent database
databases lineageShow a database's fork family tree
databases attachAttach another database so its tables are queryable
databases detachDetach a previously attached database
databases useSet the current (default) database
databases unsetClear the current database
databases removeDelete a database and all its tables
databases loadLoad a csv, json, or parquet file, or a saved result, into a table
databases tables listList tables in a database
databases tables addDeclare a table, with its key and layout
databases tables showShow column definitions for a table
databases tables loadLoad a file or saved result into a table: replace, append, or match rows by key
databases tables removeDelete a table from a database
databases context listList named contexts in a database
databases context showPrint context content to stdout
databases context pullDownload context to ./<NAME>.md
databases context pushUpload ./<NAME>.md as named context
databases queryExecute a SQL query against a database
databases query statusCheck a running query and retrieve results
databases queries listList query runs
databases results getShow a stored query result by ID
databases results listList stored query results
query "<sql>"Execute a SQL query (shortcut for databases query)
query statusCheck a running query and retrieve results
jobs listList background jobs (active by default)
jobs <id>Show one background job
ingest createCreate a load definition
ingest listList the ingests in the workspace
ingest showShow one ingest: state, selector, destination, schedule
ingest pauseStop an ingest (cancel the active run and future runs)
ingest resumeClear a stop and let the schedule dispatch again
ingest scheduleChange when a scheduled/continuous ingest runs next
ingest logsList the runs of one ingest
ingest runShow one run: status, snapshots, timings
ingest removeDelete an ingest and release its destination table
ingest sources testCheck a config and credentials without creating anything
ingest sources addCreate a datasource and its first config version
ingest sources listList the datasources in the workspace
ingest sources showShow one datasource: state, config, discovery
ingest sources update-configAppend a config version (rotate credentials)
ingest sources removeDelete a datasource
ingest sources typesBrowse the catalog of source types
ingest sources fieldsShow the fields a source family accepts
search "<text>" --index <name>Run a full-text or vector search against an index
search createCreate a search index over a table column
search listList search indexes
search showShow one search index by name
search removeRemove a search index by name
search embeddings listList embedding providers
search embeddings showShow one embedding provider
search embeddings addCreate a new embedding provider
search embeddings updateUpdate an embedding provider
search embeddings removeDelete an embedding provider
manage usageShow workspace usage: queries, bytes scanned, stored bytes
manage completionsGenerate shell completions (bash, zsh, fish)
manage upgradeUpgrade the CLI to the latest release
manage skills installInstall/update the agent skill into agent directories
manage skills statusShow the agent skill's installation status
manage skills listList installed skills (alias for status)
support reportFile a support ticket with the Hotdata team

Global options

OptionDescription
--api-key <key>API key (overrides env var and config)
--no-inputDisable interactive prompts; error instead
-v, --versionPrint version
-h, --helpPrint help

Most commands also accept -w, --workspace-id <id> and -o, --output <table\|json\|yaml>.

Four commands trade yaml for csv — they accept table\|json\|csv and never yaml: query, databases query, search "<text>", and databases results get. Note that databases results list is not one of them; it stays on table\|json\|yaml. query status, databases query status, and search create take no -o at all.

Workspaces

hotdata workspaces list
hotdata workspaces use [<workspace_id>]
  • list shows all workspaces with a * marker on the active one.
  • use switches the active workspace. Omit the ID for interactive selection.
  • The active workspace is the default for all commands that accept -w.

Databases

Instant databases are Hotdata-owned catalogs you populate with csv, json, or parquet files. Tables are addressed as <catalog>.<schema>.<table> in SQL, where <catalog> is the alias set with --catalog at create time.

hotdata databases list [--limit <n>] [--cursor <token>] [-o table|json|yaml]
hotdata databases count
hotdata databases show <name_or_id>
hotdata databases create \
  [--catalog <alias>] \
  [--name <label>] \
  [--table <name>]... \
  [--schema <schema>] \
  [--attach <database[=alias]>]... \
  [--expires-at <duration>] \
  [--if-not-exists]
hotdata databases use <database_id>
hotdata databases unset
hotdata databases remove <name_or_id>
  • create mints a database. --catalog sets the SQL alias (SELECT … FROM <alias>.schema.table); it must be [a-z_][a-z0-9_]* and globally unique. --name is a free-form display label. --table (repeatable) declares tables up front and accepts schema.table dot notation to span schemas. --schema sets the default schema for bare --table entries (default public). --attach (repeatable) attaches another database as a queryable catalog on the new one; accepts a database name, catalog alias, or id, optionally database=alias to set the SQL alias (--attach reference --attach salesdb=sales). --expires-at accepts a relative duration (24h, 7d, 90m) or RFC 3339 timestamp; omitting it means the database never expires. --if-not-exists (requires --name) returns the existing database with that name instead of creating another, so a script can bind to its database on every run.
  • use marks a database as the default for subsequent commands and takes the database id printed by create (unlike show/remove and -d/--database, it does not resolve a name or catalog alias); unset clears it.
  • remove deletes the database and all its tables.

Fork a database

hotdata databases fork [<database>] [--name <label>] [--expires-at <duration>]
  • Forks an instant database into a new, independent database with the same schemas, tables, and data. It answers to the same SQL catalog alias inside its own query scope; the two databases diverge freely afterwards — writes to one never affect the other. Useful for safe experiments before risky changes.
  • The source defaults to the current database. --name defaults to <source-name>-fork.
  • Attached catalogs are re-attached to the fork; indexes are not carried over.
  • A fork answers to the same catalog alias as its source, so once a database has been forked, --catalog is ambiguous for that alias. Address either one by id: databases load --database <id>, -d <id>, or databases use <id>.

Lineage

hotdata databases lineage [<database>] [--forks-limit <n>]
  • Prints the fork family tree from the root down, with the given database (default: the current one) marked in place. Every reachable generation is included.
  • Lineage is a historical record, not a live link: a fork and its source stay independent, and a deleted generation remains in the chain. Forks made before the server recorded lineage carry none.

Attach another database

hotdata databases attach <database> [-d <name_or_id>] [--alias <alias>]
hotdata databases detach <database> [-d <name_or_id>]
  • attach makes another instant database's live tables visible inside this one's query scope, so you can join across the two in a single query without exporting data. Name the other database by name, catalog alias, or id. Reachable in SQL as <alias>.<schema>.<table>, or <catalog-alias>.<schema>.<table> when --alias is omitted.
  • --alias is required when the other database kept the stock default catalog name: default cannot be attached under its own name.
  • -d/--database selects the database to attach into (defaults to the current database).
  • detach removes an attachment, withdrawing visibility without deleting any data. Accepts the database name, catalog alias, or id, or the alias it was attached under.
  • Attaching is read-only — loads still target your own database — and is not transitive: you see the database you attached, not the ones it attached. The source cannot be deleted while you hold it, until it passes its expires_at; after that it can go regardless, and your queries keep working without its tables.

Load a file into a table

hotdata databases load --catalog <alias> --table <table> --file <path> [--format csv|json|parquet]
hotdata databases load --catalog <alias> --table <table> --url <https://...>
hotdata databases load --catalog <alias> --table <table> --upload-id <id>
hotdata databases load --catalog <alias> --table <table> --result-id <id>
hotdata databases load --database <id> --table <table> --file <path>
hotdata databases load --catalog <alias> --table <table> --file <path> \
  --mode replace|append|upsert|update|delete [--key <col>]...
  • --catalog is the alias set at create time; --schema defaults to public. --database <id|name> targets the database directly and takes precedence over --catalog; use it when forks share an alias.
  • Sources: --file uploads from a local path; --url downloads a remote file; --upload-id uses a pre-staged upload from POST /v1/uploads; --result-id loads a saved query result (which must belong to the target database).
  • csv, json, and parquet are accepted. The format comes from the extension — .json, .jsonl, and .ndjson all mean json — and --format overrides it; an unrecognised extension is left to the server to resolve from the bytes. A json file is read in any shape: an array of objects, a pretty-printed document, or one object per line. The load itself takes newline-delimited json only, so --file and --url reshape the other two locally before upload.
  • --mode sets how the rows are applied: replace (default) replaces the table's contents, append adds them (--append is shorthand), and upsert, update and delete match existing rows by key.
  • upsert, update and delete need a key: the one the table was declared with (databases tables add --key), or --key <col> on the load. Repeat --key for a composite key (--key tenant --key id).
  • Concurrent loads into one table are serialised server-side. While another load holds the table the server answers 409 RESOURCE_LOCKED with a Retry-After, and the CLI keeps retrying for --retry-timeout seconds (default 60; 0 fails immediately).

Manage tables

hotdata databases tables [<database>]
hotdata databases tables list [--database <name_or_id>] [--schema <p>] [--table <p>] [--limit <n>]
hotdata databases tables add <table> [--database <name_or_id>] [--schema <s>] [--key <col>]... \
  [--key-determines <col>]... [--sorted-by <col>[=desc]]... [--partition-by <col>[=year|month|day|hour]]...
hotdata databases tables show <catalog.schema.table>
hotdata databases tables load <table> [--database <name_or_id>] [--schema <s>] (--file|--url|--upload-id|--result-id) \
  [--mode replace|append|upsert|update|delete] [--key <col>]...
hotdata databases tables remove <table | schema.table | catalog.schema.table> [--database <name_or_id>] [--schema <s>]
  • tables (or tables list) lists the tables in a database; --database defaults to the current database.
  • show prints column definitions for catalog.schema.table (or schema.table with an active database).
  • tables add declares a table before its first load. --key names the column (repeat for a composite key) that identifies a row; declaring one is what enables upsert, update and delete loads. Without a key the table still loads with replace and append. --key-determines names a column whose value is fixed by the key; declare it only where that holds, or a keyed load can leave a duplicate key behind. --sorted-by and --partition-by set the on-disk layout and cannot be changed once the table exists.
  • tables load loads a csv, json, or parquet file or a saved result into a table, addressing the database by --database rather than by --catalog. --mode and --key work as for databases load.
  • tables remove drops a table from the database. A schema.table name takes precedence over --schema; a catalog.schema.table name picks the database by its catalog, and cannot be combined with --database.

Context

Sync named Markdown context files with an instant database — persistent notes and schema documentation scoped to a database, useful for giving agents durable context.

hotdata databases context list [-d <database>] [--prefix <p>]
hotdata databases context show <name> [-d <database>]
hotdata databases context pull <name> [--force] [--dry-run] [-d <database>]
hotdata databases context push <name> [--dry-run] [-d <database>]
  • pull downloads context to ./<NAME>.md (--force overwrites an existing file); push uploads ./<NAME>.md to the database as named context.
  • <name> follows SQL identifier rules; a trailing .md is ignored (e.g. USER.md → USER).

Query run history & results

hotdata databases queries list [--status <csv>] [--limit <n>] [--cursor <token>]
hotdata databases queries <query_run_id>
hotdata databases results list [--limit <n>] [--offset <n>]
hotdata databases results get <result_id> [-o table|json|csv]
  • queries list shows past runs with status, timing, row count, and a truncated SQL preview (default limit 20); --status filters by run status (comma-separated, e.g. running,failed). View a run by ID for full metadata (timings, result_id, SQL).
  • Every query result is stored automatically — retrieve rows with results get <result_id> (the result-id printed in a query's footer) without re-running the query.

Query

query is a top-level shortcut into databases query.

hotdata query "<sql>" \
  [-d <database>] \
  [--dialect hotsql|duckdb|postgres|snowflake] \
  [-o table|json|csv]
hotdata query status <query_run_id>
  • Default output is table, which prints results with row count and execution time.
  • -d/--database runs against a specific instant database (defaults to the current database set via databases use). It is sent as the X-Database-Id header, so it takes the database id — it does not resolve a name or catalog alias.
  • --dialect names the SQL the query is written in. Anything other than hotsql (the default) is transpiled to HotSQL server-side before it runs; writes follow the same rules as hotsql. See SQL dialects.
  • query also runs INSERT, CREATE TABLE, CREATE TABLE AS and DROP TABLE against the current database's own tables, for an API key holding the write permission. The result is one row with a rows_affected column. UPDATE and DELETE are not supported in SQL yet — use hotdata databases load --mode update|delete|upsert. See Writing data with SQL.
  • Long-running queries fall back to async execution and return a query_run_id. Poll it with hotdata query status <query_run_id>.
  • Exit codes for query status: 0 succeeded, 1 failed, 2 still running (poll again), 3 succeeded but the result is a truncated preview.

Ingest

An ingest is a saved load definition: it reads from an external source and writes rows into an instant database. Add the source first (hotdata ingest sources add), then create an ingest that reads it. Selector and destination are fixed at creation; creating an ingest starts nothing — the scheduler dispatches every run, so watch it with hotdata ingest logs <id> rather than expecting a run id back.

# Create a load definition (source + what to read + where it lands)
hotdata ingest create --source <name_or_id> --sql "SELECT * FROM <schema>.<table>" --database-id <db>
hotdata ingest create --source <name_or_id> --all --database-id <db>
hotdata ingest create --source <name_or_id> --table <t>... --type scheduled --every 1h --database-id <db>

# Inspect and control ingests
hotdata ingest list [--datasource-id <id>] [--type <t>] [--state <s>]
hotdata ingest show <ingest_id>
hotdata ingest logs [<ingest_id>] [--status <s>] [--wait] [--wait-timeout <s>]
hotdata ingest run <run_id> [--wait]
hotdata ingest pause <ingest_id>
hotdata ingest resume <ingest_id>
hotdata ingest schedule <ingest_id> [--every <dur>] [--next now|<rfc3339>]
hotdata ingest remove <ingest_id>
  • create needs a --source (a ds_… id or a display name) and a selector. Selector shorthands: --sql (SQL sources: SELECT <cols|*> FROM [<schema>.]<table> [WHERE …] [LIMIT n]), --raw-sql (a verbatim query in the source's own dialect), --all, --table/--topic/--table-path (per family), or --selector for raw family JSON. Destination flags: --database-id, --dest-table / --dest-table-prefix, --dest-schema (default public), and --write-mode replace|upsert. --type is one-time (default), scheduled, or continuous (--stream); recurring types need --every or --schedule.
  • There is no run-now verb — bring a run forward with hotdata ingest schedule <id> --next now.
  • pause cancels the active run and stops future ones; resume clears the stop but runs nothing immediately (one-time ingests can't be resumed — create a new one).
  • logs lists an ingest's runs (newest first); run <run_id> shows one run. Both accept --wait to watch until a terminal state (exit 0 succeeded, 1 failed/cancelled, 2 still queued/running).
  • remove soft-deletes the ingest and releases its destination table; the table, its data, and the source are never deleted.

Sources

A source (datasource) stores an external system's connection config and credentials. It's surfaced under ingest sources so a source and the ingests that read it share one command tree.

hotdata ingest sources types [<name>]
hotdata ingest sources fields [<family>]
hotdata ingest sources test --family <family> [--config <json>] [--credentials <json>] [--bucket-url <url>]
hotdata ingest sources add [--family <family>] [--display-name <name>] [--config <json>] [--credentials <json>]
hotdata ingest sources list [--family <f>] [--state <s>]
hotdata ingest sources show <datasource_id>
hotdata ingest sources update-config <datasource_id> [--config <json>] [--credentials <json>]
hotdata ingest sources remove <datasource_id>
  • Families: sql, rest, filesystem, iceberg, delta, ducklake, kafka, derived. Use sql for any SQL dialect (the dialect goes in the config), filesystem for buckets, and derived to build a table from another table in your own workspace. hotdata ingest sources fields prints the list with each family's required config.
  • types browses the catalog of source types and their families; fields <family> shows the config, credentials, and selector fields a family accepts (generated by the service, so it matches exactly what the API accepts).
  • test checks a config and credentials without creating anything — valid means the shape was accepted, probed means the source was actually contacted.
  • add returns a stable ds_… id and loads no data; on a terminal it prompts for the source type and fields (skip with --no-input and --config). Config and credentials are JSON — inline, @file.json, or @- for stdin; keep secrets out of argv with @file.
  • update-config appends a config version — rotate credentials by passing the same config plus new --credentials.
  • remove soft-deletes a source; its ingests must be removed first.

Full-text (BM25) and vector search over an indexed table column, plus index and embedding-provider management.

hotdata search "<query text>" \
  --index <name> \
  [-d <database>] \
  [--select <columns>] \
  [--limit <n>] \
  [-o table|json|csv]
  • The search type (full-text vs vector) is inferred from the index. --index addresses the index by name (from hotdata search list); -d/--database is the database the index lives in (defaults to the active database).
  • Full-text results are ordered by relevance (descending); vector results by distance (ascending). --select limits the returned columns (comma-separated, defaults to all).

Manage indexes

hotdata search create [<name>] --type text|vector|sorted --from <catalog.schema.table | schema.table> --column <col> [-d <database>] \
  [--metric l2|cosine|dot] \
  [--algorithm hnsw|ivf] [--nlist <n>] [--probe-fraction <f>] [--vector-precision <precision>] \
  [--provider <embedding_provider_id>] \
  [--dimensions <n>] [--output-column <name>] [--description "..."] \
  [--async]
hotdata search list [-d <database>] [--schema <s>] [--table <t>]
hotdata search show <name> [-d <database>]
hotdata search remove <name> [-d <database>]
  • --type: text (BM25 full-text), vector (similarity), or sorted (range/equality filters). text is the CLI's name for the index type the API calls bm25. The index name is derived from table, column, and type when omitted.
  • --metric applies to vector indexes. --provider enables server-side auto-embeddings on a text column; --dimensions overrides the embedding output dimensions and --output-column names the generated column (default {column}_embedding).
  • --type values are not freely combinable. An index created with --provider (auto-embedding vector) must be the only index on its table: creating one fails if the table already has an index, and no other index can be added while it exists. A vector index over an existing embedding column carries no such restriction, so it coexists with text and sorted indexes. See vector indexes.
  • --async submits index creation as a background job — poll with hotdata jobs <job_id>.
  • --algorithm applies to vector indexes: hnsw (default) or ivf. --nlist and --probe-fraction configure an ivf index; see IVF indexes.
  • search list and search show report an ivf index's cluster count as nlist. An index appears in search list once its build finishes; while an --async build runs, search list notes it and hotdata jobs list --job-type create_index tracks it.
  • --vector-precision sets how precisely a vector index stores each value: float64, float32, float16, float8 or int8. hnsw accepts all but int8 and defaults to the column's precision; ivf accepts int8 (its default) or float32.

Embedding providers

Embedding providers are the models behind vector search.

hotdata search embeddings list
hotdata search embeddings show <id>
hotdata search embeddings add --name <name> --provider-type local|service \
  [--config '{"model":"..."}'] \
  [--provider-api-key <key> | --secret-name <name>]
hotdata search embeddings update <id> [--name <name>] [--config <json>] [--provider-api-key <key> | --secret-name <name>]
hotdata search embeddings remove <id>
  • --provider-type local uses a local embedding model; service calls an external API (e.g. OpenAI).
  • --provider-api-key auto-creates a managed secret for the provider's API key; --secret-name references an existing secret instead.

Jobs

hotdata jobs list \
  [--job-type <type>] \
  [--status <status>] \
  [--all] \
  [--limit <n>] [--offset <n>] \
  [-o table|json|yaml]
hotdata jobs <job_id>
  • list shows only active jobs (pending and running) by default; use --all to see every job.
  • --job-type accepts: create_index, managed_load, database_fork.
  • --status accepts: pending, running, succeeded, partially_succeeded, failed.

Manage

Account, configuration, and CLI maintenance.

hotdata manage usage [--since <rfc3339>] [-o table|json|yaml]
hotdata manage completions bash|zsh|fish
hotdata manage upgrade
hotdata manage skills install [--project]
hotdata manage skills status
  • usage shows workspace usage: query count, bytes scanned, and stored bytes. --since counts usage from an RFC 3339 timestamp (e.g. 2026-06-01T00:00:00Z); defaults to the current billing window.
  • completions prints a shell completion script; upgrade updates the CLI to the latest release (brew upgrade for Homebrew installs, otherwise a direct binary download) and refreshes the installed skill to match.
  • skills install installs or refreshes the hotdata agent skill into ~/.agents/skills, linked into detected agent directories (Claude Code, pi); --project installs into the current project instead of globally. The install script already runs this step, so a fresh install needs no separate call. See Agent Skills.

Support

hotdata support report -m <text> --subject <subject> [--kind bug|question|billing|feature|account|other] \
  [--severity urgent|high|medium|low] [--logs <file|->] [--context KEY=VALUE]... [--no-workspace]
  • Files a support ticket from the terminal. Omit -m in an interactive terminal to compose the report in $EDITOR, where the first non-comment line is the subject.
  • --logs attaches a text file (- reads stdin, capped at 256 KiB client-side); --context adds up to 20 key/value pairs. The current workspace is attached unless --no-workspace is given. Replies go to the email on your Hotdata account.

Configuration

Config is stored at ~/.hotdata/config.yml, keyed by profile (default: default). Override the config directory with HOTDATA_CONFIG_DIR.

VariableDescription
HOTDATA_API_KEYAPI key (overrides the config file)
HOTDATA_WORKSPACEDefault workspace ID for the current process
HOTDATA_DATABASEDefault instant database for the current process; overrides the saved current database, but an explicit -d wins
HOTDATA_API_URLOverride the API endpoint (default https://api.hotdata.dev/v1)
HOTDATA_APP_URLOverride the app URL used for browser auth
HOTDATA_NO_UPDATE_CHECKSkip the interactive new-release check
HOTDATA_UPLOAD_CONCURRENCYParallel parts for multipart uploads in databases load (default 12)

See also