vainu-cli
CLI and Python client library for the Vainu company data API. Query Nordic company data, export large datasets, and integrate Vainu into your workflows.
New here? Start with Get started — copy one command, sign in, run your first search. No coding experience required.
Get started
You will copy one command, sign in with your browser, and run your first company search.
Time: about 5 minutes.
Step 1 — Install
Pick the section for your computer. The installer uses uv to handle Python for you — you do not need to install Python yourself.
Mac
- Open Terminal (press
Cmd + Space, typeTerminal, press Enter). - Copy and paste this entire line, then press Enter:
curl -LsSf https://raw.githubusercontent.com/vainu-app/vainu-cli/main/scripts/install.sh | sh
- Wait until you see
Next steps:— installation is done. - Close Terminal and open it again (so the
vainucommand is recognized).
Windows
- Open PowerShell (Start menu → type
PowerShell→ open Windows PowerShell). - Copy and paste this entire line, then press Enter:
irm https://raw.githubusercontent.com/vainu-app/vainu-cli/main/scripts/install.ps1 | iex
- Wait until you see
Next steps:— installation is done. - Close PowerShell and open it again.
If Windows blocks the script, run PowerShell as your normal user (not Administrator) and try again.
Linux
Same as Mac — open a terminal and run:
curl -LsSf https://raw.githubusercontent.com/vainu-app/vainu-cli/main/scripts/install.sh | sh
Then close and reopen the terminal.
Step 2 — Sign in
In Terminal (Mac/Linux) or PowerShell (Windows), run:
vainu login
Your browser opens. Sign in with your Vainu account. When done, return to the
terminal — you should see Logged in as ....
Step 3 — Verify
vainu doctor
You should see All checks passed. If something failed, the command prints what to
fix.
Step 4 — Your first search
Look up a Finnish company by business ID:
Mac / Linux:
vainu organizations --payload "$(vainu examples path 08-simple-filtering)"
Windows (PowerShell):
vainu organizations --payload (vainu examples path 08-simple-filtering)
You get JSON with company details. That's it.
Troubleshooting
| Problem | Fix |
|---|---|
'vainu' is not recognized |
Close and reopen Terminal / PowerShell. Still broken? Run install again. |
| Mac: command not found after install | Add to ~/.zprofile: export PATH="$HOME/.local/bin:$PATH", then open a new window. |
| Linux: command not found after install | Add to ~/.bashrc: export PATH="$HOME/.local/bin:$PATH", then open a new window. |
| Windows: command not found | Close PowerShell completely and reopen. Check %USERPROFILE%\.local\bin exists. |
| Login browser does not open | Run vainu login --no-browser and open the printed URL manually. |
| Signals commands fail | Signals need OAuth — use vainu login, not an API key alone. |
| Want the latest CLI | Run vainu update (or vainu upgrade). |
Run vainu doctor anytime to diagnose install and auth issues.
Other install options
Requires Python 3.11+ — uv installs it for you if it is missing. The install scripts above pull from PyPI and fall back to GitHub while the package is not published yet, so the commands here are only needed for manual installs.
From a git checkout:
./scripts/install.sh --local # Mac / Linux
.\scripts\install.ps1 -Local # Windows (PowerShell)
Straight from GitHub with uv:
uv tool install git+https://github.com/vainu-app/vainu-cli.git
As a library dependency in a Python project:
uv add git+https://github.com/vainu-app/vainu-cli.git
To move to a later release once installed: vainu update (or vainu upgrade).
Using with AI agents? See AGENTS.md and the
vainu-cli skill.
Quick start
CLI
Set your API key:
export VAINU_API_KEY=your-api-key
Search for a company:
vainu companies --query "?country=FI&business_id=FI01320292"
Export a large dataset to a file:
vainu companies-async \
--query "?country=FI" \
--format jsonl \
--output finnish_companies.jsonl
Search organizations with a JSON payload. --payload takes inline JSON, a file path, or
- to read stdin:
vainu organizations --payload '{"query": {"country": "SE"}}'
vainu organizations --payload payload.json
echo '{"query": {"country": "SE"}}' | vainu organizations --payload -
Python library
Async client:
import asyncio
from vainu_cli import VainuAPIKeyClient
async def main():
client = VainuAPIKeyClient(api_key="your-api-key")
try:
result = await client.companies(payload="?country=FI&business_id=FI01320292")
print(result)
# Async export job — polls until complete
async_result = await client.companies_async(
payload="?country=FI", format="jsonl"
)
await async_result.download_to_file("companies.jsonl")
finally:
await client.close()
asyncio.run(main())
Sync client:
from vainu_cli import VainuAPIKeySyncClient
client = VainuAPIKeySyncClient(api_key="your-api-key")
try:
result = client.companies(payload="?country=FI&business_id=FI01320292")
print(result)
finally:
client.close()
Authentication
API key (default)
export VAINU_API_KEY=your-api-key
vainu companies --query "?country=FI"
# or pass inline
vainu --api-key your-api-key companies --query "?country=FI"
OAuth 2.0 client credentials
export VAINU_CLIENT_ID=your-client-id
export VAINU_CLIENT_SECRET=your-client-secret
vainu --auth-method oauth companies --payload payload.json
Tokens are fetched and refreshed transparently, and the access token is cached between invocations so repeated commands skip the token round-trip. The cache lives in the OS keyring (Keychain / Secret Service / Credential Locker), falling back to a 0600 file under the user config dir when no keyring backend is available. Entries are keyed by base URL, client ID and scope, so several environments can be used side by side.
VAINU_TOKEN_CACHE=0 vainu --auth-method oauth companies ... # mint a fresh token
VAINU_AUTH_STORE=file vainu --auth-method oauth companies ... # skip the keyring
vainu auth logout # drop cached tokens
A token that the API rejects with 401 is discarded and the request retried once, so a revoked token costs one extra call rather than an hour of failures.
CLI reference
vainu [OPTIONS] COMMAND [ARGS]...
Options:
--auth-method [apikey|oauth|jwt] Authentication method (default: apikey)
--api-key TEXT API key (or VAINU_API_KEY)
--client-id TEXT OAuth client ID (or VAINU_CLIENT_ID)
--client-secret TEXT OAuth client secret (or VAINU_CLIENT_SECRET)
--base-url TEXT Override API base URL
--async-mode / --no-async-mode Use async client for search commands
-v, --verbose Enable DEBUG logging
--version Show version and exit
Commands:
companies Fetch company data
companies-async Export company data via async job
organizations Fetch organization data
organizations-async Export organization data via async job
organizations-count Count matching organizations without returning rows
fields Inspect organization field metadata
enrichment-agent Run an enrichment agent prompt against one company
signals-news Fetch news signals
signals-data-changes Fetch company data-change signals
lists Manage organization lists (static and dynamic)
update Upgrade vainu-cli to the latest PyPI release
upgrade Alias for update
vainu companies
--query TEXT Query string, e.g. "?country=FI"
--payload JSON/FILE/- Inline JSON, a file path, or "-" for stdin
--payload-path ... Alias for --payload
--format json | csv | jsonl (default: json)
--stream/--no-stream Stream lines as they arrive (default on for csv/jsonl)
--output FILE Write to file instead of stdout
vainu companies-async
Submits an async export job, polls until complete, and downloads the result.
--query TEXT Query string
--payload JSON/FILE/- Inline JSON, a file path, or "-" for stdin
--payload-path ... Alias for --payload
--format json | csv | jsonl (default: json)
--output FILE Output file (required)
--poll-interval INT Polling interval in seconds (default: 3)
--timeout INT Max wait seconds (default: 14400)
vainu organizations / vainu organizations-async
Same options as the company commands (organizations always use POST with a JSON payload).
organizations takes --stream / --no-stream too; the -async export commands do not, since
they already download to a file in chunks.
organizations and organizations-async also accept --payload-path as an alias for
--payload.
To see which field paths you can put in query vs the fields output list, use
vainu fields organizations.
vainu organizations-count
Counts the organizations matching a query without returning any rows — the cheap way to size a
segment before exporting it. Takes the same query and database as organizations, but
fields, limit and offset are ignored: only count metadata comes back.
A payload written for organizations can be passed straight to organizations-count. order is
stripped before sending, because the count endpoint rejects it with
400 invalid order by value for any value — sorting is meaningless when only a total comes back.
--payload JSON/FILE/- Inline JSON, a file path, or "-" for stdin
--payload-path ... Alias for --payload
--database TEXT Country database, e.g. FI, SE, NO or DK
--list TEXT Count a saved Vainu list (supplies its own query and database)
--recount/--no-recount Force a fresh count instead of reusing the cached one
--max-cache-age INT Recount if the cached count is older than this many seconds
--wait/--no-wait Keep re-requesting until the count is ready (default: --wait)
--poll-interval INT Polling interval in seconds (default: 3)
--timeout INT Max wait seconds (default: 14400)
--language TEXT Accept-Language header
--output FILE Write to file instead of stdout
--database, --list, --recount and --max-cache-age override the same keys in --payload,
so you can keep the query in a file and vary only the country:
vainu organizations-count --payload "$(vainu examples path 01-amount-of-companies)"
vainu organizations-count --payload "$(vainu examples path 01-amount-of-companies)" --database SE
# count a saved list without writing any JSON
vainu organizations-count --list 68a1f2c9abcdef0123456789
# inline JSON, no file needed
vainu organizations-count --payload '{"query": {"?GTE": {"financial_data.revenue": 1000000}}, "database": "FI"}'
# just the number, for scripting
COUNT=$(vainu organizations-count --list 68a1f2c9abcdef0123456789 | jq .count)
The response is count metadata, not rows:
{
"count": 180086,
"time": "2026-04-16T15:21:16.445551",
"status": "ready",
"duration": 164.5,
"rate_of_change": null,
"eta_utc": null
}
About --wait. The API computes counts in the background and defaults to async: true, so a
cold cache answers {"count": null, "status": "scheduled"} and the finished count is collected by
re-sending the same payload. --wait (the default) does that for you until status leaves
scheduled/process, tolerating a few transient network failures on the way. --no-wait prints
that first reply as-is, which is what you want when you only need status and eta_utc:
vainu organizations-count --list 68a1f2c9abcdef0123456789 --recount --no-wait
A status of error prints the body and exits non-zero.
vainu update / vainu upgrade
Upgrade the CLI to the latest PyPI release. Prefers uv tool upgrade vainu-cli (what
the install scripts use); falls back to pip install --upgrade vainu-cli if uv is
not on PATH.
vainu update
vainu upgrade
vainu fields
Lists organization field metadata from
GET /v3/organizations_fields/ — names, types, and whether each path is filterable
(usable in a VQL query) or output (usable in the fields list). The response is a
catalog, not company records.
vainu fields organizations
--view table | summary | json (default: table)
--output FILE Write the view to a file instead of stdout
--api-version TEXT API version query param (default: v3)
--category TEXT Only this main_category (e.g. basic, contacts)
--database TEXT Only fields available for FI | SE | NO | DK | NL
--search TEXT Case-insensitive match on path, English name, or description
--filterable Only fields usable in VQL filters (filter/search)
--returnable Only fields usable in the fields output list (export/profile)
--permission-gated Only fields that require an extra account entitlement
vainu fields organizations
vainu fields organizations --filterable --search revenue
vainu fields organizations --returnable --category contacts
vainu fields organizations --permission-gated --view summary
vainu fields organizations --view json --output fields.json
The default table view is grep-friendly: path, name, filterable, output,
permission, type. summary prints counts by category and lists permission gates.
json returns the (optionally filtered) API payload, including allowed operators.
Filterable means application_availability includes filter or search. Output means
export or profile. Some paths — contact email/phone, payment delays, vehicles, real
estate — are listed with a requires_permission slug and stay inaccessible until the
account is entitled to them.
Streaming
--format jsonl and --format csv stream by default: each line is written the moment it
arrives, rather than the command sitting silent for a minute and then printing everything at
once. Nothing larger than a single line is held in memory, so piping a large export into another
tool costs almost nothing:
vainu organizations --payload payload.json --format jsonl | jq -r .business_id
vainu signals-news --payload payload.json --format jsonl --output signals.jsonl
--format jsonnever streams — a JSON body is a single document and is not valid until its last byte. Asking for--stream --format jsonis an error; leaving the flag off just buffers.--no-streamforces the old buffered behaviour forjsonl/csv.- Works under both the default sync client and
--async-mode. - With
csv, the first line out is the header row. - Streaming drops blank lines and, with
--output, always ends the file with a newline;--no-streamcopies the body verbatim, trailing byte included. The rows themselves are the same either way.
vainu enrichment-agent
Runs an enrichment agent prompt against one company and prints the structured fields the prompt defines. The prompt itself is built in the Vainu UI — this command only needs its id.
--prompt TEXT Enrichment agent prompt id from the Vainu UI (required)
--business-id TEXT Company business id, e.g. FI23365096 (required)
--database TEXT Country database: FI | SE | NO | DK (required)
--refresh/--no-refresh Re-run instead of reusing the cached answer
--payload JSON/FILE/- Inline JSON, a file path, or "-" for stdin (alternative to the flags above)
--payload-path ... Alias for --payload
--format json | jsonl (default: json)
--language TEXT Accept-Language header
--request-timeout INT HTTP timeout in seconds (default: 121)
--output FILE Write to file instead of stdout
vainu enrichment-agent --prompt 12345 --database FI --business-id FI23365096
{
"response": {
"main_business_activity": "Supercell Oy is a mobile game developer that creates …",
"products_and_services": "Supercell's primary products are its mobile games …"
}
}
The keys under response are whatever the prompt was configured to return, so they differ per
prompt. Notes worth knowing:
- Like the rest of the v3 API, this needs an OAuth or JWT token — not a static API key.
- Results are cached per prompt + company. A cached answer comes back quickly and spends no
Vainu agent credits;
--refreshforces a fresh run and does spend them. - An uncached run researches the company on the spot and can take minutes. Raise
--request-timeoutfor those; the run continues server-side either way, so a re-run after a timeout usually returns the now-cached answer. --prompt/--database/--business-idmay come from--payloadinstead, and any flag you also pass overrides the file. That makes a saved payload reusable: keep the prompt id and database in the file and vary--business-idper run.- There is no
--stream— a single enrichment result is one document, not a row stream.
vainu signals-news / vainu signals-data-changes
Fetch signals from the v3 Signals API (beta): signals-news returns externally sourced events
(news, press releases, contract notices), signals-data-changes returns events generated from
changes in company records.
--payload JSON/FILE/- Inline JSON, a file path, or "-" for stdin (required)
--payload-path ... Alias for --payload
--format json | jsonl (default: json)
--stream/--no-stream Stream lines as they arrive (default on for jsonl)
--language TEXT Accept-Language header
--output FILE Write to file instead of stdout
Both endpoints require an OAuth or JWT token — the v3 API does not accept a static API key.
Run vainu login, or set VAINU_CLIENT_ID / VAINU_CLIENT_SECRET and pass
--auth-method oauth.
Payload keys: query (required — see the
filtering query language),
limit (default 20, capped at 100) and offset (capped at 100000). Notes worth knowing:
- The response is a bare JSON array, newest first. There is no
countornext, andorderis not supported — sending it returns400 invalid order by value. csvis not available; use--format jsonlfor exports and page withlimit/offsetuntil a page returns fewer rows thanlimit.- Signal types are integer
tagsids, shared by both endpoints and by the Vainu UI. The full list is atTAGS_BY_TYPE.json. - Relative dates need plural units:
"30 days ago"and"1 years ago"work,"1 year ago"returns 400. - News signals can be filtered by
content,title,link,typeandcountries; data-change signals only bytags,vainu_date,business_idsandprospects. business_idstakes country-prefixed ids (FI25578642) and supports?INonly.- An empty array means "nothing matched" or "the query timed out", and an unknown field name is ignored rather than rejected — so always include a date bound and check field spelling when a result looks too large or too empty.
vainu lists
Manage saved organization lists — both dynamic lists (membership from a VQL query, shown as "My Lists" in the Vainu UI) and static lists (a fixed set of business IDs, shown as "Custom Lists").
Requires OAuth, JWT, or vainu login — not a static API key alone.
vainu lists List all lists (static + dynamic)
vainu lists get ID Retrieve one list by id
vainu lists delete ID Delete any list by id
vainu lists static List static lists
vainu lists static get ID
vainu lists static create --payload JSON/FILE/- Required: name, country (FI|SE|NO|DK|NL)
vainu lists static update ID --payload JSON/FILE/-
vainu lists static add ID --payload JSON/FILE/- JSON array of business IDs
vainu lists static remove ID --payload JSON/FILE/-
vainu lists static delete ID
vainu lists dynamic List dynamic lists
vainu lists dynamic get ID
vainu lists dynamic create --payload JSON/FILE/- Required: name, country, query (serialized VQL)
vainu lists dynamic update ID --payload JSON/FILE/-
vainu lists dynamic delete ID
--format json | jsonl (default: json)
--output FILE Write JSON response to file (create/update/get/list commands)
List all accessible lists and grab an id for export:
vainu lists
vainu lists get 63d8de4eb7dfe9f5896fa539
Create a dynamic list (query is a JSON string, not a nested object):
cat > dynamic.json <<'EOF'
{
"name": "FI companies with 500+ employees",
"country": "FI",
"query": "{\"?GTE\": {\"financial_data.employees.absolute_count\": 500}}"
}
EOF
vainu lists dynamic create --payload dynamic.json
Create a static list and add/remove companies (replace placeholder business IDs with your own):
vainu lists static create --payload - <<'EOF'
{"name": "Targets", "country": "FI", "business_ids": ["FI01234567"]}
EOF
echo '["FI07654321"]' | vainu lists static add LIST_ID --payload -
echo '["FI01234567"]' | vainu lists static remove LIST_ID --payload -
Rename or replace membership via update:
echo '{"name": "Renamed list"}' | vainu lists static update LIST_ID --payload -
echo '{"business_ids": []}' | vainu lists static update LIST_ID --payload - # clear all members
Export every company in a saved list (swap in your list id from vainu lists):
vainu organizations-async \
--payload example_payloads/organizations_api/04-get-all-companies-in-vainu-list-async-sync.json \
--format jsonl \
--output list_companies.jsonl
Notes:
- Static lists support atomic add and remove (
PATCH .../add/and.../remove/); dynamic lists do not — edit thequerywithvainu lists dynamic updateinstead. countryis immutable after creation.- Each
business_idmust match the list country prefix (FI…,SE…, etc.). - Add/remove payloads must be a JSON array of ids, e.g.
["FI01234567"], not an object.
Example payloads
The repository ships ready-made Organizations API request bodies under
example_payloads/organizations_api/,
transcribed from the v3 recipes. Each file is a
complete POST body — query, fields, database, limit and friends at the top level — so it
can be handed straight to --payload. The paths below assume you have cloned the repo.
Each file also carries a _meta block naming the recipe it came from. It is sent along with the
rest of the body and the API ignores it; drop it if you prefer a minimal request.
| File | What it shows | Notes |
|---|---|---|
01-amount-of-companies-matching-the-query.json |
Counting NO companies with revenue ≥ 1M | Use with vainu organizations-count |
02-filter-contacts-return-only-ceos-of-companies.json |
Subdocument aggregation returning only CEO / Privacy Officer contacts | |
03-geospatial-business-unit-search-returning-only-matching-business_units.json |
Geo sphere search with unwind_subdocument — only matching business units |
|
04-get-all-companies-in-vainu-list-async-sync.json |
Every company in a saved Vainu list | list is a placeholder ID — swap in your own |
05-get-count-of-vehicles-with-make-and-registration.json |
Per-company Skoda count via ?FILTER_SUBDOCUMENTS + ?COUNT |
limit is 1000000 — lower it before running interactively |
06-simple-fuzzy-search-api.json |
Fuzzy free-text search for "volvo" | Targets /v3/organizations/search/, which the CLI does not reach |
07-search-companies-with-geo-sphere-with-coordinates.json |
?GEO_WITHIN_SPHERE returning whole companies |
Radius is in radians (metres ÷ 6371000) |
08-simple-filtering-example-using-v3organizations.json |
Minimal exact-match filter on business_id |
Start here |
09-simple-oauth-client-credentials-example.json |
The same filter, run under OAuth client credentials | |
10-technology-search-shopify.json |
?STARTSWITH on technology_data.name |
|
11-track-modifications.json |
?RANGE over modifications.* for incremental sync |
Contains <NOW_MINUS_10_SECONDS_ISO8601> — substitute both timestamps before sending |
12-count-companies-in-vainu-list.json |
Counting every company in a saved list | Use with vainu organizations-count; list is a placeholder ID |
Set up credentials once (see Authentication), then run the smallest example:
export VAINU_API_KEY=your-api-key
vainu organizations \
--payload example_payloads/organizations_api/08-simple-filtering-example-using-v3organizations.json
Aggregations use the same command — only the payload changes:
vainu organizations \
--payload example_payloads/organizations_api/02-filter-contacts-return-only-ceos-of-companies.json
Add the global -v before the command to see request timing, and --language after it to
localise the response:
vainu -v organizations \
--language fi \
--payload example_payloads/organizations_api/10-technology-search-shopify.json
--async-mode runs the same request through the httpx client instead of requests:
vainu --async-mode organizations \
--payload example_payloads/organizations_api/07-search-companies-with-geo-sphere-with-coordinates.json
With --output the result is written to a file and the confirmation goes to stderr, so stdout
stays clean for piping:
vainu organizations \
--payload example_payloads/organizations_api/03-geospatial-business-unit-search-returning-only-matching-business_units.json \
--output uppsala_units.json
--format csv returns the raw CSV body untouched:
vainu organizations \
--payload example_payloads/organizations_api/05-get-count-of-vehicles-with-make-and-registration.json \
--format csv \
--output skoda_counts.csv
Large result sets belong in an async export job, where --output is required:
vainu organizations-async \
--payload example_payloads/organizations_api/04-get-all-companies-in-vainu-list-async-sync.json \
--format jsonl \
--poll-interval 5 \
--output list_companies.jsonl
Any payload works under OAuth client credentials instead of an API key:
export VAINU_CLIENT_ID=your-client-id
export VAINU_CLIENT_SECRET=your-client-secret
vainu --auth-method oauth organizations \
--payload example_payloads/organizations_api/09-simple-oauth-client-credentials-example.json
Signals API payloads
Signals request bodies live under
example_payloads/signals_api/.
Each file is a complete POST body — query, limit, offset at the top level.
| File | What it shows | Notes |
|---|---|---|
01-news-signals-for-one-company.json |
Every news signal for one company over the last year | Start here. Relative dates need plural units |
02-news-signals-by-tags.json |
Funding + M&A by signal type id | Values inside one ?IN are OR-ed |
03-news-signals-excluding-tags.json |
Excluding a noisy type, keeping only tagged signals | ?NOT alone also lets untagged signals through, hence ?EXISTS |
04-news-signals-keyword-monitor.json |
Keyword search over signal content in a date window | News only; matching starts at word boundaries |
05-data-changes-for-companies.json |
New financial statements and CEO changes for two companies | Data changes have no countries field and no text filtering |
06-data-changes-jsonl-paging.json |
First page of a JSON Lines export | Raise offset by limit until a page is short |
export VAINU_CLIENT_ID=your-client-id
export VAINU_CLIENT_SECRET=your-client-secret
vainu signals-news \
--payload example_payloads/signals_api/01-news-signals-for-one-company.json
--format jsonl writes one signal per line, streaming each one as it arrives (pass
--no-stream to buffer the whole page instead):
vainu signals-data-changes \
--payload example_payloads/signals_api/06-data-changes-jsonl-paging.json \
--format jsonl \
--output data_changes.jsonl
Enrichment Agent API payloads
| File | What it shows | Notes |
|---|---|---|
01-run-enrichment-agent-for-one-company.json |
Running one prompt against one company | prompt is CHANGEME — swap in the prompt id from your Vainu UI |
vainu enrichment-agent \
--payload example_payloads/enrichment_agent_api/01-run-enrichment-agent-for-one-company.json \
--prompt 12345 \
--business-id FI01320292
Python API
Async clients
| Class | Auth |
|---|---|
VainuAPIKeyClient(api_key, base_url?, language?, timeout?) |
Static API key |
VainuOAuthAPIClient(client_id, client_secret, scope?, base_url?, language?, timeout?) |
OAuth 2.0 |
Methods (all async):
| Method | Description |
|---|---|
companies(payload, format) |
Fetch company data (dict for json, raw str for csv/jsonl) |
companies_async(payload, format) |
Submit async job → AsyncResult |
organizations(payload, format) |
Fetch organization data (dict for json, raw str for csv/jsonl) |
organizations_async(payload, format) |
Submit async job → AsyncResult |
organizations_count(payload, wait?, poll_interval?, max_wait_seconds?) |
Count matching organizations (dict of count metadata). Drops order, which the endpoint rejects. wait=True re-sends the payload until status leaves scheduled/process |
organization_fields(api_versions?) |
Organization field catalog (list) |
enrichment_agent(payload, format) |
Run an enrichment agent prompt on one company (dict for json) |
signals_news(payload, format) |
Fetch news signals (list for json, raw str for jsonl) |
signals_data_changes(payload, format) |
Fetch data-change signals (list for json, raw str for jsonl) |
organization_lists(format) |
List all organization lists (list for json) |
organization_list_get(list_id, format) |
Retrieve one list summary |
organization_list_delete(list_id) |
Delete a list (static or dynamic) |
organization_list_static_create(payload, format) |
Create a static list |
organization_list_static_update(list_id, payload, format) |
Update a static list |
organization_list_static_add(list_id, business_ids) |
Add business IDs to a static list |
organization_list_static_remove(list_id, business_ids) |
Remove business IDs from a static list |
organization_list_dynamic_create(payload, format) |
Create a dynamic list |
organization_list_dynamic_update(list_id, payload, format) |
Update a dynamic list |
stream_companies(payload, format) |
Context manager → line iterator (csv/jsonl) |
stream_organizations(payload, format) |
Context manager → line iterator (csv/jsonl) |
stream_signals_news(payload, format) |
Context manager → line iterator (jsonl) |
stream_signals_data_changes(payload, format) |
Context manager → line iterator (jsonl) |
close() |
Close HTTP connection |
Sync clients
| Class | Auth |
|---|---|
VainuAPIKeySyncClient(api_key, base_url?, language?, timeout?) |
Static API key |
VainuOAuthSyncClient(client_id, client_secret, scope?, base_url?, language?, timeout?) |
OAuth 2.0 |
Same methods as the async clients but blocking (no await).
AsyncResult
result.download_url # URL of the exported file
result.duration # Job duration in seconds
await result.json() # Download and parse as dict (async)
await result.download_to_file(path) # Download via curl (async)
result.json() # Download and parse as dict (sync)
result.download_to_file(path) # Download via streaming requests (sync)
Search methods return parsed JSON only when format="json". For format="csv" and
format="jsonl", they return the raw response text so the caller can write or stream it
without JSON re-encoding. organizations_count takes no format — the endpoint renders only its JSON metadata object — and always returns a dict. The signals methods return a list under format="json" — the
Signals API responds with a bare array rather than a page object — and accept json/jsonl
only.
enrichment_agent returns the prompt's fields under response and has no stream_* variant,
since one enrichment result is a single document. An uncached run can take minutes, so pass a
higher timeout (seconds, default 121) to the client when you expect one:
client = VainuOAuthSyncClient(client_id="...", client_secret="...", timeout=600)
result = client.enrichment_agent(
payload={"prompt": "12345", "database": "FI", "business_id": "FI23365096"}
)
print(result["response"])
client.close()
List management methods (organization_lists, organization_list_static_*,
organization_list_dynamic_*) require OAuth/JWT/browser auth like signals and enrichment — not
a static API key.
import json
from vainu_cli import VainuOAuthSyncClient
client = VainuOAuthSyncClient(client_id="...", client_secret="...")
lists = client.organization_lists()
print(lists[0]["id"], lists[0]["name"])
created = client.organization_list_dynamic_create({
"name": "Big FI companies",
"country": "FI",
"query": json.dumps({"?GTE": {"financial_data.revenue": 1_000_000}}),
})
client.organization_list_delete(created["id"])
client.close()
Streaming
The stream_* methods are (async) context managers yielding an iterator of lines, so rows can
be handled while the server is still producing them and no full body is ever held in memory.
Lines arrive as str with the newline stripped and blank lines skipped; the response is
released when the with block exits, even if you stop iterating early.
import asyncio, json
from vainu_cli import VainuOAuthAPIClient, VainuOAuthSyncClient
# sync
client = VainuOAuthSyncClient(client_id="...", client_secret="...")
with client.stream_signals_news(payload={"query": {}}, format="jsonl") as lines:
for line in lines:
print(json.loads(line)["title"])
client.close()
# async
async def main():
client = VainuOAuthAPIClient(client_id="...", client_secret="...")
async with client.stream_organizations(payload={"query": {}}, format="jsonl") as lines:
async for line in lines:
print(json.loads(line)["business_id"])
await client.close()
asyncio.run(main())
format defaults to jsonl here and must be a line-oriented format — format="json" raises
ValueError, since a single JSON document only becomes valid once its last byte lands. With
csv the first line is the header row. Error bodies behave as they do on the buffered methods:
400/403/404 are yielded as the response text rather than raised, other failures raise.
Environment variables
| Variable | Description |
|---|---|
VAINU_API_KEY |
Static API key |
VAINU_CLIENT_ID |
OAuth client ID |
VAINU_CLIENT_SECRET |
OAuth client secret |
VAINU_JWT_REFRESH_TOKEN |
JWT refresh token |
VAINU_BASE_URL |
Override API base URL |
Development
git clone https://github.com/vainu-app/vainu-cli.git
cd vainu-cli
uv sync --extra dev
# Run tests
uv run pytest -v
# Lint
uv run ruff check src/ tests/
uv run ruff format src/ tests/
# Security checks
uv run bandit -c pyproject.toml -r src/
uv run pip-audit
# Build
uv build
License
MIT © 2026 Vainu
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 vainu_cli-0.1.0.tar.gz.
File metadata
- Download URL: vainu_cli-0.1.0.tar.gz
- Upload date:
- Size: 29.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
174ec46605c7a39d8b5f9ba01cd5e5007106520e831098846603951946e1e08e
|
|
| MD5 |
ba43260cd0c66fed79cfcea702c17ba2
|
|
| BLAKE2b-256 |
58a53d46f4e560408ef510358dce3b10922fc50182ac9c31b67d0ddfe61f685c
|
Provenance
The following attestation bundles were made for vainu_cli-0.1.0.tar.gz:
Publisher:
publish.yml on vainu-app/vainu-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vainu_cli-0.1.0.tar.gz -
Subject digest:
174ec46605c7a39d8b5f9ba01cd5e5007106520e831098846603951946e1e08e - Sigstore transparency entry: 2501541871
- Sigstore integration time:
-
Permalink:
vainu-app/vainu-cli@3e350dd51d05452e2e13149cfc6f8a375e6c59cd -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/vainu-app
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3e350dd51d05452e2e13149cfc6f8a375e6c59cd -
Trigger Event:
release
-
Statement type:
File details
Details for the file vainu_cli-0.1.0-py3-none-any.whl.
File metadata
- Download URL: vainu_cli-0.1.0-py3-none-any.whl
- Upload date:
- Size: 27.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec22744c804480bd80bb925971aebdb68f86157aeda045e87356927ac865616f
|
|
| MD5 |
57f1345f3deda62fd6f13139634a718b
|
|
| BLAKE2b-256 |
313e02316ebcc48e6985b7f43c03289c8783e613d0fac347aa6b26493ecda973
|
Provenance
The following attestation bundles were made for vainu_cli-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on vainu-app/vainu-cli
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vainu_cli-0.1.0-py3-none-any.whl -
Subject digest:
ec22744c804480bd80bb925971aebdb68f86157aeda045e87356927ac865616f - Sigstore transparency entry: 2501541905
- Sigstore integration time:
-
Permalink:
vainu-app/vainu-cli@3e350dd51d05452e2e13149cfc6f8a375e6c59cd -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/vainu-app
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3e350dd51d05452e2e13149cfc6f8a375e6c59cd -
Trigger Event:
release
-
Statement type: