Developer API
Machine access for enterprise customers. An API key belongs to a workspace, not a person, and the same confidentiality rules the website enforces apply to it.
The rule to design around
Confidential data is returned only for listings your workspace holds live data-room access to. For everything else `confidential` is null and `confidentialWithheldReason` says why. The seller's own range/hidden choice also applies — `metrics.disclosure` tells you which mode a listing is in.
Authentication
Create a key from your workspace page. It is shown once — we store only a SHA-256 hash and have no way to retrieve it. If you lose it, revoke and issue a new one.
curl https://exithunt.app/api/v1/opportunities?limit=50 \
-H "Authorization: Bearer eh_live_..."Scopes: opportunities:read, deals:read, webhooks:manage
GET /api/v1/opportunities
Active opportunities, cursor-paginated. Parameters: `limit` (max 200), `cursor`, `businessType`. The response carries `data` and `nextCursor`.
Webhooks
Events: LISTING_PUBLISHED, LISTING_UPDATED, OFFER_RECEIVED, DEAL_STAGE_CHANGED. Every delivery is signed — verify before you process it.
import { createHmac, timingSafeEqual } from "node:crypto";
// Headers: x-exithunt-event, x-exithunt-timestamp, x-exithunt-signature
function verify(secret, rawBody, timestamp, signature) {
const expected = createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signature);
return a.length === b.length && timingSafeEqual(a, b);
}There is no retry queue. If your endpoint doesn't answer, the delivery is recorded FAILED with its response code and we do not try again — catch up by polling the API. We say so plainly because designing against an at-least-once guarantee that doesn't exist is worse than having no webhooks at all.
For enterprise access, see pricing.