Examples use fictional data. Every company, person, address and count in them is invented, and their domains use the reserved .example TLD.
Getting started
Quickstart
Create a workspace, mint a key, and pull your first slice of the company graph. Four minutes end to end, no sales call in the middle.
Base URL and request shape
Every endpoint lives under one host and one version prefix. Requests and responses are JSON encoded as UTF-8, timestamps are ISO-8601 in UTC, and money is always an integer number of cents. Identifiers are prefixed so a value stays self-describing wherever it lands — a log line, a spreadsheet, a webhook payload.
POST /v1/search/companies HTTP/1.1
Host: api.salvo.example
x-api-key: sk_live_9f4a2c7e10b6
content-type: application/json
accept: application/json| Prefix | Resource |
|---|---|
org_ | Organization (workspace) |
usr_ | User |
prj_ | Project |
icp_ | Ideal-customer profile |
cmp_ | Campaign · company record |
led_ | Lead |
thr_ / msg_ | Inbox thread · message |
dom_ / mbx_ | Sending domain · mailbox |
job_ / exp_ | Enrichment job · export job |
key_ / whk_ | API key · webhook |
Get an API key
Create a key in the app under Settings → API keys. The secret is shown exactly once — after that only its prefix and last-used timestamp are readable, because Salvo stores nothing but a bcrypt hash. Send it in the x-api-key header; while the site is behind a private-preview password, add those credentials too (HTTP Basic).
# The key comes from Settings → API keys (shown once).
curl "https://beamro.com/api/v1/me" -H "x-api-key: $SALVO_API_KEY"
# Behind the private-preview password, add the gate credentials:
curl -u "preview-user:preview-password" "https://beamro.com/api/v1/me" -H "x-api-key: $SALVO_API_KEY"Keys hold full workspace scope
Ansk_live_ key can read your leads and spend your wallet. Keep it server-side, rotate it from the keys endpoint, and never inline it in a browser bundle — the Salvo web app itself proxies through same-origin /api/* handlers so no token ever reaches page JavaScript.Your first request
Ask the graph a plain-language question. The semantic layer turns it into structured filters, and the response tells you exactly how it read you in interpretation so nothing is a black box.
curl -X POST "https://beamro.com/api/v1/search/companies" \
-H "x-api-key: $SALVO_API_KEY" \
-H "content-type: application/json" \
-d '{
"filters": {
"q": "independent event design studios that install florals",
"countries": [
"US"
],
"employeeMax": 50,
"limit": 3
}
}'{
"rows": [
{
"id": "cmp_3h8q1w6x0m4v9k2t7b",
"domain": "harlowmeadow.example",
"name": "Harlow Meadow Events",
"legal_name": "Harlow Meadow Events LLC",
"description": "Full-service event design studio for weddings and corporate galas.",
"ultimate_profile": "Designs and installs floral and lighting schemes for 60–90 events a year across the north-east US.",
"industry_nace": "N82.30",
"industry_labels": [
"Event management",
"Event design"
],
"employee_count": 24,
"employee_range": "11-50",
"revenue_range": "$1M-$10M",
"founded_year": 2011,
"country": "US",
"country_name": "United States",
"region": "north-america",
"city": "Providence",
"address": "100 Sample Street",
"lat": 41.824,
"lng": -71.4128,
"website": "https://harlowmeadow.example",
"linkedin_url": "https://www.linkedin.example/company/harlow-meadow-events",
"logo_url": null,
"tech_stack": [
"HubSpot",
"Squarespace",
"Stripe"
],
"socials": {
"instagram": "https://instagram.example/harlowmeadow"
},
"funding": null,
"registry": {
"number": "RG421887",
"status": "active"
},
"gmaps": {
"rating": 4.8,
"reviews": 214
},
"sources": [
"website",
"linkedin",
"gmaps"
],
"quality_score": 91,
"updated_at": "2026-08-14T09:12:44.000Z"
}
],
"total": 348,
"took_ms": 84,
"interpretation": {
"intent": "companies",
"filters": {
"segments": [
"event-management"
],
"countries": [
"US"
],
"employeeMax": 50
},
"explanation": "Matched the event-management segment, limited to US companies under 50 staff."
},
"credits_charged": 3
}Before the data catalog is loaded
Until a deployment’s catalog is loaded, search answersrows: [], total: 0 and catalog_empty: true, and charges nothing.Paging through a result set
List and search endpoints take limit and offset and return the unpaged total alongside the rows. limit defaults to 25 and is capped at 100. Past roughly ten thousand rows, stop paging and hand the same filters to an export job — it is one credit per row either way and far kinder to both sides.
const filters = { segments: ['event-management'], countries: ['US'] };
const all = [];
for (let offset = 0; ; offset += 100) {
const page = await salvo.search.companies({ ...filters, limit: 100, offset });
all.push(...page.rows);
if (all.length >= page.total || page.rows.length === 0) break;
}Client libraries
The TypeScript client is first-party and mirrors this reference one method per endpoint — if a call exists here it exists there, with the same argument names. Other languages use plain HTTP; the Python samples throughout this reference need nothing but requests.
# TypeScript / Node 22+
npm add @salvo/sdk
# Python 3.10+
pip install requestsWhere to go next
- Search — the filter grammar behind every list of companies, people and places.
- Campaigns — hand a target definition to the agents and let them write, send and follow up.
- Webhooks — get told the moment a reply is classified or a meeting is booked, instead of polling.
- Errors and rate limits — the two pages worth reading before you go to production.