A command-line interface for querying Perplexity.ai with persistent authentication
Project description
Perplexity CLI
A command-line interface for querying Perplexity.ai with persistent authentication and encrypted token storage.
Features
- Persistent authentication - Token stored securely and reused across invocations
- Encrypted tokens - Tokens encrypted with system-derived keys
- Real-time streaming - Optional streaming of responses as they arrive (use --stream flag)
- Multiple output formats - Plain text, Markdown, rich terminal, or JSON output
- Source references - Web sources extracted and displayed with inline citations
- Thread library export - Export your entire Perplexity thread history to CSV with timestamps
- Date filtering - Filter exported threads by date range
- Configurable URLs - Base URL and endpoints configurable via JSON or environment variables
- Error handling - Clear error messages with exit codes and automatic retry logic with exponential backoff
- Logging - Configurable logging with verbose/debug modes and log file support
- Connection pooling - Reuses HTTP connections for improved performance
- Optimised performance - Cached functions and guarded debug logging for minimal overhead
Installation
Quick Install (Recommended for Users)
The easiest way to use pxcli is with uvx:
uvx pxcli auth
Or install with uv pip:
uv pip install pxcli
pxcli auth
Note: The command can also be run as perplexity-cli for convenience.
Development Installation (For Contributors)
Clone and set up development environment:
git clone https://github.com/jamiemills/perplexity-cli.git
cd perplexity-cli
uv venv --python=3.12
source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv pip install -e ".[dev]"
Run tests to verify setup:
pytest
Prerequisites
- Python 3.12 or higher
- Google Chrome (for authentication)
Quick Start
Authenticate (One Time)
pxcli auth
This opens your browser to authenticate with Perplexity.ai. Your token is encrypted and stored locally.
Ask a Question
pxcli query "What is Python?"
Check Status
pxcli status
Output Formats
Query with JSON output:
pxcli query "Explain machine learning" --format json
Verbose Mode
Get detailed logging:
pxcli query "What is AI?" --verbose
Log Out
pxcli logout
Configuration
Configuration File
pxcli stores configuration in ~/.config/perplexity-cli/config.json.
Default configuration:
{
"version": 1,
"features": {
"save_cookies": false,
"debug_mode": false
}
}
Feature Toggles:
-
save_cookies(default: false) - Store browser cookies alongside JWT token- When enabled: Saves 100+ cookies including Cloudflare verification cookies
- Purpose: May help bypass Cloudflare bot detection in some environments
- Privacy: Cookies are encrypted with same security as JWT token
-
debug_mode(default: false) - Enable debug-level logging- When enabled: All commands log at DEBUG level
- Alternative: Use
--debugflag for one-time debug output
Managing Configuration:
# View current configuration
pxcli show-config
# Enable cookie storage
pxcli set-config save_cookies true
# Enable debug mode
pxcli set-config debug_mode true
# Disable features
pxcli set-config save_cookies false
pxcli set-config debug_mode false
Note: After changing save_cookies, you must re-authenticate for the change to take effect:
pxcli set-config save_cookies true
pxcli auth # Re-authenticate to save cookies
Environment Variables
Environment variables override configuration file settings:
PERPLEXITY_SAVE_COOKIES: "true" or "false" - Override cookie storage settingPERPLEXITY_DEBUG_MODE: "true" or "false" - Override debug mode settingPERPLEXITY_BASE_URL: Custom API base URL (default: https://www.perplexity.ai)PERPLEXITY_QUERY_ENDPOINT: Custom query endpointPERPLEXITY_RATE_LIMITING_ENABLED: Enable/disable rate limiting (default: true)PERPLEXITY_RATE_LIMITING_RPS: Requests per period (default: 20)PERPLEXITY_RATE_LIMITING_PERIOD: Period in seconds (default: 60)
Precedence: CLI flags > Environment variables > Config file > Defaults
Example:
export PERPLEXITY_DEBUG_MODE=true
pxcli query "test" # Uses debug mode from environment variable
Output Formats
Available formats: plain, markdown, rich, json
Default is rich for terminal output with formatting. Use --format flag:
pxcli query "..." --format markdown
pxcli query "..." --format json
Token Management
Your authentication token is encrypted and stored at:
- Linux/macOS:
~/.config/perplexity-cli/token.json - Windows:
%APPDATA%\perplexity-cli\token.json
The token is encrypted using Fernet (AES-128-CBC) with a key derived from your system hostname and OS user. Tokens are not portable between machines.
To re-authenticate:
pxcli logout
pxcli auth
Usage
Authentication Setup
The first time you use pxcli, you need to authenticate with Perplexity.ai. This is a one-time process that extracts your session token and stores it securely on your machine.
Step 1: Install Chrome for Testing
Download a dedicated Chrome browser for authentication (this keeps testing separate from your main Chrome instance):
npx @puppeteer/browsers install chrome@stable
This downloads Chrome to ~/.local/bin/chrome/ (the path may change between Chrome versions).
Step 2: Create a Shell Alias
Set up an alias to easily run Chrome with remote debugging enabled:
# Add this line to your shell config (~/.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"'
Note: The mac_arm-* pattern matches the version directory. The exact path varies by Chrome version.
Step 3: Start Chrome and Authenticate
# Terminal 1: Start Chrome with debugging enabled
chromefortesting
# Terminal 2: Run authentication
pxcli auth
The authentication process will:
- Connect to Chrome via the remote debugging port
- Navigate to Perplexity.ai
- Wait for you to log in (you'll see the login page in Chrome)
- Extract your session token automatically
- Save it encrypted to
~/.config/perplexity-cli/token.json
Once complete, you won't need to authenticate again unless you run pxcli logout.
Custom Port (Optional)
If port 9222 is already in use, specify a different port:
pxcli auth --port 9223
Then start Chrome with the matching port:
alias chromefortesting='open ~/.local/bin/chrome/mac_arm-*/chrome-mac-arm64/Google\ Chrome\ for\ Testing.app --args "--remote-debugging-port=9223" "about:blank"'
Query Perplexity
# Default: batch mode (wait for complete response)
pxcli query "What is machine learning?"
# Plain text output (for scripts)
pxcli query --format plain "What is Python?"
# Markdown format
pxcli query --format markdown "Explain quantum computing" > answer.md
# JSON format (structured output for programmatic use)
pxcli query --format json "What is machine learning?" > answer.json
# Streaming mode: see response incrementally as it arrives
pxcli query --stream "What is Python?"
# Remove citations and references section
pxcli query --strip-references "What is Python?"
# Combine options
pxcli query --format plain --strip-references "What is 2+2?"
# Use in scripts (plain text by default)
ANSWER=$(pxcli query --format plain "What is 2+2?")
echo "The answer is: $ANSWER"
# Enable verbose logging
perplexity-cli --verbose query "What is Python?"
# Enable debug logging with custom log file
perplexity-cli --debug --log-file /tmp/perplexity.log query "What is Python?"
Status and Logout
# Check authentication status
pxcli status
# Remove stored token
pxcli logout
Commands
pxcli auth [--port PORT]
Authenticate with Perplexity.ai via Chrome.
Options:
--port PORT- Chrome remote debugging port (default: 9222)
pxcli query QUESTION [OPTIONS]
Submit a query and get an answer with source references.
Arguments:
QUESTION- Your question (quoted)
Options:
--format {plain,markdown,rich,json}- Output format (default: rich)plain- Plain text, suitable for scriptsmarkdown- GitHub-flavoured Markdownrich- Terminal output with colours and formattingjson- Structured JSON with answer and references
--strip-references- Remove citations and references section--stream / --no-stream- Stream response mode (default: --no-stream)--stream- Response appears incrementally as it arrives (faster perceived latency)--no-stream- Wait for complete response before displaying (batch mode, default)
Global Options:
--verbose, -v- Enable verbose output (INFO level logging)--debug, -d- Enable debug output (DEBUG level logging)--log-file PATH- Write logs to file (default: ~/.config/perplexity-cli/perplexity-cli.log)
Exit codes:
0- Success1- Error
pxcli status
Display authentication status and token information.
pxcli logout
Remove stored authentication token.
pxcli configure STYLE
Set a custom style prompt applied to all queries.
Example:
pxcli configure "be concise"
pxcli view-style
Display currently configured style.
pxcli clear-style
Remove configured style.
pxcli export-threads [OPTIONS]
Export your Perplexity.ai thread library to CSV format with creation timestamps.
Uses your stored authentication token - no browser required after initial auth setup!
Options:
--from-date DATE- Start date for filtering (YYYY-MM-DD format, inclusive)--to-date DATE- End date for filtering (YYYY-MM-DD format, inclusive)--output PATH- Output CSV file path (default: threads-TIMESTAMP.csv)--force-refresh- Ignore local cache and fetch fresh data from Perplexity API--clear-cache- Delete local cache file before export
Examples:
# Export all threads (uses cache if available)
pxcli export-threads
# Export threads from 2025
pxcli export-threads --from-date 2025-01-01
# Export threads from a specific date range
pxcli export-threads --from-date 2025-01-01 --to-date 2025-12-31
# Export to custom file
pxcli export-threads --output my-threads.csv
# Force fresh data from API (bypass cache)
pxcli export-threads --force-refresh
# Clear cache and export fresh
pxcli export-threads --clear-cache
Setup:
Just authenticate once with pxcli auth - the export command reuses your stored token. No browser needed!
Output format:
created_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/...
The export includes:
- created_at - ISO 8601 timestamp with timezone (UTC)
- title - Thread question/title
- url - Full URL to the thread
How it works: The command uses your stored authentication token to call the Perplexity.ai API directly. It automatically paginates through your entire library (handles thousands of threads) and exports the results to CSV.
Caching: Thread exports are automatically cached locally to improve performance. On first export, all threads are fetched and cached. On subsequent exports, the cache is used unless:
- Requested date range extends beyond cached data (smart partial updates only fetch the gap)
--force-refreshflag is used to bypass cache- Cache is cleared with
--clear-cacheflag
The cache is encrypted with the same system-derived key as your auth token and stored at ~/.config/perplexity-cli/threads-cache.json.
Configuration
Token Storage and Encryption
Tokens are stored encrypted at ~/.config/perplexity-cli/token.json (Linux/macOS) or %APPDATA%\perplexity-cli\token.json (Windows).
Encryption:
- Uses Fernet symmetric encryption (AES-128-CBC)
- Key derived from system hostname and OS user
- Tokens not portable between machines
- No user passwords required
Format:
{
"version": 1,
"encrypted": true,
"token": "encrypted_token_data"
}
File permissions: 0600 (owner read/write only)
URL Configuration
Perplexity URLs are configured in ~/.config/perplexity-cli/urls.json.
Default configuration:
{
"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,
"description": "Allow 20 requests per 60 seconds (~3s delay). Override via env vars or edit this file."
}
}
To use alternative URLs, edit this file. Configuration is automatically created on first run.
Environment Variables:
You can override configuration values using environment variables:
PERPLEXITY_BASE_URL- Overridesperplexity.base_urlPERPLEXITY_QUERY_ENDPOINT- Overridesperplexity.query_endpoint
Example:
export PERPLEXITY_BASE_URL="https://custom.example.com"
pxcli query "What is Python?"
Rate Limiting Configuration
Thread export operations are rate-limited by default to prevent overwhelming the Perplexity API and encountering 429 (Too Many Requests) errors.
Default Rate Limit:
- 20 requests per 60 seconds
- Approximately 3 second delay between API requests
- Safe for exporting libraries with thousands of threads
Adjust Rate Limiting:
Edit ~/.config/perplexity-cli/urls.json and modify the rate_limiting section:
{
"rate_limiting": {
"enabled": true,
"requests_per_period": 20,
"period_seconds": 60
}
}
Common Configurations:
{
"rate_limiting": {
"enabled": true,
"requests_per_period": 10,
"period_seconds": 60,
"description": "Conservative: ~6 second delay (10 requests/60s). Use if encountering rate limits."
}
}
{
"rate_limiting": {
"enabled": true,
"requests_per_period": 30,
"period_seconds": 60,
"description": "Aggressive: ~2 second delay (30 requests/60s). Use for faster exports."
}
}
{
"rate_limiting": {
"enabled": false,
"description": "Disabled: No rate limiting (not recommended, may hit API limits)."
}
}
Environment Variable Overrides:
You can override rate limiting settings without editing the config file:
PERPLEXITY_RATE_LIMITING_ENABLED- Set to "true" or "false"PERPLEXITY_RATE_LIMITING_RPS- requests_per_period (e.g., "10")PERPLEXITY_RATE_LIMITING_PERIOD- period_seconds (e.g., "60")
Example:
# Disable rate limiting for a single export
export PERPLEXITY_RATE_LIMITING_ENABLED=false
pxcli export-threads
# Use conservative rate limiting (10 requests/minute)
export PERPLEXITY_RATE_LIMITING_RPS=10
export PERPLEXITY_RATE_LIMITING_PERIOD=60
pxcli export-threads
Troubleshooting
"Not authenticated"
Run pxcli auth to authenticate.
"Failed to decrypt token"
Token was encrypted on a different machine or with a different user. Run pxcli auth to re-authenticate.
Chrome connection fails
Ensure Chrome is running with --remote-debugging-port=9222. Verify the port matches the one you specified.
Token file has insecure permissions
Token file was modified or has incorrect permissions. Delete the file and re-authenticate:
rm ~/.config/perplexity-cli/token.json
pxcli auth
Output Formats
Plain
Plain text output suitable for scripts and piping.
pxcli query --format plain "What is Python?"
Markdown
GitHub-flavoured Markdown with headers and formatting.
pxcli query --format markdown "Explain AI" > answer.md
Rich
Terminal output with colours, bold text, and formatted tables (default).
pxcli query "What is Python?"
JSON
Structured JSON output suitable for programmatic processing and integration with other tools.
pxcli query --format json "What is machine learning?"
Output structure:
{
"format_version": "1.0",
"answer": "Machine learning is a subfield of artificial intelligence...",
"references": [
{
"index": 1,
"title": "Machine learning, explained | MIT Sloan",
"url": "https://mitsloan.mit.edu/ideas-made-to-matter/machine-learning-explained",
"snippet": "Machine learning is a powerful form of artificial intelligence..."
}
]
}
Use cases:
- Parse responses programmatically in scripts or applications
- Save structured data for later analysis
- Integrate with data pipelines
- Extract references for citation management
- Process answers through additional tools or APIs
Examples:
Save to file:
pxcli query --format json "What is Python?" > python.json
Extract and display answer as readable text:
# Use jq -r to render newlines as actual line breaks
pxcli query --format json "What is Python?" | jq -r '.answer'
Extract just the reference URLs:
pxcli query --format json "What is Python?" | jq -r '.references[] | .url'
Remove references from JSON output:
pxcli query --format json --strip-references "What is Python?"
Count the number of references:
pxcli query --format json "What is Python?" | jq '.references | length'
Parse JSON in a script:
python3 << 'EOF'
import json
import subprocess
result = subprocess.run(
["perplexity-cli", "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']}")
EOF
Note: When viewing JSON output, use jq -r (raw output) to properly display newlines in the answer text. Without -r, you'll see escape sequences like \n instead of actual line breaks.
Performance
pxcli is optimised for speed and responsiveness:
Batch Mode (Default)
By default, responses are displayed after the complete response is fetched. This is reliable for scripts and most use cases.
Optional Real-Time Streaming
Use the --stream flag to see responses incrementally as they're generated, providing the fastest perceived latency for interactive use—users see output within milliseconds.
Connection Reuse
HTTP connections are pooled and reused across multiple queries, eliminating TCP connection and TLS handshake overhead (typically 100-200ms per request on slow networks).
Optimised Logging
Debug logging is guarded, so when not enabled, no unnecessary CPU work occurs (no string formatting, no list comprehensions, no header lookups).
Cached Functions
Deterministic functions like key derivation and version lookup are cached using @lru_cache, eliminating repeated system calls.
Typical Performance
- First query response visible in: 500-800ms
- Subsequent queries: 300-600ms faster (connection reuse)
- Streaming updates (with
--stream): 10-50ms increments
Development
Setup Development Environment
git clone https://github.com/jamiemills/perplexity-cli.git
cd perplexity-cli
uv venv --python=3.12
source .venv/bin/activate # On Windows: .venv\Scripts\activate
uv pip install -e ".[dev]"
Run Tests
pytest
Linting, Formatting, and Type Checking
Format code (auto-fix):
ruff format src/
Check for linting issues:
ruff check src/
Type checking:
mypy src/
For more details on development practices, see .claude/TESTING_GUIDE.md.
Security
- Tokens encrypted at rest using Fernet
- Encryption key derived from system identifiers
- File permissions restricted to owner (0600)
- Tokens validated on each request
- Token expiration detection (warns if token is >30 days old)
- Audit logging for token operations
- No credentials printed to logs
Contributing
Contributions are welcome. Please ensure:
- Code follows PEP 8 standards
- All tests pass
- New features include tests
License
MIT
Dependencies
- click - CLI framework
- httpx - HTTP client
- websockets - WebSocket support
- rich - Terminal formatting
- cryptography - Token encryption
- tenacity - Retry logic with exponential backoff
- python-dateutil - Date parsing for thread exports
- pydantic - Data validation and serialisation
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pxcli-0.4.4.tar.gz.
File metadata
- Download URL: pxcli-0.4.4.tar.gz
- Upload date:
- Size: 89.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1cda61501bf65769dab58524cc6d353c4c67996371d41ab775c6f8ca3f30eac
|
|
| MD5 |
77d30a7d536c9da2de9acca505ae4d65
|
|
| BLAKE2b-256 |
f690b310ff3326843369522b9fdc7a5d3a6f79b08322d8879e47da8bb82ca877
|
File details
Details for the file pxcli-0.4.4-py3-none-any.whl.
File metadata
- Download URL: pxcli-0.4.4-py3-none-any.whl
- Upload date:
- Size: 68.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3857039a3006a0db2c404e6cbc8fed003a45b6886b9936f3a5926e2e2a05e503
|
|
| MD5 |
8aa065013417984636f1d4d927767c29
|
|
| BLAKE2b-256 |
09da9b99552ea8b33dc4ef64ff9e0556dade3009f41b1a37d900ba5bc52c1695
|