# CLI (/developers/cli)



The Paragraph CLI covers the same ground as the [REST API](/developers/api-reference), with JSON output for scripts and agents and an interactive menu for everyone else.

## Install [#install]

<Tabs items="[&#x22;npm&#x22;, &#x22;Homebrew&#x22;]">
  <Tab value="npm">
    ```bash
    npm install -g @paragraph-com/cli
    ```

    Requires Node.js 18 or later.
  </Tab>

  <Tab value="Homebrew">
    ```bash
    brew tap paragraph-xyz/tap && brew install paragraph
    ```
  </Tab>
</Tabs>

## Sign in [#sign-in]

```bash
paragraph login
```

This opens Paragraph in your browser, where you pick the publication to connect and approve access. To use an [API key](/developers#authentication) instead, pass it with `--token`, pipe it in with `--with-token`, or set `PARAGRAPH_API_KEY`:

```bash
paragraph login --token <your-api-key>
echo "<your-api-key>" | paragraph login --with-token
PARAGRAPH_API_KEY=<your-api-key> paragraph post list
```

Run `paragraph whoami` to check which publication you're using. Credentials are stored in `~/.paragraph/config.json`.

## Posts [#posts]

New posts are drafts until you publish them.

```bash
paragraph post create --title "October update" --file ./draft.md
paragraph post list --status draft
paragraph post get <id-or-slug> --field markdown > post.md
paragraph post update <id-or-slug> --file ./updated.md
paragraph post publish <id-or-slug>
paragraph post publish <id-or-slug> --newsletter   # also email subscribers
```

`post get` accepts an ID, a URL, or `@publication/slug`. Other post commands include `draft`, `archive`, `delete`, `test-email`, `feed`, and `by-tag`, and destructive ones take `--dry-run`.

## Drafted content [#drafted-content]

Short-form drafts (X posts and threads, LinkedIn posts, one-off emails, X Articles) go to the publication's library. Nothing is sent until someone sends it from the app.

```bash
paragraph content create --kind tweet --title "Launch thread" --tweet "First." --tweet "Second."
paragraph content list --kind tweet --status draft
```

See [Drafted content](/developers/content) for every kind, how to group drafts with the post they came from, and the `content` commands that edit, archive, and restore them.

## Publications, search, and subscribers [#publications-search-and-subscribers]

```bash
paragraph publication get @your-publication
paragraph search post --query "release notes"
paragraph subscriber list --limit 100
paragraph subscriber add --email reader@example.com
paragraph subscriber import --csv subscribers.csv
```

Run `paragraph --help` for every command, or `paragraph <command> --help` for its flags.

## Scripts and agents [#scripts-and-agents]

Every command takes `--json`. Data goes to stdout and status messages to stderr, so output pipes cleanly:

```bash
paragraph --json post list | jq '.data[0].title'
```

Lists return `{ "data": [...], "pagination": { "cursor": "...", "hasMore": true } }`, and single items return the object itself. In `--json` mode, errors are JSON on stderr with a non-zero exit code:

```json
{ "error": "Not found.", "code": "NOT_FOUND", "status": 404 }
```

`code` is one of `UNAUTHORIZED`, `FORBIDDEN`, `NOT_FOUND`, `RATE_LIMITED`, `SERVER_ERROR`, `REQUEST_FAILED`, `CLIENT_ERROR`, or `UNKNOWN`.

Outside a terminal, `delete` requires `--yes`. Set `PARAGRAPH_NON_INTERACTIVE=1` or `CI=true` to turn off the interactive menu, and `PARAGRAPH_API_URL` to point at a different API.

To teach a coding agent the CLI, install the [Paragraph CLI skill](/developers/agent-skills).

## Interactive mode [#interactive-mode]

Run `paragraph` with no arguments for a menu-driven interface with scrollable lists and keyboard navigation. It's skipped automatically when output is piped or `--json`, `--help`, or `--version` is passed.

## Source [#source]

The CLI is open source on [GitHub](https://github.com/paragraph-xyz/paragraph-cli) and published to [npm](https://www.npmjs.com/package/@paragraph-com/cli).
