Vaarta API Guides
Practical notes for integrating the Competitive Signals API. For the full endpoint reference, see the API Reference.
Authentication
Create a key in the dashboard under Settings → API Keys. The raw key is shown once — copy it immediately; only its hash is stored. Send it as a Bearer token on every request:
Authorization: Bearer vt_live_xxxxxxxx…
A 401 means the key is missing, malformed, or revoked — the response never distinguishes which, to prevent key enumeration. Rotation: a workspace may hold up to 5 active keys. To rotate, generate a new key, deploy it, then revoke the old one. Revocation is immediate and permanent (soft-delete); revoked keys keep returning 401.
Rate limits
| Plan tier | Requests / minute | /v1/snapshot per hour |
|---|---|---|
| Free | 60 | — (starter+ only) |
| Starter | 60 | 3 |
| Pro | 60 | 3 |
| Team | 60 | 3 |
The per-minute limit is enforced per key (default 60 RPM across all tiers). Exceeding it returns 429 with a Retry-After header (seconds until the window frees up). /v1/snapshot has a separate hard cap of 3 accepted calls per rolling hour because each triggers a real collection run. The limiter fails closed: if the rate-limit check itself errors, the request is rejected with 429 rather than passed through.
Webhook signature verification
Every webhook POST carries three headers:
X-Vaarta-Signature—sha256=followed by the HMAC-SHA256 of the raw request body, hex-encoded.X-Vaarta-Timestamp— Unix seconds when the delivery was signed.X-Vaarta-Delivery— a per-delivery UUID, useful for idempotency.
Verify each delivery against your subscription secret (shown once on creation). The body is canonical JSON (object keys sorted) — HMAC the exact raw bytes you receive, do not re-serialize:
expected = "sha256=" + hex( HMAC_SHA256(secret, raw_request_body) )
# 1. constant-time compare against the header
if not constant_time_equals(expected, header["X-Vaarta-Signature"]):
reject # 401 — bad signature
# 2. reject stale deliveries (replay protection)
if abs(now_unix_seconds - header["X-Vaarta-Timestamp"]) > 300:
reject # signature is valid but the delivery is too oldThe timestamp exists to defeat replay attacks: an attacker who captures a valid signed payload cannot resubmit it later, because your receiver rejects deliveries outside a short freshness window (e.g. 5 minutes).