Skip to content

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.

Anatomy of a request
POST /v1/search/companies HTTP/1.1
Host: api.salvo.example
x-api-key: sk_live_9f4a2c7e10b6
content-type: application/json
accept: application/json
PrefixResource
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

An sk_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
    }
  }'
200 response (truncated)
{
  "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 answers rows: [], 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.

Walk a whole segment
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.

Install
# TypeScript / Node 22+
npm add @salvo/sdk

# Python 3.10+
pip install requests

Where 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.