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.
| Command | Description |
|---|---|
auth login | Log in via browser |
auth register | Create a new account via browser (GitHub OAuth; --email for email + password) |
auth logout | Remove authentication for a profile |
auth status | Show authentication status |
workspaces list | List all workspaces |
workspaces use | Set the default workspace |
databases list | List instant databases in the workspace |
databases count | Count instant databases in the workspace |
databases show | Show details for an instant database |
databases create | Create a new instant database |
databases fork | Fork a database into a new, independent database |
databases lineage | Show a database's fork family tree |
databases attach | Attach another database so its tables are queryable |
databases detach | Detach a previously attached database |
databases use | Set the current (default) database |
databases unset | Clear the current database |
databases remove | Delete a database and all its tables |
databases load | Load a csv, json, or parquet file, or a saved result, into a table |
databases tables list | List tables in a database |
databases tables add | Declare a table, with its key and layout |
databases tables show | Show column definitions for a table |
databases tables load | Load a file or saved result into a table: replace, append, or match rows by key |
databases tables remove | Delete a table from a database |
databases context list | List named contexts in a database |
databases context show | Print context content to stdout |
databases context pull | Download context to ./<NAME>.md |
databases context push | Upload ./<NAME>.md as named context |
databases query | Execute a SQL query against a database |
databases query status | Check a running query and retrieve results |
databases queries list | List query runs |
databases results get | Show a stored query result by ID |
databases results list | List stored query results |
query "<sql>" | Execute a SQL query (shortcut for databases query) |
query status | Check a running query and retrieve results |
jobs list | List background jobs (active by default) |
jobs <id> | Show one background job |
ingest create | Create a load definition |
ingest list | List the ingests in the workspace |
ingest show | Show one ingest: state, selector, destination, schedule |
ingest pause | Stop an ingest (cancel the active run and future runs) |
ingest resume | Clear a stop and let the schedule dispatch again |
ingest schedule | Change when a scheduled/continuous ingest runs next |
ingest logs | List the runs of one ingest |
ingest run | Show one run: status, snapshots, timings |
ingest remove | Delete an ingest and release its destination table |
ingest sources test | Check a config and credentials without creating anything |
ingest sources add | Create a datasource and its first config version |
ingest sources list | List the datasources in the workspace |
ingest sources show | Show one datasource: state, config, discovery |
ingest sources update-config | Append a config version (rotate credentials) |
ingest sources remove | Delete a datasource |
ingest sources types | Browse the catalog of source types |
ingest sources fields | Show the fields a source family accepts |
search "<text>" --index <name> | Run a full-text or vector search against an index |
search create | Create a search index over a table column |
search list | List search indexes |
search show | Show one search index by name |
search remove | Remove a search index by name |
search embeddings list | List embedding providers |
search embeddings show | Show one embedding provider |
search embeddings add | Create a new embedding provider |
search embeddings update | Update an embedding provider |
search embeddings remove | Delete an embedding provider |
manage usage | Show workspace usage: queries, bytes scanned, stored bytes |
manage completions | Generate shell completions (bash, zsh, fish) |
manage upgrade | Upgrade the CLI to the latest release |
manage skills install | Install/update the agent skill into agent directories |
manage skills status | Show the agent skill's installation status |
manage skills list | List installed skills (alias for status) |
support report | File a support ticket with the Hotdata team |
Global options
| Option | Description |
|---|---|
--api-key <key> | API key (overrides env var and config) |
--no-input | Disable interactive prompts; error instead |
-v, --version | Print version |
-h, --help | Print 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>]
listshows all workspaces with a*marker on the active one.useswitches 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>
createmints a database.--catalogsets the SQL alias (SELECT … FROM <alias>.schema.table); it must be[a-z_][a-z0-9_]*and globally unique.--nameis a free-form display label.--table(repeatable) declares tables up front and acceptsschema.tabledot notation to span schemas.--schemasets the default schema for bare--tableentries (defaultpublic).--attach(repeatable) attaches another database as a queryable catalog on the new one; accepts a database name, catalog alias, or id, optionallydatabase=aliasto set the SQL alias (--attach reference --attach salesdb=sales).--expires-ataccepts 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.usemarks a database as the default for subsequent commands and takes the database id printed bycreate(unlikeshow/removeand-d/--database, it does not resolve a name or catalog alias);unsetclears it.removedeletes 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.
--namedefaults 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,
--catalogis ambiguous for that alias. Address either one by id:databases load --database <id>,-d <id>, ordatabases 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>]
attachmakes 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--aliasis omitted.--aliasis required when the other database kept the stockdefaultcatalog name:defaultcannot be attached under its own name.-d/--databaseselects the database to attach into (defaults to the current database).detachremoves 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>]...
--catalogis the alias set at create time;--schemadefaults topublic.--database <id|name>targets the database directly and takes precedence over--catalog; use it when forks share an alias.- Sources:
--fileuploads from a local path;--urldownloads a remote file;--upload-iduses a pre-staged upload fromPOST /v1/uploads;--result-idloads 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.ndjsonall mean json — and--formatoverrides 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--fileand--urlreshape the other two locally before upload. --modesets how the rows are applied:replace(default) replaces the table's contents,appendadds them (--appendis shorthand), andupsert,updateanddeletematch existing rows by key.upsert,updateanddeleteneed a key: the one the table was declared with (databases tables add --key), or--key <col>on the load. Repeat--keyfor 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_LOCKEDwith aRetry-After, and the CLI keeps retrying for--retry-timeoutseconds (default 60;0fails 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(ortables list) lists the tables in a database;--databasedefaults to the current database.showprints column definitions forcatalog.schema.table(orschema.tablewith an active database).tables adddeclares a table before its first load.--keynames the column (repeat for a composite key) that identifies a row; declaring one is what enablesupsert,updateanddeleteloads. Without a key the table still loads withreplaceandappend.--key-determinesnames 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-byand--partition-byset the on-disk layout and cannot be changed once the table exists.tables loadloads a csv, json, or parquet file or a saved result into a table, addressing the database by--databaserather than by--catalog.--modeand--keywork as fordatabases load.tables removedrops a table from the database. Aschema.tablename takes precedence over--schema; acatalog.schema.tablename 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>]
pulldownloads context to./<NAME>.md(--forceoverwrites an existing file);pushuploads./<NAME>.mdto the database as named context.<name>follows SQL identifier rules; a trailing.mdis 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 listshows past runs with status, timing, row count, and a truncated SQL preview (default limit 20);--statusfilters 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>(theresult-idprinted 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/--databaseruns against a specific instant database (defaults to the current database set viadatabases use). It is sent as theX-Database-Idheader, so it takes the database id — it does not resolve a name or catalog alias.--dialectnames the SQL the query is written in. Anything other thanhotsql(the default) is transpiled to HotSQL server-side before it runs; writes follow the same rules ashotsql. See SQL dialects.queryalso runsINSERT,CREATE TABLE,CREATE TABLE ASandDROP TABLEagainst the current database's own tables, for an API key holding thewritepermission. The result is one row with arows_affectedcolumn.UPDATEandDELETEare not supported in SQL yet — usehotdata 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 withhotdata query status <query_run_id>. - Exit codes for
query status:0succeeded,1failed,2still running (poll again),3succeeded 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>
createneeds a--source(ads_…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--selectorfor raw family JSON. Destination flags:--database-id,--dest-table/--dest-table-prefix,--dest-schema(defaultpublic), and--write-mode replace|upsert.--typeisone-time(default),scheduled, orcontinuous(--stream); recurring types need--everyor--schedule.- There is no
run-nowverb — bring a run forward withhotdata ingest schedule <id> --next now. pausecancels the active run and stops future ones;resumeclears the stop but runs nothing immediately (one-time ingests can't be resumed — create a new one).logslists an ingest's runs (newest first);run <run_id>shows one run. Both accept--waitto watch until a terminal state (exit0succeeded,1failed/cancelled,2still queued/running).removesoft-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. Usesqlfor any SQL dialect (the dialect goes in the config),filesystemfor buckets, andderivedto build a table from another table in your own workspace.hotdata ingest sources fieldsprints the list with each family's required config. typesbrowses 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).testchecks a config and credentials without creating anything —validmeans the shape was accepted,probedmeans the source was actually contacted.addreturns a stableds_…id and loads no data; on a terminal it prompts for the source type and fields (skip with--no-inputand--config). Config and credentials are JSON — inline,@file.json, or@-for stdin; keep secrets out of argv with@file.update-configappends a config version — rotate credentials by passing the same config plus new--credentials.removesoft-deletes a source; its ingests must be removed first.
Search
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.
--indexaddresses the index by name (fromhotdata search list);-d/--databaseis the database the index lives in (defaults to the active database). - Full-text results are ordered by relevance (descending); vector results by distance (ascending).
--selectlimits 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), orsorted(range/equality filters).textis the CLI's name for the index type the API callsbm25. The index name is derived from table, column, and type when omitted.--metricapplies to vector indexes.--providerenables server-side auto-embeddings on a text column;--dimensionsoverrides the embedding output dimensions and--output-columnnames the generated column (default{column}_embedding).--typevalues 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. Avectorindex over an existing embedding column carries no such restriction, so it coexists withtextandsortedindexes. See vector indexes.--asyncsubmits index creation as a background job — poll withhotdata jobs <job_id>.--algorithmapplies to vector indexes:hnsw(default) orivf.--nlistand--probe-fractionconfigure anivfindex; see IVF indexes.search listandsearch showreport anivfindex's cluster count asnlist. An index appears insearch listonce its build finishes; while an--asyncbuild runs,search listnotes it andhotdata jobs list --job-type create_indextracks it.--vector-precisionsets how precisely a vector index stores each value:float64,float32,float16,float8orint8.hnswaccepts all butint8and defaults to the column's precision;ivfacceptsint8(its default) orfloat32.
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 localuses a local embedding model;servicecalls an external API (e.g. OpenAI).--provider-api-keyauto-creates a managed secret for the provider's API key;--secret-namereferences 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>
listshows only active jobs (pendingandrunning) by default; use--allto see every job.--job-typeaccepts:create_index,managed_load,database_fork.--statusaccepts: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
usageshows workspace usage: query count, bytes scanned, and stored bytes.--sincecounts usage from an RFC 3339 timestamp (e.g.2026-06-01T00:00:00Z); defaults to the current billing window.completionsprints a shell completion script;upgradeupdates the CLI to the latest release (brew upgradefor Homebrew installs, otherwise a direct binary download) and refreshes the installed skill to match.skills installinstalls or refreshes the hotdata agent skill into~/.agents/skills, linked into detected agent directories (Claude Code, pi);--projectinstalls 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
-min an interactive terminal to compose the report in$EDITOR, where the first non-comment line is the subject. --logsattaches a text file (-reads stdin, capped at 256 KiB client-side);--contextadds up to 20 key/value pairs. The current workspace is attached unless--no-workspaceis 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.
| Variable | Description |
|---|---|
HOTDATA_API_KEY | API key (overrides the config file) |
HOTDATA_WORKSPACE | Default workspace ID for the current process |
HOTDATA_DATABASE | Default instant database for the current process; overrides the saved current database, but an explicit -d wins |
HOTDATA_API_URL | Override the API endpoint (default https://api.hotdata.dev/v1) |
HOTDATA_APP_URL | Override the app URL used for browser auth |
HOTDATA_NO_UPDATE_CHECK | Skip the interactive new-release check |
HOTDATA_UPLOAD_CONCURRENCY | Parallel parts for multipart uploads in databases load (default 12) |
See also
- Quick Start — Install, authenticate, and run your first query
- API Reference — Full HTTP API documentation
- Pull Data — Supported source types