CLI and Python client for the Business Validation API.
pip install voygrRequires Python 3.10+.
voygr signup you@example.com --name "Your Name"{"success": true, "message": "API key sent to your email"}voygr login pk_live_abc123{"success": true, "message": "API key saved"}voygr check "Starbucks" "1390 Market St, San Francisco, CA 94102"{"success":true,"existence_status":"exists","open_closed_status":"open","request_id":"abc123","validation_timestamp":"2026-03-13T12:00:00Z"}Default output is compact JSON. Add --human for a readable format:
voygr --human check "Starbucks" "1390 Market St, San Francisco, CA 94102"existence_status: exists
open_closed_status: open
request_id: abc123
validation_timestamp: 2026-03-13T12:00:00Z
If you lose access to your API key, request a recovery link:
voygr recover you@example.comA link is sent to the email if an account exists. Clicking it rotates your key — the existing one stops working immediately, and a new one is sent to the same address. Remaining quota and plan tier carry over.
See voygr recover for full details.
Request an API key. The key is sent to the provided email address.
email(required) -- email address to receive the key--name-- your name; defaults to the email if omitted
voygr signup jane@example.com --name "Jane Smith"{"success": true, "message": "API key sent to your email"}No authentication required. If the email already has an account, the response directs you to voygr recover instead of resending the key.
Request a recovery link for a forgotten or lost API key. The link is sent to the provided email if an account exists.
voygr recover jane@example.com{"success": true, "message": "If an account exists for that email, a recovery link has been sent."}Clicking the link rotates your API key — the existing key stops working at that moment, and the new one is sent to the same email. Remaining quota and plan tier carry over to the new key.
The response is uniform whether or not the email is registered, so it cannot be used to enumerate accounts. Rate-limited per email and per source IP.
Store your API key locally at ~/.config/voygr/config.json.
voygr login pk_live_abc123{"success": true, "message": "API key saved"}Remove the stored API key.
voygr logout{"success": true, "message": "API key removed"}Check whether a business exists at the given address and whether it's open or closed. Requires authentication.
name(required) -- business nameaddress(required) -- full street address
voygr check "Blue Bottle Coffee" "66 Mint St, San Francisco, CA 94103"{"success":true,"existence_status":"exists","open_closed_status":"open","request_id":"req_7f2a","validation_timestamp":"2026-04-03T15:30:00Z"}existence_status values: exists, not_exists, uncertain
open_closed_status values: open, closed, uncertain
Batch mode. Reads a CSV file with name and address columns and checks each row. Output is JSONL (one JSON object per line).
Exits with code 1 if any check in the batch fails.
See the Batch Mode section for details.
Check your remaining validation quota. Requires authentication.
voygr usage{"quota_limit":100,"current_usage":12,"remaining":88,"percentage_used":12.0,"period":"lifetime","status":"active"}Print shell completion setup instructions for the given shell. See Shell Completions.
Use --file to check multiple businesses from a CSV file:
voygr check --file businesses.csvThe CSV must have a header row with name and address columns:
name,address
Starbucks,"1390 Market St, San Francisco, CA 94102"
Blue Bottle Coffee,"66 Mint St, San Francisco, CA 94103"Output is JSONL -- one JSON object per line, making it easy to pipe into jq or other tools:
{"success":true,"existence_status":"exists","open_closed_status":"open","request_id":"req_a1","validation_timestamp":"2026-04-03T15:30:00Z"}
{"success":true,"existence_status":"exists","open_closed_status":"open","request_id":"req_a2","validation_timestamp":"2026-04-03T15:30:01Z"}If a row fails, the error record includes input_name and input_address so you can trace it back:
{"success":false,"error":"RATE_LIMIT_ERROR","message":"Too many requests","input_name":"Starbucks","input_address":"1390 Market St, San Francisco, CA 94102"}The command exits with code 1 if any check in the batch fails, even if others succeeded.
These flags apply to every command:
| Flag | Description |
|---|---|
--api-key KEY |
API key for this request. Overrides env var and config file. |
--base-url URL |
API base URL. Defaults to https://dev.voygr.tech. |
--human |
Human-readable output. Default is JSON. |
--debug |
Print HTTP request details to stderr. |
--version |
Show version and exit. |
voygr --api-key pk_live_abc123 --human check "Whole Foods" "399 4th St, SF, CA"| Variable | Description |
|---|---|
VOYGR_API_KEY |
API key. Used when no --api-key flag is passed. |
VOYGR_BASE_URL |
Base URL. Used when no --base-url flag is passed. |
export VOYGR_API_KEY=pk_live_abc123
voygr check "Whole Foods" "399 4th St, San Francisco, CA 94107"The API key is resolved in this order:
--api-keyflagVOYGR_API_KEYenvironment variable- Config file at
~/.config/voygr/config.json
If none of these are set, commands that require authentication (check, usage) will exit with an error.
The CLI automatically retries requests that fail with 429 (rate limit) or 5xx (server error) responses. Retries use exponential backoff, up to 3 attempts.
No configuration needed for CLI usage -- retries are always on.
For library users, pass retries to the constructor:
client = Client(api_key="pk_live_abc123", retries=3)Set retries=0 to disable (the default for the library).
Generate completion setup instructions for your shell:
# Bash
voygr completions bash
# Zsh
voygr completions zsh
# Fish
voygr completions fishEach command prints the snippet you need to add to your shell config (.bashrc, .zshrc, or ~/.config/fish/config.fish). Follow the printed instructions to enable tab completion for all commands and flags.
| Code | Meaning |
|---|---|
0 |
Success |
1 |
API error (bad request, auth failure, quota exceeded, network error) |
2 |
User error (missing argument, invalid flag) |
All errors are written to stderr as JSON:
{"error": "AUTHENTICATION_ERROR", "message": "AUTHENTICATION_ERROR: Invalid or revoked API key"}Common error codes:
| Error Code | Meaning |
|---|---|
VALIDATION_ERROR |
Invalid request (missing fields, bad format) |
AUTHENTICATION_ERROR |
Missing or invalid API key |
QUOTA_EXCEEDED |
No validations remaining |
RATE_LIMIT_ERROR |
Too many requests (retry after a moment) |
from voygr import Client
client = Client(api_key="pk_live_abc123")
# Check a business
result = client.check(
name="Starbucks",
address="1390 Market St, San Francisco, CA 94102",
)
print(result["existence_status"]) # "exists"
print(result["open_closed_status"]) # "open"
# Check remaining quota
usage = client.usage()
print(f"{usage['remaining']} validations left")
# Sign up (no API key needed)
client = Client()
client.signup(email="you@example.com", name="Your Name")The Client constructor accepts:
api_key-- your API key (required forcheckandusage)base_url-- defaults tohttps://dev.voygr.techretries-- number of retries on 429/5xx errors (default0)
All methods return a dict parsed from the JSON response. On HTTP errors, they raise APIError with status_code, error_code, and a message.
from voygr import Client, APIError
client = Client(api_key="pk_live_abc123")
try:
result = client.check(name="Test", address="123 Main St")
except APIError as e:
print(e.status_code) # 402
print(e.error_code) # "QUOTA_EXCEEDED"Base URL: https://dev.voygr.tech
Get an API key sent to your email. No authentication required.
Request:
{"name": "Jane Smith", "email": "jane@example.com"}Response (200):
{"success": true, "message": "API key sent to your email"}Check business existence and open/closed status. Requires X-API-Key header.
Request:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes | Business name |
address |
string | yes | Full street address |
{"name": "Blue Bottle Coffee", "address": "66 Mint St, San Francisco, CA 94103"}Response (200):
{
"success": true,
"existence_status": "exists",
"open_closed_status": "open",
"request_id": "abc123",
"validation_timestamp": "2026-03-13T12:00:00Z"
}| Field | Type | Values |
|---|---|---|
existence_status |
string | exists, not_exists, uncertain |
open_closed_status |
string | open, closed, uncertain |
request_id |
string | Unique request identifier |
validation_timestamp |
string | ISO 8601 UTC timestamp |
Check remaining validation quota. Requires X-API-Key header.
Response (200):
{
"quota_limit": 100,
"current_usage": 12,
"remaining": 88,
"percentage_used": 12.0,
"period": "lifetime",
"status": "active"
}All endpoints return errors in a consistent format:
{
"success": false,
"error": "Description of the error",
"error_code": "ERROR_TYPE",
"request_id": "abc123"
}| HTTP Status | Error Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR |
Invalid request body |
| 401 | AUTHENTICATION_ERROR |
Missing API key |
| 402 | QUOTA_EXCEEDED |
No validations remaining |
| 403 | AUTHENTICATION_ERROR |
Invalid or revoked API key |
| 429 | RATE_LIMIT_ERROR |
Too many requests |
MIT