A command-line interface for querying Perplexity.ai with persistent authentication and encrypted token storage.
- Query Perplexity.ai from the terminal with a single command
- Persistent authentication with encrypted token storage (PBKDF2-HMAC key derivation)
- Multiple output formats -- plain text, Markdown, rich terminal, or structured JSON
- Real-time streaming -- optional incremental output as the response arrives
- Source references -- web sources extracted and displayed with inline citations
- Citation stripping -- remove citation markers and references section from output
- Response style presets -- configure a persistent style prompt applied to all queries
- Thread library export -- export your entire Perplexity thread history to CSV
- Date filtering -- filter exported threads by date range
- Automatic retry -- exponential backoff on transient errors and rate limits
- Cloudflare bypass -- Chrome TLS fingerprint impersonation via curl_cffi
- Configurable -- URLs, rate limits, cookie storage, and debug mode all configurable via file or environment variables
- Python 3.12 or later
- Google Chrome (for initial authentication only)
Install from PyPI and run using either pxcli or perplexity-cli (both are equivalent):
# Install with uv
uv pip install pxcli
# Or run directly without installing
uvx pxcli --helpThen query using whichever name you prefer:
pxcli query "Tell me what happened in AI this week"
perplexity-cli query "Tell me what happened in AI this week"Clone the repository and install in development mode:
git clone https://github.com/jamiemills/perplexity-cli.git
cd perplexity-cli
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"
pytest # verify setupBoth pxcli and perplexity-cli are available after installation:
pxcli query "Tell me what happened in AI this week"
perplexity-cli query "Tell me what happened in AI this week"pxcli query "Tell me what happened in AI this week"The query command works without authentication for simple questions.
pxcli authThis opens Chrome via the DevTools Protocol, waits for you to log in to Perplexity.ai, extracts your session token, and saves it encrypted locally. See Authentication setup for full instructions.
After authentication, you can:
# Attach files to queries (requires auth)
pxcli query --attach README.md "What is this project?"
# Export your thread library to CSV
pxcli export-threads
# Check your authentication status
pxcli status
# Configure a response style to apply to all queries
pxcli configure "be concise and technical"# Default: rich terminal output, batch mode (waits for complete response)
pxcli query "What is machine learning?"
# Stream the response as it arrives
pxcli query --stream "What is machine learning?"
# Remove citation markers [1], [2] and the references section
pxcli query --strip-references "What is machine learning?"Use --format (or -f) to choose the output format. The default is rich.
# Rich terminal output with colours and formatted tables (default)
pxcli query "What is Python?"
# Plain text with underlined headers (good for scripts and piping)
pxcli query --format plain "What is Python?"
# GitHub-flavoured Markdown
pxcli query --format markdown "What is Python?" > answer.md
# Structured JSON with answer text and references array
pxcli query --format json "What is Python?" > answer.jsonThe JSON output has this structure:
{
"format_version": "1.0",
"answer": "Python is a high-level programming language...",
"references": [
{
"index": 1,
"title": "Python.org",
"url": "https://www.python.org",
"snippet": "Python is a programming language..."
}
]
}Useful with jq:
# Extract just the answer text (use -r so newlines render properly)
pxcli query --format json "What is Python?" | jq -r '.answer'
# Extract reference URLs
pxcli query --format json "What is Python?" | jq -r '.references[].url'
# Count references
pxcli query --format json "What is Python?" | jq '.references | length'
# Strip references from JSON output
pxcli query --format json --strip-references "What is Python?"Flags can be combined freely:
pxcli query --format plain --strip-references "What is 2+2?"
pxcli query --stream --strip-references "Explain Kubernetes"
pxcli query --format markdown --strip-references "How does DNS work?" > dns.md# Capture output in a variable
ANSWER=$(pxcli query --format plain "What is 2+2?")
echo "The answer is: $ANSWER"
# Parse JSON in Python
python3 << 'EOF'
import json, subprocess
result = subprocess.run(
["pxcli", "query", "--format", "json", "What is Python?"],
capture_output=True, text=True
)
data = json.loads(result.stdout)
print(data["answer"])
for ref in data["references"]:
print(f"- {ref['title']}: {ref['url']}")
EOFSet a persistent style prompt that is appended to every query. This lets you control the tone and format of responses without repeating instructions.
# Set a style
pxcli configure "be brief and concise"
# View the current style
pxcli view-style
# Clear the style
pxcli clear-styleThe style is stored in ~/.config/perplexity-cli/style.json and persists across sessions.
Most commands require authentication, but the query command is an exception. Here's what requires authentication and what doesn't:
export-threads-- Export your thread library to CSVstatus-- Check your authentication statusconfigure,view-style,clear-style-- Configure response styles
query-- Submit queries to Perplexity.ai (works with or without a token, with exceptions below)
If you have authenticated with pxcli auth, your token will be used automatically with query. If you haven't authenticated, query will attempt to run without a token (behaviour depends on whether the Perplexity API permits unauthenticated requests).
Even though the query command can run without authentication, some features require a token:
- File attachments -- The
--attachflag or file paths detected in the query text require authentication to upload files to Perplexity - Thread export -- The
export-threadscommand requires authentication to access your thread library
For most users, we recommend authenticating to ensure the best experience. For automated scripts or testing, you can use query for simple questions without authentication. Any queries involving file uploads will require authentication.
The first time you use pxcli, you need to authenticate with Perplexity.ai. This is a one-time process that extracts your session token via Chrome's DevTools Protocol.
Download a dedicated Chrome instance (keeps testing separate from your main browser):
npx @puppeteer/browsers install chrome@stableThis downloads Chrome to ~/.local/bin/chrome/.
Add this to your shell configuration (~/.bashrc, ~/.zshrc, etc.):
alias chromefortesting='open ~/.local/bin/chrome/mac_arm-*/chrome-mac-arm64/Google\ Chrome\ for\ Testing.app --args "--remote-debugging-port=9222" "about:blank"'The mac_arm-* pattern matches the version directory. Adjust the path for your platform.
# Terminal 1: Start Chrome with debugging enabled
chromefortesting
# Terminal 2: Run authentication
pxcli authThe process will:
- Connect to Chrome via the remote debugging port
- Navigate to Perplexity.ai
- Wait for you to log in
- Extract your session token
- Save it encrypted to
~/.config/perplexity-cli/token.json
Once complete, you do not need to authenticate again unless you run pxcli logout or the token expires.
If port 9222 is in use:
pxcli auth --port 9223Start Chrome with the matching port in your alias.
Export your entire Perplexity thread history to CSV.
# Export all threads
pxcli export-threads
# Filter by date range
pxcli export-threads --from-date 2025-01-01
pxcli export-threads --from-date 2025-01-01 --to-date 2025-12-31
# Custom output file
pxcli export-threads --output my-threads.csv
# Bypass local cache
pxcli export-threads --force-refresh
# Clear cache before export
pxcli export-threads --clear-cachecreated_at,title,url
2025-12-23T23:06:00.525132Z,What is Python?,https://www.perplexity.ai/search/...
2025-12-22T20:54:36.349239Z,Explain AI,https://www.perplexity.ai/search/...Fields: created_at (ISO 8601 UTC), title, url.
Thread exports are cached locally in encrypted form at ~/.config/perplexity-cli/threads-cache.json. The cache stores only threads within the requested date range, so subsequent exports with different date filters will fetch fresh data as needed. Use --force-refresh to bypass the cache or --clear-cache to delete it.
Thread export requests are rate-limited by default (20 requests per 60 seconds) to avoid HTTP 429 errors. See Rate limiting for configuration.
pxcli stores feature toggles in ~/.config/perplexity-cli/config.json:
{
"version": 1,
"features": {
"save_cookies": false,
"debug_mode": false
}
}Manage settings with:
# View current configuration
pxcli show-config
# Enable cookie storage (saves Cloudflare cookies alongside JWT token)
pxcli set-config save_cookies true
# Enable persistent debug logging
pxcli set-config debug_mode true
# Disable
pxcli set-config save_cookies false
pxcli set-config debug_mode falseAfter changing save_cookies, re-authenticate for the change to take effect:
pxcli set-config save_cookies true
pxcli authTokens are encrypted and stored at:
- Linux/macOS:
~/.config/perplexity-cli/token.json - Windows:
%APPDATA%\perplexity-cli\token.json
Encryption uses Fernet (AES-128-CBC) with a key derived via PBKDF2-HMAC (100,000 iterations) from the system hostname and OS user. Tokens are not portable between machines. File permissions are restricted to owner only (0600).
To re-authenticate:
pxcli logout
pxcli authAPI endpoints are configured in ~/.config/perplexity-cli/urls.json (created automatically on first run):
{
"perplexity": {
"base_url": "https://www.perplexity.ai",
"query_endpoint": "https://www.perplexity.ai/rest/sse/perplexity_ask"
},
"rate_limiting": {
"enabled": true,
"requests_per_period": 20,
"period_seconds": 60
}
}Rate limiting applies to thread export requests. Adjust in urls.json:
{
"rate_limiting": {
"enabled": true,
"requests_per_period": 10,
"period_seconds": 60
}
}Set "enabled": false to disable (not recommended).
Environment variables override configuration file settings. Precedence: CLI flags > environment variables > config file > defaults.
| Variable | Description |
|---|---|
PERPLEXITY_BASE_URL |
API base URL |
PERPLEXITY_QUERY_ENDPOINT |
Query endpoint path |
PERPLEXITY_SAVE_COOKIES |
true or false -- override cookie storage |
PERPLEXITY_DEBUG_MODE |
true or false -- override debug mode |
PERPLEXITY_RATE_LIMITING_ENABLED |
true or false |
PERPLEXITY_RATE_LIMITING_RPS |
Requests per period (integer) |
PERPLEXITY_RATE_LIMITING_PERIOD |
Period in seconds (integer) |
pxcli [OPTIONS] COMMAND [ARGS]...
Options:
--version Show version and exit
-v, --verbose INFO level logging
-d, --debug DEBUG level logging
--log-file PATH Log file path (default: ~/.config/perplexity-cli/perplexity-cli.log)
Command options:
query -f {plain,markdown,rich,json} --strip-references
--stream / --no-stream
auth --port PORT
export-threads --from-date DATE --to-date DATE --output PATH
--force-refresh --clear-cache
configure STYLE
set-config KEY VALUE
Authenticate with Perplexity.ai via Chrome DevTools Protocol. Default port: 9222.
Submit a query and display the answer.
| Option | Description |
|---|---|
--format, -f |
Output format: plain, markdown, rich (default), json |
--strip-references |
Remove citation markers and references section |
--stream / --no-stream |
Stream response incrementally (default: --no-stream) |
Exit codes: 0 success, 1 error, 130 interrupted.
Display authentication status and verify token validity.
Remove stored authentication token.
Set a persistent style prompt applied to all queries.
Display the currently configured style.
Remove the configured style.
Set a configuration option. Keys: save_cookies, debug_mode. Values: true, false.
Display current configuration and any environment variable overrides.
Display the Agent Skill definition for integrating pxcli with AI agents.
Export thread library to CSV.
| Option | Description |
|---|---|
--from-date DATE |
Start date filter (YYYY-MM-DD, inclusive) |
--to-date DATE |
End date filter (YYYY-MM-DD, inclusive) |
--output PATH |
Output file path (default: threads-TIMESTAMP.csv) |
--force-refresh |
Bypass local cache |
--clear-cache |
Delete cache before export |
Run pxcli auth to authenticate.
The token was encrypted on a different machine or with a different user. Run pxcli auth to re-authenticate.
Ensure Chrome is running with --remote-debugging-port=9222 and the port matches what you specified.
Delete the file and re-authenticate:
rm ~/.config/perplexity-cli/token.json
pxcli authgit clone https://github.com/jamiemills/perplexity-cli.git
cd perplexity-cli
uv venv
source .venv/bin/activate
uv pip install -e ".[dev]"pytest # run all tests
pytest --cov=perplexity_cli # with coverage
pytest -m integration # integration tests only
pytest -m security # security tests onlyruff format src/ tests/ # auto-format
ruff check src/ tests/ # lint
mypy src/ # type check- Tokens encrypted at rest using Fernet (AES-128-CBC)
- Key derivation via PBKDF2-HMAC with 100,000 iterations (with backward-compatible SHA-256 fallback for legacy tokens)
- File permissions restricted to owner (0600)
- Token validity checked on each request
- Token age warnings (>30 days)
- No credentials written to logs
- click -- CLI framework
- curl-cffi -- HTTP client with Chrome TLS fingerprint impersonation (query and thread export paths)
- rich -- Terminal formatting
- cryptography -- Token encryption
- tenacity -- Retry logic with exponential backoff
- pydantic -- Data validation and serialisation
- python-dateutil -- Date parsing for thread exports
- websockets -- WebSocket support
Useful functions to add into your profile e.g. $HOME/.zshrc of choice.
px() {
local q="$*"
uvx pxcli query --strip-references --format rich "${q}."
}
pxc() {
local q="$*"
uvx pxcli query --strip-references --format plain "${q}. Just give me the commands to run on a Mac. Put them on a single line"
}
after which you can run, for example
pxc "how can I find what remote branches exist for this repo"
MIT