> ## Documentation Index
> Fetch the complete documentation index at: https://developers.investorlift.com/llms.txt
> Use this file to discover all available pages before exploring further.

# CLI

> The official command-line tool: one command per endpoint, a table or a CSV file from your terminal, the cost of every call on stderr.

The official command-line tool is `@investorlift/cli`. Its binary is `investorlift`. It is in beta, and its version
starts at 0. It runs every endpoint of this reference as one command, through the official TypeScript package. You need
Node 20 or later and a key. You write no code.

<Note>
  Investorlift generates the command table from the same OpenAPI document that generates this reference. A new endpoint
  or a new filter reaches the tool at the next release. The `--help` of each command is the description on its endpoint
  page, with the cost sentence.
</Note>

<Steps>
  <Step title="Run it">
    ```bash theme={null}
    npx @investorlift/cli@0.1 --help
    ```

    `npx` downloads the tool and runs it. To keep it on your path, install it once:

    ```bash theme={null}
    npm install -g @investorlift/cli
    investorlift --help
    ```
  </Step>

  <Step title="Give it the key">
    ```bash theme={null}
    export GM_API_KEY="zpka_..."
    ```

    The tool reads `GM_API_KEY` first. For a key that stays on this machine, run `investorlift auth login`. The tool asks
    for the key with echo off and writes it to a file only you can read. `investorlift auth status` says which source is
    in use and masks the key. `investorlift auth logout` removes the file.

    The file is `~/.config/investorlift/credentials.json`. When `XDG_CONFIG_HOME` holds an absolute path, the file is
    `$XDG_CONFIG_HOME/investorlift/credentials.json`. `investorlift auth login` also stores the host when you pass
    `--base-url` or export `GM_BASE_URL`. A later call without `--base-url` and without `GM_BASE_URL` uses the stored
    host.

    A key file the tool cannot parse is a usage error that names the file. The tool refuses a host without `http://` or
    `https://`. That error names the source: `--base-url`, `GM_BASE_URL` or the key file. On Windows the file has no `0600`
    mode. Your user profile alone protects it there.

    The tool has no `--api-key` flag, so a key never lands in your shell history. [Get an API key](/get-a-key) explains
    the console.
  </Step>

  <Step title="Make one call">
    ```bash theme={null}
    investorlift deals summary --lat 33.476917 --lng -111.920385 --radius-miles 2
    ```

    On a terminal the answer is a table. In a pipe or a file it is the JSON the API sent. `--format json`, `table`,
    `csv` or `ndjson` overrides that. The response facts go to stderr after every call, so stdout is the answer alone:

    ```text theme={null}
    request_id=req_01J9...  credits=0 charged  data_end=phx=2026-08-27  dataset_version=phx=1789636470
    ```

    Quote the request id to [support@investorlift.com](mailto:support@investorlift.com). `--quiet` drops the line.
  </Step>

  <Step title="Find the command">
    The command is the endpoint's method name on the [SDKs](/guides/sdks) page, as two words. `deals.summary` is
    `deals summary`. `wholesaleListings.list` is `wholesale-listings list`. A flag is a query parameter with dashes:
    `radius_miles` is `--radius-miles`, and the tool also accepts `--radius_miles`. An id in the path is the first
    argument. A list parameter repeats the flag, or takes a comma list.

    ```bash theme={null}
    investorlift investors get inv_0a20a550f33b --market phx
    investorlift deals list --zip 85251 --zip 85254 --kind flip,wholesale --limit 100
    investorlift properties search --body '{"location":{"zip":["85251"]},"limit":25}'
    ```

    `--body` takes a JSON text, `@file.json`, or `-` for stdin. Every request block on this site has a CLI tab that shows
    the same call. `investorlift <group> <command> --help` lists the flags of one command with the text of its endpoint
    page.
  </Step>

  <Step title="Write a spreadsheet">
    ```bash theme={null}
    investorlift deals list --zip 85251 --kind flip --bought-after 2024-08-12 --sort date_desc --limit 100 \
      --all --max-pages 5 --format csv > deals.csv
    ```

    `--all` follows `page.next_cursor` to the last page. A command that spends credits requires `--max-pages`, because
    every page adds to the bill. A command at 0 credits takes no bound. The CSV flattens a nested block into dotted
    columns, `property.zip`, and `--columns id,property.zip,bought_price` picks and orders them.

    With `--format json`, `--all` prints one document that the tool builds, not the envelope of one page. `data` holds
    the rows of every page. `meta` and `summary` come from the first page. `page` is `{ pages, next_cursor }`: the count of
    pages read, and the cursor after the last page read.

    A `400 invalid_cursor` in the middle of a long export stops the loop with the error's exit code, here 2. The tool
    first prints the pages it has, in the requested format. Then it prints the error. Stderr states how many pages it
    printed and names the resume cursor as `--cursor <c>`. The tool never starts again from page 1 on its own. [Give me a spreadsheet](/guides/walkthroughs/spreadsheet) shows the same export with the loop
    written out.
  </Step>

  <Step title="Read an error">
    ```bash theme={null}
    investorlift deals summary --lat 40.7 --lng -74.0 --radius-miles 2
    echo $?
    ```

    A refusal prints the problem body on stderr. The body has the status and the code, the detail, the `recovery`
    sentence and the request id. The exit code names the family, so a script can branch on it:

    | Exit code | Meaning                                                                                                                           |
    | --------- | --------------------------------------------------------------------------------------------------------------------------------- |
    | 0         | Success, or `304 Not Modified` for `--etag`.                                                                                      |
    | 1         | A usage error of the tool: an unknown command or flag, a missing `--max-pages`, a bad `--body`, a key file the tool cannot parse. |
    | 2         | The API refused the request: a 400, 404, 406, 410, or a 422 that is not a coverage fact.                                          |
    | 3         | No key, or the key is refused (401).                                                                                              |
    | 4         | Over a limit: 429, or a 403 for the quota, the plan, a missing subscription or an overdue payment.                                |
    | 5         | Outside coverage, or a dataset not measured for the market (422).                                                                 |
    | 6         | The service failed: 500, 503, 504.                                                                                                |
    | 7         | No answer: a network failure, a timeout, or an abort.                                                                             |

    With `--format json` the problem body goes to stdout instead, with the exit code inside it. A parser then reads one
    stream. The codes and the recovery sentences are on [Errors](/guides/concepts/errors).
  </Step>

  <Step title="See the request">
    ```bash theme={null}
    investorlift deals summary --lat 33.476917 --lng -111.920385 --radius-miles 2 --dry-run
    ```

    `--dry-run` prints the curl command the tool is about to send, with `$GM_API_KEY` where the key goes, and sends
    nothing. Use it to learn the raw API, or to paste a request into a bug report.
  </Step>
</Steps>

## Retries and freshness

The tool retries as the package does, and no more. It retries a `429` and the two `503` codes that carry
`Retry-After`. It retries one `500 internal_error` and one connection failure before an answer. `--max-retries 0`
turns them off. The rule is on [Rate limits](/guides/concepts/rate-limits).

`--etag` sends `If-None-Match`. A `304` prints `not modified` on stderr, nothing on stdout, and exits 0. The flag
works on a command that answers one record or one summary. On every paged command the tool refuses it with a usage
error, exit code 1. `investorlift dataset get --etag <etag>` is the freshness probe.

Every call prints `dataset_version` in its facts line. The [cache window](/guides/terms) is the rule.

## Flags on every command

| Flag                                   | What it does                                                                                                                  |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `--format json\|table\|csv\|ndjson`    | The shape of stdout. Default: table on a terminal, JSON in a pipe.                                                            |
| `--columns a,b.c`                      | The columns of a table, a CSV or an NDJSON row, as dotted paths.                                                              |
| `--all`, `--max-pages N`, `--cursor C` | Follow the pages, bound the loop, or start at one cursor.                                                                     |
| `--body JSON\|@file\|-`                | The body of a POST.                                                                                                           |
| `--etag E`                             | Send `If-None-Match`. Refused on a paged command, exit code 1.                                                                |
| `--base-url URL`                       | Another host. `GM_BASE_URL` does the same, and `auth login` stores one in the key file. `auth status` prints the host in use. |
| `--header "Name: value"`               | An extra header. Repeat for more.                                                                                             |
| `--max-retries N`, `--timeout-ms N`    | The retry bound and the per-attempt timeout.                                                                                  |
| `--dry-run`                            | Print the curl command and send nothing.                                                                                      |
| `--quiet`                              | Drop the facts line.                                                                                                          |

## Shell completion

```bash theme={null}
investorlift completion bash > ~/.local/share/bash-completion/completions/investorlift
investorlift completion zsh > "${fpath[1]}/_investorlift"
investorlift completion fish > ~/.config/fish/completions/investorlift.fish
```

## What the tool does not do

* It does not create a key, change a plan or show your usage. The [console](/get-a-key) does.
* It does not cache answers and does not enforce the plan limits. The API reports both in the facts line.
* It does not connect to [the MCP endpoint](/mcp/overview). An AI client that runs shell commands can call the tool
  instead. Its `--help` states the cost of each command, and its exit codes are stable.
* It does not read the CSV export of [a lender's loans](/api-reference/endpoints/lenders-loans#csv). Every list
  becomes CSV with `--format csv`.

## Versions

`investorlift --version` prints the tool, the package under it and the API version the table came from. A minor API
change is a minor release of the package and of the tool. A removal follows the 30-day notice of the
[Developer Agreement](/guides/terms), and the [changelog](/changelog) records it.

Every request carries a `User-Agent` of three parts: the tool version, the package version and the Node version. The
tool sends nothing else about your machine.


## Related topics

- [Connecting a client](/mcp/connect.md)
- [Quickstart](/guides/quickstart.md)
- [SDKs](/guides/sdks.md)
- [API reference](/api-reference/introduction.md)
- [Frequently asked questions](/guides/faq.md)
