Skip to main content

Overview

The MoltSets CLI is the command-line tool for MoltSets. Use it to search people and companies, enrich CSV files with verified emails, mobile phones, and profiles, and check your plan’s limits.
  • One command per API tool, generated from the MoltSets OpenAPI spec.
  • moltsets enrich runs any lookup over a CSV, with batching, dedupe, resume, fair use waits, and plan-boundary notes built in.
  • --all and --max page through searches within your plan’s concurrency limit. --out writes straight to CSV.
  • JSON on stdout when piped, readable tables in a terminal, logs on stderr, and documented exit codes.
Use the CLI for bulk jobs, CSV files, scripts, and paginated searches. Use the MoltSets MCP for one-off lookups in chat.

Install

The CLI needs Node.js 18 or later.
The code is published as @moltsets/cli. The moltsets package is a shortcut to it, so npx moltsets works. Both install the same moltsets command, so install only one of them globally.

Quick start

1

Create an API key

Create a free account at app.moltsets.com, then create an API key under API keys. Keys start with ms_. See Authentication for details.
2

Save the key

Run moltsets login, or set export MOLTSETS_API_KEY=ms_....
3

Check your setup

Run moltsets whoami.
4

Try a lookup

Authentication

The CLI looks for your API key in this order:
  1. --api-key ms_... on the command line.
  2. The MOLTSETS_API_KEY environment variable.
  3. The config file saved by moltsets login:
    • macOS and Linux: $XDG_CONFIG_HOME/moltsets/config.json, or ~/.config/moltsets/config.json.
    • Windows: %APPDATA%\moltsets\config.json.
moltsets login opens the API keys page in your browser and asks you to paste a key. It checks the key with get_account (free) and saves it with file permissions 600. It only runs in an interactive terminal. In scripts, CI, and agents, set MOLTSETS_API_KEY instead. moltsets logout removes the saved key. These account commands are free. They spend no tokens or fair use records:

Searching

search_people and search_companies return up to 25 records per call (10 on the Free plan). Each record returned counts against your plan’s search records, so probe first:
Then page through the results:
  • --all pages until the results run out. --max <n> stops after n records.
  • The API caps total at 10,000, so a total of 10,000 means at least that many. When total is capped, the CLI keeps paging until a page comes back short.
  • Pages run in parallel, never more at once than your plan allows: 3 on Free and Solo, 5 on Pro, 8 on Team and Max. See Rate limits.
  • --out file.csv flattens nested objects with dot notation (company.name, company.domain). Lists are joined with ; .

Flag values

  • Filters that accept one or more values take a repeated flag or commas: --country "United States" --country Canada, or --seniority VP,Director. Use \, for a literal comma, for example --company "Acme\, Inc.".
  • List-only filters such as --exclude-company-domain are always sent as a list.
  • Enum filters (seniority, industry, department, functional_area, employee_range, revenue_range, and their exclude_ versions) are checked before any call. A bad value fails with exit code 2 and the list of allowed values.
  • --body '<json>' or --body-file path.json sends a raw request body, for anything the flags can’t express. Flags are applied on top.

Suppression lists

Pass the accounts you already have with --exclude-company-domain (people) or --exclude-domain (companies). Excluded records cost nothing, so this is always cheaper than filtering the results yourself:

Enriching a CSV

Always start with a dry run, then a small sample:
Tools that take several inputs use --map param=column:
Not sure which tool to use? See I have X, I want Y.

How enrichment works

  • Batching. Tools with a batch parameter (linkedin_urls or emails) are sent 100 rows at a time. Results are matched back to rows by the input they echo, never by position. Other tools run one row per call, 5 at a time by default (--concurrency). For search_* tools, concurrency is capped at your plan’s search limit. --no-batch forces one row per call.
  • Dedupe and skips. The same input is looked up once, and the result is copied to every row that has it. Matching is case-insensitive, and a LinkedIn URL and its slug count as the same. Rows with an empty input are marked skipped.
  • Resume. Progress is saved to <output>.moltsets-state.json after every batch. Re-run the same command to carry on, or pass --restart to start again.
  • Fair use. Every response reports the records left in the 5-hour and weekly windows. If the 5-hour window can’t cover the next batch, the CLI says so on stderr and waits until it resets, rather than running into a 429. --no-wait saves and stops instead (exit code 5).
  • Tokens and plan limits. On insufficient_tokens, insufficient_phone_tokens, or plan_restricted, the CLI keeps any partial results, marks the rest dropped, saves, and stops (exit code 4). The raw response is also kept in <output>.moltsets-raw.json, so results you already paid for are never lost.
  • Progress shows on stderr: rows done, found, not found, errors, and an estimate of the time left.
  • Dry run. --dry-run counts rows after dedupe and skips. It compares the expected usage with your fair use headroom, token balance, and phone token balance. For name-based tools, it samples up to 20 rows with the free search_linkedin_profile count_only search to estimate the match rate (--sample <n>).

Output columns

The output keeps every original column and adds: Plan-boundary notes mean the value exists but your plan withheld it. They aren’t misses:
  • personal_email_redacted: personal email withheld past the Team and Max personal email cap (15,000 per 5 hours, 75,000 per week). It resets on its own.
  • personal_email_restricted: personal email isn’t available on your account.
  • identity_hashes_restricted: SHA256 hashes aren’t returned on the Team and Max plans.
Every email carries an A to F risk score. Where an email has no grade, the CLI fills in F in JSON, tables, and CSV.

Output and exit codes

  • When stdout isn’t a terminal, commands print JSON: the full API envelope { "results": ..., "status": "ok" | "not_found", "metadata": ... }. In a terminal, they print a readable table or summary. --json and --table force either.
  • Logs, progress, and warnings go to stderr, so stdout is always clean data. -q hides progress and information messages.
  • The CLI never prompts unless both stdin and stdout are terminals.
  • status: "not_found" is a normal miss. Nothing is charged and the exit code is 0.
  • On a 429, the CLI waits for metadata.retry_after or Retry-After and retries up to 4 times. Waits longer than 5 minutes fail straight away with exit code 5. search_concurrency_exceeded is retried after 1 second.
  • On a 500, a batch is retried once as two halves, and any other request is retried once, before failing.
Errors print the API error code and a one-line suggestion:

Using the CLI with AI agents

The CLI is built to be driven by agents: JSON on stdout when piped, nothing interactive, stable exit codes, and resumable bulk jobs. Install the agent skill to teach Claude when and how to use the CLI safely. It covers dry run first, sample, then the full run; suppression lists; reading ms_status, ms_note, and risk scores; and exit codes.

Command reference

Every tool in the API is a subcommand with the same name. Each request body property is a flag, with snake_case turned into kebab-case. For example, linkedin_url becomes --linkedin-url. Run moltsets <tool> --help to see the flags, allowed values, and an example. See the API Reference for every tool and its parameters. All tools also take --body and --body-file, plus the global options --api-key, --json, --table, and -q. Other commands: whoami, account, billing, usage, login, logout, enrich, and skill install.