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 | Not available (pro+ only) |
| Pro | 300 | 3 |
| Team | 1,000 | 3 |
The per-minute limit is enforced per key and scales with your plan tier (60 RPM on Free, 300 on Pro, 1,000 on Team). 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).