Companies, visits, segments, goals and period summaries as JSON.
Signed real-time events for your CRM, Zapier, Make or n8n.
Alerts in your channels, with a button to the company.
Let AI assistants answer questions about your visitors.
Quickstart
- In lead.box, open Integrations → API and create a key.
- Make your first request. Every response is JSON and only contains data of your workspace.
- Add a webhook, Slack or Teams in the same place to receive events as they happen.
curl -H "Authorization: Bearer $LEADBOX_API_KEY" \ "https://api.lead.box/v1/companies?since=2026-10-01T00:00:00Z&country=DE&exclude_list_id=<customers-list-id>&limit=50"
{
"data": [
{ "object": "company", "id": "…", "name": "Example GmbH", "domain": "example.com", "country": "DE",
"industry": "Software", "pages_viewed": 7, "sessions": 2, "last_seen_at": "2026-10-24T09:12:00Z", … }
],
"next_cursor": "MjAyNi0xMC0yNFQwOToxMjowMFp8…"
}Authentication
Send your API key as a bearer token: Authorization: Bearer lbx_…. A key belongs to one workspace and can only read. Keep it on your server and never ship it to a browser or mobile app.
- read:companiesAllowsCompanies, segments, tags and lists.
- read:visitsAllowsVisits, sessions, summaries, returning companies and goals.
- read:contactsAllowsPeople you saved or revealed yourself in lead.box. Off by default.
| Scope | Allows |
|---|---|
| read:companies | Companies, segments, tags and lists. |
| read:visits | Visits, sessions, summaries, returning companies and goals. |
| read:contacts | People you saved or revealed yourself in lead.box. Off by default. |
AI assistants can also connect without a key through OAuth 2.1, see MCP server.
REST API
Base URL: https://api.lead.box/v1. All endpoints are GET. Timestamps are ISO 8601 in UTC; empty values are null; the API is versioned in the path and only changes in a backwards-compatible way within v1.
- GET /companiesScoperead:companiesReturnsIdentified companies, most recent activity first. Filters: since, q, country, industry, city, region, min_score, hot, min_pageviews, first_seen_since, tag_id, list_id, exclude_list_id, segment, site. Cursor paging; expand=visits,contacts.
- GET /companies/{id}Scoperead:companiesReturnsOne company with tags, lists, score reasons and notes; expand=visits,contacts.
- GET /companies/returningScoperead:visitsReturnsCompanies back on the website after a quiet period, with the previous visit and days away (days, gap_days, limit).
- GET /visitsScoperead:visitsReturnsPage views of identified companies (since, company_id, cursor, limit); group=sessions groups them into sessions.
- GET /summaryScoperead:visitsReturnsOverview of a period (days or since, max 31 days): page views, sessions, active, new, hot and returning companies, top companies, pages, industries and countries.
- GET /segmentsScoperead:companiesReturnsSaved segments; api_filterable tells whether a segment can be used as a filter, api_unsupported names the reason if not.
- GET /tagsScoperead:companiesReturnsTags of the workspace (ids for tag_id).
- GET /listsScoperead:companiesReturnsAccount lists of the workspace (ids for list_id and exclude_list_id).
- GET /goalsScoperead:visitsReturnsConversion goals of the workspace.
- GET /goals/conversionsScoperead:visitsReturnsGoal conversions of identified companies, newest first (days, goal_id, limit).
- GET /sitesReturnsThe tracked website of the workspace.
- GET /meReturnsWorkspace, key name, scopes and limits of the calling key.
| Endpoint | Scope | Returns |
|---|---|---|
| GET /companies | read:companies | Identified companies, most recent activity first. Filters: since, q, country, industry, city, region, min_score, hot, min_pageviews, first_seen_since, tag_id, list_id, exclude_list_id, segment, site. Cursor paging; expand=visits,contacts. |
| GET /companies/{id} | read:companies | One company with tags, lists, score reasons and notes; expand=visits,contacts. |
| GET /companies/returning | read:visits | Companies back on the website after a quiet period, with the previous visit and days away (days, gap_days, limit). |
| GET /visits | read:visits | Page views of identified companies (since, company_id, cursor, limit); group=sessions groups them into sessions. |
| GET /summary | read:visits | Overview of a period (days or since, max 31 days): page views, sessions, active, new, hot and returning companies, top companies, pages, industries and countries. |
| GET /segments | read:companies | Saved segments; api_filterable tells whether a segment can be used as a filter, api_unsupported names the reason if not. |
| GET /tags | read:companies | Tags of the workspace (ids for tag_id). |
| GET /lists | read:companies | Account lists of the workspace (ids for list_id and exclude_list_id). |
| GET /goals | read:visits | Conversion goals of the workspace. |
| GET /goals/conversions | read:visits | Goal conversions of identified companies, newest first (days, goal_id, limit). |
| GET /sites | The tracked website of the workspace. | |
| GET /me | Workspace, key name, scopes and limits of the calling key. |
Saved segments work as a filter (segment=<id>) with the same meaning as in the app: countries and regions, industries, company size, tags, account lists, visited URL, visit counts and dates.
{
"object": "company",
"id": "…", "name": "Example GmbH", "domain": "example.com", "website": "https://example.com",
"logo_url": "https://…", "description": "…", "industry": "Software",
"size": "51-200", "employee_range": "51-200", "revenue_range": "10M-50M", "founded_year": 2012,
"address": { "postal_code": "10115", "city": "Berlin", "region": "Berlin", "country": "DE", "continent": "EU" },
"country": "DE", "city": "Berlin", "phone": "+49 30 000000",
"linkedin_url": "https://www.linkedin.com/company/example",
"social": { "linkedin": "https://www.linkedin.com/company/example", "crunchbase": null },
"total_funding": null, "keywords": ["saas"], "tech_stack": ["HubSpot", "WordPress"],
"score": 72, "is_hot": true, "icp_tier": "A", "lifecycle_stage": "mql",
"score_reasons": { "tier": "hot", "factors": { … }, "summary": "…", "recommended_action": "…", "computed_at": "…" },
"tags": [{ "id": "…", "name": "Prospect", "color": "#…", "lifecycle_stage": null }],
"lists": [{ "id": "…", "name": "Q4 Targets" }],
"notes": [{ "id": "…", "content": "…", "created_at": "…", "updated_at": null }], // detail endpoint only
"pages_viewed": 7, "sessions": 2, "first_seen_at": "…", "last_seen_at": "…",
"last_page_url": "https://your-site.com/pricing", "last_referrer": "https://www.google.com/",
"site": { "id": "…", "name": "Your site", "domain": "your-site.com" },
"visits": [ … ], // with expand=visits (last 90 days)
"contacts": [ … ] // with expand=contacts (needs read:contacts)
}{
"object": "visit", "id": "…", "company_id": "…", "session_id": "…",
"page_url": "https://your-site.com/pricing", "page_title": "Pricing",
"referrer": "https://www.google.com/", "duration_seconds": 35, "visited_at": "…",
"device_type": "desktop", "browser": "Chrome", "os": "macOS", "country": "DE", "city": "Berlin",
"utm": { "source": "linkedin", "medium": "cpc", "campaign": "q4", "term": null, "content": null }
}
// group=sessions → { "object": "session", "session_id", "company_id", "started_at", "ended_at",
// "duration_seconds", "referrer", "utm", "device_type", "country", "pages": [{ "url", "title", "visited_at" }] }Pagination
List endpoints return up to limit items (1 to 100, default 50) and a next_cursor. Pass it as cursor to get the next page; it is null on the last page. Results are ordered by last activity, newest first.
let cursor = null;
do {
const url = new URL('https://api.lead.box/v1/companies');
url.searchParams.set('limit', '100');
if (cursor) url.searchParams.set('cursor', cursor);
const page = await fetch(url, { headers: { Authorization: `Bearer ${KEY}` } }).then((r) => r.json());
handle(page.data);
cursor = page.next_cursor;
} while (cursor);Errors
Errors use the HTTP status code and a JSON body with a stable machine-readable code and a human-readable message.
HTTP/1.1 400 Bad Request
{ "error": { "code": "invalid_filter", "message": "min_score must be 0-100" } }- 400Codeinvalid_filter, invalid_since, invalid_period, invalid_cursor, invalid_company_idMeaningA parameter is missing or malformed. The message names it.
- 401CodeunauthorizedMeaningNo key, an unknown key or a revoked key.
- 403Codeinsufficient_scope, key_disabled, api_access_disabledMeaningThe key lacks a scope, or the key or the API access of the workspace was disabled by support.
- 404Codenot_found, segment_not_foundMeaningThe company or segment does not exist in this workspace.
- 422Codesegment_unsupportedMeaningThe saved segment uses OR groups or visitor-level fields that cannot be used as an API filter.
- 429Coderate_limited, too_many_auth_failuresMeaningA limit was reached. Wait for Retry-After seconds.
- 504Codequery_timeoutMeaningThe query took longer than 5 seconds. Narrow the filters or lower the limit.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_filter, invalid_since, invalid_period, invalid_cursor, invalid_company_id | A parameter is missing or malformed. The message names it. |
| 401 | unauthorized | No key, an unknown key or a revoked key. |
| 403 | insufficient_scope, key_disabled, api_access_disabled | The key lacks a scope, or the key or the API access of the workspace was disabled by support. |
| 404 | not_found, segment_not_found | The company or segment does not exist in this workspace. |
| 422 | segment_unsupported | The saved segment uses OR groups or visitor-level fields that cannot be used as an API filter. |
| 429 | rate_limited, too_many_auth_failures | A limit was reached. Wait for Retry-After seconds. |
| 504 | query_timeout | The query took longer than 5 seconds. Narrow the filters or lower the limit. |
Webhooks
lead.box sends a signed JSON POST (version v1) to your HTTPS endpoint. Each delivery carries the full company object and, where it applies, the visit or goal that triggered it. Contacts are never sent by webhook. Use the delivery id to ignore duplicates.
- company.identifiedWhen it is sentA new company is identified on your website.
- company.scored_hotWhen it is sentA company is classified as hot.
- company.returnedWhen it is sentA known company is back after at least 7 days without a visit (at most once per company and day).
- goal.convertedWhen it is sentAn identified company reaches one of your goals (at most once per goal, company and day); includes the goal.
- visit.newWhen it is sentA visit of an identified company is recorded.
- test.pingWhen it is sentSent by the "Send test" button in the settings.
| Event | When it is sent |
|---|---|
| company.identified | A new company is identified on your website. |
| company.scored_hot | A company is classified as hot. |
| company.returned | A known company is back after at least 7 days without a visit (at most once per company and day). |
| goal.converted | An identified company reaches one of your goals (at most once per goal, company and day); includes the goal. |
| visit.new | A visit of an identified company is recorded. |
| test.ping | Sent by the "Send test" button in the settings. |
{
"version": "v1",
"id": "8f0c…", // delivery id, use for de-duplication
"event": "company.identified", // | company.scored_hot | company.returned | goal.converted | visit.new | test.ping
"created_at": "2026-10-18T09:12:00Z",
"workspace_id": "…",
"data": {
"company": { …full company object as above, without notes/contacts… },
"visit": { …visit object… }, // for visit.new, company.returned and goal.converted
"goal": { "id": "…", "name": "Pricing page visited" }, // only for goal.converted
"converted_at": "2026-10-18T09:11:40Z" // only for goal.converted
}
}Every request carries X-LeadBox-Signature: t=<unix>,v1=<hex>, an HMAC-SHA256 over <t>.<raw body> with your signing secret. Reject timestamps older than five minutes. Answer with a 2xx status within 10 seconds; failed deliveries are retried after 1 min, 5 min, 30 min, 2 h and 12 h, and a webhook that keeps failing is paused.
import crypto from 'node:crypto';
// rawBody = the exact request body string, header = X-LeadBox-Signature
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map(p => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto.createHmac('sha256', secret)
.update(`${parts.t}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Optional filters per webhook: only companies from selected countries, or leave out companies on an account list (for example existing customers).
Slack & Microsoft Teams
Paste an incoming webhook address from Slack, or a Teams workflow webhook address, under Integrations and pick the events. lead.box posts a short message in the language of the workspace owner: the company, industry, size and location, the page it looked at, and a button that opens the company in lead.box. Country and list filters apply here too. No code needed.
MCP server
Connect AI assistants that support remote MCP servers (Streamable HTTP) to https://api.lead.box/mcp. All tools are read-only and only see one workspace.
- get_activity_summaryWhat it doesOverview of a period: page views, sessions, active, new, hot and returning companies plus top lists. A good first call for "How was the week?".
- search_companiesWhat it doesSearch companies with the same filters as the REST API, including saved segments, tags and lists.
- get_companyWhat it doesFull profile of one company, optionally with recent sessions and contacts.
- get_company_timelineWhat it doesChronological sessions and pages of one company.
- list_recent_visitsWhat it doesRecent page views of identified companies, grouped into sessions.
- list_hot_companiesWhat it doesCompanies currently flagged hot.
- list_returning_companiesWhat it doesCompanies that came back after a quiet period.
- list_goal_conversionsWhat it doesGoals and recent conversions of identified companies.
- list_segmentsWhat it doesSaved segments (ids for search_companies).
- list_tags_and_listsWhat it doesTags and account lists (ids for search_companies).
| Tool | What it does |
|---|---|
| get_activity_summary | Overview of a period: page views, sessions, active, new, hot and returning companies plus top lists. A good first call for "How was the week?". |
| search_companies | Search companies with the same filters as the REST API, including saved segments, tags and lists. |
| get_company | Full profile of one company, optionally with recent sessions and contacts. |
| get_company_timeline | Chronological sessions and pages of one company. |
| list_recent_visits | Recent page views of identified companies, grouped into sessions. |
| list_hot_companies | Companies currently flagged hot. |
| list_returning_companies | Companies that came back after a quiet period. |
| list_goal_conversions | Goals and recent conversions of identified companies. |
| list_segments | Saved segments (ids for search_companies). |
| list_tags_and_lists | Tags and account lists (ids for search_companies). |
Without a key: add the MCP address as a custom connector in your assistant. You sign in with lead.box, choose the workspace and allow read access (OAuth 2.1). You can disconnect apps any time in lead.box under Integrations → Connected apps.
With a key:
claude mcp add --transport http leadbox https://api.lead.box/mcp \ --header "Authorization: Bearer $LEADBOX_API_KEY"
Questions to try
- How was our website traffic this week? Which companies were most active?
- Which companies came back after more than two weeks, and what did they look at?
- Show German software companies that visited our pricing page in the last 30 days.
- Which companies reached the goal "Pricing page visited" yesterday?
- Summarise the visit history of example.com before my call.
Limits
- Requests per key / OAuth appValue60 / minute · 10,000 / day (UTC)
- Requests per workspace (all keys + apps)Value120 / minute · 20,000 / day (UTC)
- Page size (limit)Value1–100, default 50 · cursor paging only
- expand=visitsValuemax 50 visits per company, last 90 days
- Query timeValuemax 5 s per query, else 504
- MCP resultValuemax 50 rows · 50,000 characters ("more_available" hint) · max 10 calls per batch
- Invalid keys per IPValue20 within 10 minutes → blocked for 15 minutes
- Webhook sizeValuemax 256 KB (otherwise slimmed, truncated: true)
- Webhook deliveryValue10 s timeout · max 2 in parallel per workspace
| Limit | Value |
|---|---|
| Requests per key / OAuth app | 60 / minute · 10,000 / day (UTC) |
| Requests per workspace (all keys + apps) | 120 / minute · 20,000 / day (UTC) |
| Page size (limit) | 1–100, default 50 · cursor paging only |
| expand=visits | max 50 visits per company, last 90 days |
| Query time | max 5 s per query, else 504 |
| MCP result | max 50 rows · 50,000 characters ("more_available" hint) · max 10 calls per batch |
| Invalid keys per IP | 20 within 10 minutes → blocked for 15 minutes |
| Webhook size | max 256 KB (otherwise slimmed, truncated: true) |
| Webhook delivery | 10 s timeout · max 2 in parallel per workspace |
Exceeding a limit returns HTTP 429 with Retry-After and X-RateLimit-* headers.
Data & privacy
- Company-level data only: the API, webhooks and MCP never return visitor IP addresses or visitor IDs.
- Contacts are only people you saved or revealed yourself in lead.box, and only with the read:contacts scope.
- URLs are returned without query strings.
- Every key, app and webhook is bound to one workspace; another workspace's data is never reachable.
Changelog
2026-10-24
- New REST endpoints: /summary, /companies/returning, /goals, /goals/conversions, /tags, /lists.
- New company filters: city, region, min_pageviews, first_seen_since, tag_id, list_id, exclude_list_id.
- Saved segments now work as API filters with the same meaning as in the app.
- New webhook events company.returned and goal.converted; per-webhook filters by country and account list.
- Slack and Microsoft Teams messages; managers can manage webhooks.
- MCP server: four new tools (activity summary, returning companies, goal conversions, tags and lists).
2026-10-18
- REST API v1 at api.lead.box, signed webhooks and the remote MCP server.