Paragraph Docs
Developers

CLI

Manage posts, publications, subscribers, and coins from the command line. Built for humans, agents, and CI.

The Paragraph CLI is a command-line tool for managing posts, publications, subscribers, and coins. It works for both human and programmatic (agent) usage.

Prerequisites: Node.js version 18 or higher (not needed for the Homebrew install).

Install

npm install -g @paragraph-com/cli
brew tap paragraph-xyz/tap && brew install paragraph

Authentication

Get your API key in the app at app.paragraph.com under Settings -> Publication -> Developer.

Authenticate

paragraph login

Or provide the key directly:

paragraph login --token <your-api-key>

Verify

paragraph whoami

For CI or agent environments, you can pipe the token or set an environment variable:

echo "<your-api-key>" | paragraph login --with-token

PARAGRAPH_API_KEY=<your-api-key> paragraph post list

Credentials are stored in ~/.paragraph/config.json (mode 0600).

Quick examples

paragraph post create --title "My Post" --file ./draft.md

paragraph post publish my-post

paragraph post list

paragraph search post --query "ethereum"

Run paragraph --help for all commands, or paragraph <command> --help for details on any command.

Using an AI agent like Claude Code or Cursor? Install the Paragraph CLI skill to teach it how to use the CLI:

npx skills add paragraph-xyz/skill --skill paragraph-cli

Command reference

Posts

List posts

paragraph post list
paragraph post list --status draft
paragraph post list --limit 50 --cursor <cursor>

# List posts from any publication (public, no auth required)
paragraph post list --publication <id-or-slug>

Get a post

Accepts an ID, URL, or @publication/slug:

paragraph post get <post-id>
paragraph post get @yearn/some-post-slug
paragraph post get https://paragraph.com/@yearn/some-post-slug

Extract a single field (raw value to stdout, pipeable):

paragraph post get <id> --field markdown > post.md
paragraph post get <id> --field title

Create a post

Creates a draft by default:

paragraph post create --title "My Post" --text "# Hello World"
paragraph post create --title "My Post" --file ./draft.md
cat draft.md | paragraph post create --title "From Stdin"
paragraph post create --title "Post" --text "Content" --subtitle "Summary" --tags "web3,defi"

Update a post

paragraph post update <id-or-slug> --title "New Title"
paragraph post update <id-or-slug> --file ./updated.md --tags "new,tags"

Post lifecycle

paragraph post publish <id-or-slug>
paragraph post publish <id-or-slug> --newsletter     # publish + email subscribers
paragraph post draft <id-or-slug>                     # revert to draft
paragraph post archive <id-or-slug>

Preview and test

paragraph post publish <id-or-slug> --dry-run
paragraph post delete <id-or-slug> --dry-run
paragraph post test-email <id>                        # send test email (drafts only)

Delete a post

paragraph post delete <id-or-slug>
paragraph post delete <id-or-slug> --yes              # skip confirmation

Browse posts

paragraph post by-tag defi --limit 20
paragraph post feed --limit 10

Shortcuts

Top-level shortcuts for common operations:

paragraph create --title "Quick Post" --text "Content"
paragraph update my-post-slug --title "Updated"
paragraph delete my-post-slug --yes

Publications

paragraph publication get @variantwriting
paragraph publication get blog.variant.fund
paragraph publication get <publication-id>
paragraph search post --query "ethereum"
paragraph search blog --query "web3"

Subscribers

paragraph subscriber list --limit 100
paragraph subscriber count <publication-id>
paragraph subscriber add --email user@example.com
paragraph subscriber add --wallet 0x1234...abcd
paragraph subscriber import --csv subscribers.csv

Coins

paragraph coin get <id-or-address>
paragraph coin popular --limit 10
paragraph coin search --query "test"
paragraph coin holders <id-or-address> --limit 50
paragraph coin quote <id-or-address> --amount <wei>

Users

paragraph user get <user-id>
paragraph user get 0x1234...    # by wallet address

Agent and programmatic usage

The CLI is designed for use by AI agents and scripts.

JSON output

All commands support --json. Data goes to stdout, status messages to stderr:

paragraph --json post list | jq '.data[0].title'
paragraph --json post get <id> | jq '.markdown'
paragraph --json search post --query "web3" | jq '.length'

Paginated commands return:

{
  "data": [{ "id": "...", "title": "..." }],
  "pagination": { "cursor": "abc123", "hasMore": true }
}

Single-item commands return the object directly:

{ "id": "...", "title": "...", "markdown": "..." }

Structured errors

In --json mode, errors are structured JSON on stderr with a non-zero exit code:

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

Error codes: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, RATE_LIMITED, SERVER_ERROR, REQUEST_FAILED, CLIENT_ERROR, UNKNOWN.

Non-interactive safety

  • delete requires --yes in non-TTY environments
  • login supports --with-token for stdin piping and --token for direct input
  • Destructive commands support --dry-run
  • Set PARAGRAPH_NON_INTERACTIVE=1 or CI=true to force CLI mode

Environment variables

VariablePurpose
PARAGRAPH_API_KEYAPI key (alternative to login)
PARAGRAPH_API_URLCustom API base URL
PARAGRAPH_NON_INTERACTIVESet to 1 to disable TUI
CISet to true to disable TUI

Interactive TUI

Running paragraph with no arguments launches an interactive terminal UI with menus, scrollable lists, and keyboard navigation.

The TUI is disabled automatically when:

  • --json, --help, or --version flags are used
  • stdout is not a TTY (e.g., piped output)
  • CI=true or PARAGRAPH_NON_INTERACTIVE=1 is set

Resources

On this page