Skip to content
Pharma Observatory a Scientist.com platform

Command line

Pharma Observatory CLI

Read Pharma Observatory data from your terminal with the pharma command: install it, sign in, and run a dataset command or a SQL query.

pharma is a command-line client for the Pharma Observatory REST API. The command is pharma in every example. It talks to the REST API only and needs a plan with API access.

Install

Homebrew (macOS and Linux)
brew install scientist-labs/pharmaobservatory/pharma
Debian and Ubuntu (apt)
sudo install -d /usr/share/keyrings
curl -fsSL https://packages.pharmaobservatory.com/pharmaobservatory.gpg \
  | sudo tee /usr/share/keyrings/pharmaobservatory-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/pharmaobservatory-archive-keyring.gpg] https://packages.pharmaobservatory.com/apt stable main" \
  | sudo tee /etc/apt/sources.list.d/pharmaobservatory.list
sudo apt-get update && sudo apt-get install pharma
Fedora, RHEL and derivatives (dnf or yum)
sudo curl -fsSL -o /etc/yum.repos.d/pharmaobservatory.repo https://packages.pharmaobservatory.com/rpm/pharmaobservatory.repo
sudo dnf install pharma

The repository metadata is signed; the individual packages are not.

Direct download: each GitHub release carries archives for Linux, macOS and Windows. Verify the checksums with cosign (keyless) and then check the archive against them; replace the tag with the release you downloaded.

Verify a release download
cosign verify-blob \
  --certificate-identity 'https://github.com/scientist-labs/pharmaobservatory-cli/.github/workflows/release.yml@refs/tags/v0.1.0' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --bundle checksums.txt.sigstore.json checksums.txt
sha256sum --check --ignore-missing checksums.txt
Check the install
pharma version

Sign in and profiles

Sign in
pharma login                  # opens your browser; loopback redirect on 127.0.0.1:53682
pharma login --no-browser     # prints the URL; sign in anywhere, then paste the callback URL (or just its code)
pharma whoami                 # profile, origin, who, organization, plan, roles and remaining quota
pharma whoami --json          # the raw body
pharma logout                 # removes the stored credential for the profile

Profiles: prod (the default) and staging are built in. Pick one with --profile, $PHARMA_PROFILE or default_profile in config.yml. Create a custom profile with pharma login --profile NAME --origin URL.

The refresh token lives in the OS keychain (macOS Keychain, Linux Secret Service, Windows Credential Manager). Where none is available, or with PHARMA_CREDENTIAL_STORE=file, it lives in credentials.json (mode 0600) in the config directory. pharma logout deletes it locally and does not revoke it server-side.

Dataset commands

One command group per object type, then the datasets of that type. The page for each object type lists its datasets, columns and source credits.

The shapes
pharma companies list                                  # the object type's primary dataset
pharma companies get <id>                              # one record and the datasets reachable from it
pharma companies get <id> sponsor-trials               # one dataset of a record
pharma datasets list                                   # every published dataset
pharma datasets get <name>                             # one dataset's grain and columns

Command and dataset names are the API names with - for _; the API spelling also works (pharma cell_lines list). Ids are passed through verbatim. Put the id and the dataset before any flags. pharma <object> --help lists the datasets and pharma <object> get <id> <dataset> --help lists a dataset's flags.

Examples
pharma biomarkers get ESR1 biomarker-evidence
pharma trials get NCT01234567 trial-drugs
pharma conditions get D001943 condition-trials
pharma companies get <id> sponsor-trials --scope group

Every dataset read takes --limit, --cursor, --sort, --group-by and --measure (and --scope on company datasets), plus --filter, repeatable and keyed by column. --filter column=value is equality (a repeated column is an IN list); column.gt, .gte, .lt, .lte, .ne, .contains and .null are the other operators.

Filter, sort and group
pharma biomarkers list --filter gene_symbol=EGFR --filter evidence_tier.lte=2 --sort -n_trial_mention
pharma biomarkers list --group-by biomarker_kind --measure count

--all follows the cursor to the last page and prints one table (or one CSV); it cannot be combined with --json. The walk stops early, prints what it has and exits 9 when the operation takes no --cursor, the server repeats a cursor, or it reaches 1000 pages.

pharma api <path> makes an authenticated GET and prints the body; -F key=value adds a query parameter.

The api command
pharma api biomarkers -F kind=gene -F limit=5
pharma api /api/v1/whoami

SQL with query

pharma query runs one SQL SELECT over one published dataset, with no JOIN. It needs a signed-in account (pharma login first). Pass - to read the SQL from stdin.

Run a query
pharma query "SELECT biomarker_kind, count(*) AS n FROM biomarkers GROUP BY biomarker_kind"
pharma query "SELECT display_name, gene_symbol FROM biomarkers WHERE gene_symbol = 'EGFR'" --csv
pharma query - < query.sql

The server stores the full result for 24 hours and enforces its own limits on rows, statement length and time. The footer names the stored result and the next page; page it with --result and --cursor, or add --all to follow the cursor to the end.

Page a stored result
pharma query --result <result_id> --cursor <cursor>
pharma query --result <result_id> --all

A refused query prints the server's error code, message and hint, and exits with the code in the table below (most refusals exit 2).

Output formats

The default is a table, with a footer on stderr (source credit, as-of date, paging).

Table, JSON and CSV
pharma biomarkers list --limit 5
pharma biomarkers list --limit 5 --json
pharma biomarkers list --limit 5 --csv

--json prints the server's body unchanged and --csv builds CSV from the response columns; they cannot be combined. --quiet (alias --no-footer) drops the as-of and paging lines. The source credit always prints: on stderr, and as trailing # Source: lines in CSV output. To skip those lines in pandas, read the file with comment='#'.

Exit codes and quota

A 429 exits 6 and says which limit was hit. The CLI does not wait by default; --wait sleeps for the Retry-After (at most 2 minutes) and retries once. A paging-depth refusal has no Retry-After: narrow the query. pharma exit-codes prints this table.

Wait out a rate limit
pharma biomarkers list --all --wait
Exit codes
Exit Class When
0 ok Success
1 unexpected An error the table does not name, or a failure in the CLI itself
2 usage A bad flag or argument, an unknown command, or a query the server refuses
3 auth Not logged in, or a rejected token
4 forbidden Access pending, a missing role, or not entitled
5 not found An unknown record or page, or a stored query result that expired
6 rate limited A quota was reached; the message says which, when it resets and how long to wait
7 unavailable A 5xx, a network failure, a timeout, or a changed warehouse generation
8 not answered A 200 whose body says unbuilt or not applicable
9 partial `--all` printed some pages and then stopped

Configuration

The config directory is $PHARMA_CONFIG_DIR, else $XDG_CONFIG_HOME/pharma, else ~/.config/pharma. It holds config.yml and, where no keychain is available, credentials.json.

New API endpoints arrive in the commands automatically: they are generated from the API document.

Markdown copy of this page