Docs · Pro

API and webhooks

Manage watches and read results programmatically, and receive every match as a signed webhook.

Authentication

Create a token in Settings → API and send it as a bearer token. Tokens belong to a workspace; the API is available on the Pro plan. Each token can make 60 requests per minute; above that you get 429 with a Retry-After header.

curl https://trykeywordsonar.com/api/v1/watches \
  -H "Authorization: Bearer ks_…"

Endpoints

MethodPathDescription
GET/watchesList watches.
POST/watchesCreate a watch. Body: name, query, type, sources, communities_include, communities_exclude, domains_include, domains_exclude, item_types, fields, languages, excluded_authors, nsfw, rule_enabled, rule_text, destinations[{destination_id, cadence}].
GET/watches/:idGet a watch.
PATCH/watches/:idUpdate any of the fields above.
DELETE/watches/:idDelete a watch and its results.
POST/watches/:id/pausePause a watch.
POST/watches/:id/resumeResume a watch (subject to plan limits).
GET/resultsList results. Filters: watch, source, type (post|comment), state (unread|read|done|hidden|filtered|all), from, to (YYYY-MM-DD), q, limit, cursor.
GET/destinationsList destinations.

Lists return { "data": [...] }; /results also returns next_cursor. Errors return { "error": { "status", "message" } }.

Webhooks

Add a webhook destination and attach it to a watch. On Immediate we send one request per result; on other cadences, one batch per cadence. Failed deliveries are retried with back-off for 24 hours, and the last delivery status is shown on the Destinations page.

POST https://your-endpoint.example/keyword-sonar
x-keywordsonar-event: result
x-keywordsonar-signature: t=1790180000,v1=5d0c…

{
  "type": "result",
  "delivered_at": "2026-09-23T14:02:11.000Z",
  "results": [{
    "result_id": "r_…", "watch_id": "w_…", "watch_name": "Fernly",
    "source": "reddit", "community": "r/SaaS", "item_type": "comment",
    "title": "What are you using for invoicing?", "snippet": "…moved our billing to Fernly…",
    "permalink": "https://www.reddit.com/r/SaaS/comments/…",
    "author": "maya_builds",
    "published_at": "2026-09-23T14:01:40.000Z", "collected_at": "2026-09-23T14:01:52.000Z",
    "matched_terms": ["fernly"]
  }]
}

Verifying signatures

import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map((kv) => kv.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}