nanoclip
v0.1.5
Published
Official CLI for the NanoClip API — upload footage, quote and approve billed analyses, fetch data packages.
Readme
nanoclip
The official command-line client for the NanoClip API. Upload footage, get an exact quote before anything is billed, approve and dispatch analyses (transcript, vision, retake-removal) one line item at a time, and collect the finished data packages.
nanoclip speaks JSON on stdout and nothing else there — built for scripts and agents first,
humans second.
Install
No install needed to try it:
npx nanoclip --versionOr install it globally:
npm i -g nanoclipRequires Node.js >= 22. ffmpeg and ffprobe must be on PATH for local media preflight and
normalization — see System dependencies below.
Authenticate
Don't have a key yet? Run auth login with no arguments — it prints where to create one:
nanoclip auth login # prints the sign-in link; add --open to launch a browserThen come back with the key:
nanoclip auth login --key <your-api-key>This verifies the key live against the API and writes it to ~/.nanoclip/config.json (mode
0600), along with a cached snapshot of your workspace, scopes, and account limits. Everything
else reads from this profile.
nanoclip auth status # offline — reads the cache, no network call
nanoclip auth logout # removes the active profileMultiple profiles are supported via --profile <name> on login/logout, or the
NANOCLIP_PROFILE env var when running a command.
The golden path
# 1. Upload footage. Output includes the server-probed duration — the billing basis.
nanoclip upload ./interview.mp4
# -> {"project_id": "proj_abc123", "duration": 612.4, ...}
# 2. Quote before you spend anything. Read-only — this never charges.
nanoclip quote -p proj_abc123 --transcript --vision
# -> {"line_items": {"transcript": 102, "vision": 60}, "total_cents": 162, "total_usd": "1.62", ...}
# 3. Dispatch each billed analysis with its own exact approval.
nanoclip transcript start -p proj_abc123 --language en --approve 1.02
nanoclip vision start -p proj_abc123 --approve 0.60
# 4. Collect the result — poll until it's done, write it to disk.
nanoclip transcript get -p proj_abc123 --wait -o transcript.jsonYou can also get an early, non-final estimate before uploading anything:
nanoclip quote --source ./interview.mp4 --transcript --vision
# -> {"estimate": true, "basis": "local_probe", ...}Agent-first output convention
Every command writes one JSON document to stdout (an object, trailing newline, nothing else on that stream) and one human-readable summary line to stderr. No tables, no spinners, no interactive prompts. This makes every command safe to pipe, parse, and script:
project_id=$(nanoclip upload ./clip.mp4 | jq -r .project_id)Errors follow the same rule — structured JSON on stdout, one line on stderr, then the process exits with a code from the taxonomy below:
{"error": {"reason": "approval_insufficient", "message": "...", "status": 402, "detail": {...}}}Exit codes
| Code | Meaning |
|---|---|
| 0 | success |
| 2 | input error — bad arguments, missing/invalid file, refused approval amount, 4xx validation |
| 3 | auth — missing or invalid API key, HTTP 401/403 |
| 4 | payment required — HTTP 402. Full stop; nothing overrides it; the request is never retried |
| 5 | API/network — timeouts, connection errors, 5xx after retries |
The spend gate
No command both quotes and charges. quote is always read-only. Every command that actually
dispatches a billed analysis — transcript start, vision start, retake-removal start — requires
its own --approve <USD>, and refuses (exit 2, nothing sent) if the live price at dispatch
time exceeds that amount by even a cent. There is no account-level spending threshold and no
"trust me" flag: every charge, tiny or large, is approved explicitly on the command line, so it's
visible in full in any shell history or agent transcript.
nanoclip transcript start -p proj_abc123 --language en --approve 1.02
# live cost recomputed at dispatch time; > $1.02 -> refused before any request firesCommands
| Command | What it does |
|---|---|
| nanoclip auth login [--open] | Print (and optionally open) the link to create an API key |
| nanoclip auth login --key <key> [--base-url <url>] [--profile <name>] | Verify a key and store it |
| nanoclip auth status | Show the active profile, offline |
| nanoclip auth logout [--profile <name>] | Remove a stored profile |
| nanoclip me | Live account info: workspace, scopes, balance, pricing, limits |
| nanoclip doctor | Check connectivity, key validity, and ffmpeg/ffprobe presence |
| nanoclip upload <video> | Preflight, normalize if needed, and upload a video or audio file |
| nanoclip quote -p <id> \| --source <video> [--transcript] [--diarize] [--vision] [--retake-removal] | Price one or more analyses, read-only |
| nanoclip transcript start -p <id> --language <tag> [--diarize] --approve <USD> | Dispatch a billed transcript analysis (see Languages) |
| nanoclip transcript get -p <id> [--wait] [-o <file>] | Collect a transcript analysis |
| nanoclip vision start -p <id> --approve <USD> | Dispatch a billed vision analysis |
| nanoclip vision get -p <id> [--wait] [-o <file>] | Collect a vision analysis |
| nanoclip retake-removal start -p <id> --language <tag> --approve <USD> | Dispatch a billed retake-removal analysis |
| nanoclip retake-removal get -p <id> [--wait] [-o <file>] | Collect a retake-removal analysis |
| nanoclip status -p <id> | Project status plus every analysis's current status in one call |
| nanoclip api <method> <path> [--input <file>\|-] | Raw authenticated request to any API endpoint |
| nanoclip --version | Print {name, version} |
| nanoclip --help, nanoclip <command> [<subcommand>] --help | Print the command surface (-h works too) |
Every command documents itself. nanoclip --help lists the whole surface, and
nanoclip <command> --help (or nanoclip <command> <subcommand> --help) gives that command's
flags — as JSON on stdout for a program to read, and as readable usage on stderr for you:
nanoclip transcript start --help
# stdout: {"name":"start","usage":"nanoclip transcript start --project <id> --approve <USD> ...
# stderr: the same surface, formatted to readThe JSON is generated from the same argument definitions the commands parse with, so it always matches what the CLI actually accepts.
get for any analysis: --wait polls until the analysis reaches a terminal status; -o <file>
additionally writes the collected data package to a file. A failed analysis is still a
successful collection — the failure is in the payload, not the exit code. Collecting an analysis
that was never dispatched exits 2 with reason not_started.
Languages
transcript and retake-removal take a --language <tag> — a base tag, lowercase, no region
subtag (en, not en-US). The language is stated, never detected from the audio. Supported:
bg cs da de el en es et fi fr he hr hu it lt lv mt nl pl pt ro ru sk sl sv ukA tag outside that set is refused before any network call (exit 2, unsupported_language).
vision takes no language — it looks at pixels, not speech.
System dependencies — doctor
ffmpeg and ffprobe are spawned as system binaries, never bundled. upload uses ffprobe to
preflight a file (container, codec, size) before sending a single byte, and spawns ffmpeg to
normalize a file into an accepted format when needed. Run:
nanoclip doctorto check config validity, API key validity, live connectivity, and whether both binaries are on
PATH, in one call.
Environment variables
Every value below overrides the corresponding entry in ~/.nanoclip/config.json for the current
process — nothing else reads or writes outside that one file.
| Variable | Overrides |
|---|---|
| NANOCLIP_API_KEY | The active profile's API key |
| NANOCLIP_BASE_URL | The active profile's API base URL |
| NANOCLIP_PROFILE | Which profile is active |
| NANOCLIP_CONFIG_DIR | Where the config file lives (default ~/.nanoclip) |
License
MIT
