# BeforePress API — complete documentation BeforePress delivers live B2B GTM signals (company funding raises, executive changes, hiring soon) detected from public records minutes after they surface — typically weeks before press coverage, and for most seed rounds the press never covers them at all. The sourcing method is proprietary; every signal carries a public receipt page so the fact itself is independently verifiable. - Base URL: `https://beforepress.com` - Format: JSON over HTTPS. Every response includes `request_id`. - Rate limits: 300 requests/minute per IP, 120 requests/minute per API key. Fair use on history: pulling more than 50,000 unique signals in a day returns 429 `fair_use_exceeded` (resets midnight UTC) — unlimited covers the live stream and normal history queries, not full-database exports. Need a bulk export? Email support@beforepress.com and it can be arranged properly. - Plans: founding $139/mo (first 50 customers, locked for life) and standard $349/mo. Both unlimited: usage is recorded, never capped. No free tier. First month refundable. - Signup: https://beforepress.com/signup (self-serve checkout; API key issued immediately after payment, shown exactly once). ## Authentication Pass your key on every request: ``` Authorization: Bearer sk_live_... ``` Errors use a stable envelope: ```json { "error": { "code": "unauthorized", "message": "...", "details": null }, "request_id": "..." } ``` Codes: `unauthorized` (401), `payment_required` (403, checkout not completed), `account_blocked` (403), `invalid_cursor` (400), `not_found` (404), `rate_limited` (429), `internal_error` (500). ## GET /v1/signals List signals, newest first (by `published_at`). Keyset pagination. Query parameters: | param | type | notes | |---|---|---| | `type` | string | `funding` or `exec_change` (`hiring` when it ships) | | `category` | string | funding only: `company_round`, `fund_close`, `crowdfunding` | | `occurred_after` | ISO 8601 | event-date floor | | `published_after` | ISO 8601 | everything published since your last poll — use this for incremental sync | | `has_domain` | `true` | only companies with a resolved website | | `q` | string | company name contains (case-insensitive) | | `min_amount` | int | only funding signals with amount_raised >= this many USD | | `occurred_before` | ISO 8601 | event-date ceiling (pair with occurred_after for windows) | | `format` | `csv` | returns the page as CSV (same filters/metering; next cursor in the `x-next-cursor` header) | | `limit` | int | 1–100, default 25 | | `cursor` | string | `next_cursor` from the previous page | Example: ```bash curl "https://beforepress.com/v1/signals?type=funding&category=company_round&limit=25" \ -H "Authorization: Bearer sk_live_..." ``` Response: ```json { "data": [ { "signal_id": "c380b187-cdae-4918-a81c-39f5cc10ae66", "signal_type": "funding", "status": "active", "occurred_at": "2026-08-04T14:32:07.000Z", "discovered_at": "2026-08-04T14:41:12.000Z", "published_at": "2026-08-04T14:41:12.000Z", "company": { "name": "Acme Robotics", "domain": "acmerobotics.com", "country": "US" }, "payload": { "category": "company_round", "amount_raised": { "amount_usd": 4400000, "amount_original": 4400000, "currency": "USD" }, "total_offering": { "amount_usd": 6000000, "amount_original": 6000000, "currency": "USD" }, "amount_undisclosed": false, "investor_count": 3, "related_persons": [{ "name": "Jane Doe", "roles": ["Executive Officer"] }] }, "receipt_url": "https://beforepress.com/s/c380b187-cdae-4918-a81c-39f5cc10ae66", "confidence": 1.0, "version": 1 } ], "next_cursor": "MjAyNi0wOC0wNFQxNDo0MToxMi4wMDBafGMzODBiMTg3...", "request_id": "..." } ``` Payload notes: - `funding` payloads: `category` distinguishes real company rounds from VC funds closing their own funds (`fund_close`) and `crowdfunding`. `amount_raised` is money actually raised; `total_offering` is the ceiling of the raise. `amount_undisclosed: true` means the record shows a raise but no amount — still fresh money. - `exec_change` payloads: `{ "change": "appointed" | "departed", "name": "...", "role": "...", "effective_date": "YYYY-MM-DD" | null }`. `occurred_at` can be in the FUTURE: companies announce appointments in advance, and catching those early is the point. - `receipt_url` is a public BeforePress verification page — paste it in cold email so prospects can verify the fact. It never reveals sourcing. - `status` becomes `retracted` if we determine a signal was materially wrong; retracted signals disappear from default listings. Incremental sync pattern (recommended): store the max `published_at` you have seen, then poll `GET /v1/signals?published_after=` every 60 seconds. Cheap, exact, no cursor bookkeeping. ### Filtering recipes (copy-paste) Only startup funding rounds (the flagship signal — excludes VC funds closing their own funds): ``` GET /v1/signals?type=funding&category=company_round ``` Only executive changes (new CEOs, CFOs, VPs): ``` GET /v1/signals?type=exec_change ``` Only crowdfunding raises (these usually include the company website): ``` GET /v1/signals?type=funding&category=crowdfunding ``` Only companies with a resolved website (most actionable for outbound tools): ``` GET /v1/signals?type=funding&category=company_round&has_domain=true ``` Historical research — everything that occurred in a date window, paged: ``` GET /v1/signals?type=funding&category=company_round&occurred_after=2026-01-01T00:00:00Z&limit=100 # then follow next_cursor until it returns null (stay under the fair-use ceiling; see Rate limits) ``` Live incremental sync (recommended production pattern): store the max `published_at` you've seen and poll once a minute: ``` GET /v1/signals?published_after= ``` ## GET /v1/signals/:id Single signal, same shape, object in `data`. ## GET /v1/usage ```json { "data": { "plan": "founding", "billing_period": "2026-08", "unique_signals_delivered": 1240, "requests_last_30_days": 96 }, "request_id": "..." } ``` Unlimited plans: `unique_signals_delivered` is informational, never a cap. ## Webhooks Push delivery of `signal.created` events within ~30 seconds of publication. Register (response includes your signing `secret` EXACTLY ONCE — store it): ```bash curl -X POST https://beforepress.com/v1/webhooks \ -H "Authorization: Bearer sk_live_..." \ -H "content-type: application/json" \ -d '{"url":"https://your-app.com/hooks/beforepress","signal_types":["funding","exec_change"]}' ``` Rules: HTTPS only, publicly resolvable hosts only (private/internal addresses rejected), max 10 active endpoints. `GET /v1/webhooks` lists (secrets never shown again), `DELETE /v1/webhooks/:id` disables. `POST /v1/webhooks/:id/test` fires a synthetic `signal.test` event (clearly marked, `payload.test: true`) so you can verify signature handling before the first real signal. Delivery format — headers: ``` BeforePress-Signature: t=1754330000,v1= Idempotency-Key: evt_... (stable across retries — dedupe on it) Content-Type: application/json ``` Body: ```json { "event_id": "evt_...", "event_type": "signal.created", "created_at": "...", "data": { ...same Signal object as the REST API... } } ``` Verify the signature (Node.js): ```js import { createHmac, timingSafeEqual } from "node:crypto"; function verify(secret, header, rawBody) { const t = /t=(\d+)/.exec(header)?.[1]; const v1 = /v1=([0-9a-f]+)/.exec(header)?.[1]; if (!t || !v1) return false; if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // 5 min tolerance const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex"); return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected)); } ``` Python: ```python import hmac, hashlib, time, re def verify(secret: str, header: str, raw_body: bytes) -> bool: t = re.search(r"t=(\d+)", header) v1 = re.search(r"v1=([0-9a-f]+)", header) if not t or not v1: return False if abs(time.time() - int(t.group(1))) > 300: return False expected = hmac.new(secret.encode(), f"{t.group(1)}.".encode() + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(v1.group(1), expected) ``` Respond 2xx within 10 seconds (do the work async). Failures retry at 1m, 5m, 30m, 2h, 12h; an endpoint failing 20 deliveries in a row is auto-disabled. ## Receipts (`receipt_url`) Every signal links to `https://beforepress.com/s/` — a public verification page showing the company, event, amount, and dates, with a cryptographic fingerprint committing BeforePress to its archived evidence. Use it in outreach ("saw the record on the 14th — here's the receipt"). It never exposes sourcing. ## Typical uses - Cold outbound triggers: poll `published_after`, filter `category=company_round`, push into Clay/Smartlead/instantly via webhook or Zapier. - Recruiting: funded companies hire within weeks; `exec_change` appointments signal team rebuilds. - Deal sourcing: `amount_undisclosed` and small `company_round` rows are raises that will never be announced anywhere else. ## Support support@beforepress.com — founder-run; the founder answers.