Skip to main content
Use monitors when you want a topic-based job that runs on a schedule. Use search when you want a one-off snapshot before creating a recurring monitor. This guide shows examples for social media teams that want to find relevant conversations early, watch competitors, and track what executives or target accounts post on X.

What you can build

WorkflowAPI patternExample
Track hot topicsx_keyword monitorNew posts mentioning your category, feature, launch, or pain point
Monitor competitorsx_keyword monitorMentions of competitor names, pricing, launches, outages, or customer complaints
Watch company leadersx_profile monitorNew posts from a CEO, founder, product leader, or analyst
Find conversation opportunitiesx_keyword search or monitorPosts where people are asking for tools, recommendations, or alternatives
Build a lightweight social inboxmonitor events pollingStore new matching posts, dedupe by URL, and route them to Slack or your CRM
X keyword and profile monitors return matching posts, author usernames, post URLs, timestamps, and engagement counts when available. If you need a complete list of users who liked, reposted, or replied to a specific X post, treat that as a separate engagement expansion workflow rather than assuming every engagement identity is included in keyword monitor results.

Create X monitors

Create separate monitors for each workflow so you can route and score results differently.
curl -X POST https://developer.thehog.ai/api/v1/monitors \
  -H "X-Access-Key: ak_xxxxxxxxxxxxxxxx" \
  -H "X-Secret-Key: sk_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "X: customer data platform conversations",
    "type": "x_keyword",
    "config": {
      "query": "\"customer data platform\" OR \"CDP\""
    },
    "cadence_minutes": 60,
    "max_results": 25
  }'

Poll new events

After a monitor runs, poll its events endpoint. Store your last successful poll timestamp and pass it as since so you only process new items.
curl "https://developer.thehog.ai/api/v1/monitors/mon_01hxyz/events?since=2024-01-01T00:00:00.000Z&limit=50" \
  -H "X-Access-Key: ak_xxxxxxxxxxxxxxxx" \
  -H "X-Secret-Key: sk_xxxxxxxxxxxxxxxx"
A typical social inbox stores:
FieldWhy it matters
event_json.urlLink for the social team
event_json.idSource-native post ID to help dedupe
event_json.author_usernameX account to review
event_json.textPost text to score, summarize, or route
event_json.created_atWhen the post was published, when available
detected_atWhen the monitor found the post
The exact event_json fields can vary by source, so write your ingestion to prefer stable fields and fall back gracefully.

One-off X search before monitoring

Run a search first when you are testing query wording or building a prospect-facing demo.
curl -X POST https://developer.thehog.ai/api/v1/search \
  -H "X-Access-Key: ak_xxxxxxxxxxxxxxxx" \
  -H "X-Secret-Key: sk_xxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "x_keyword",
    "query": "\"product analytics\" \"need a tool\"",
    "max_results": 20
  }'
The response returns 202 Accepted with a poll_url. Poll that URL until the search status is succeeded.

JavaScript example: route new X posts

Create x-social-listening.mjs:
const API_BASE_URL = process.env.HOG_API_BASE_URL ?? "https://developer.thehog.ai";
const ACCESS_KEY = process.env.HOG_API_ACCESS_KEY;
const SECRET_KEY = process.env.HOG_API_SECRET_KEY;

const monitorId = process.argv[2];
const since = process.argv[3] ?? new Date(Date.now() - 60 * 60 * 1000).toISOString();

if (!ACCESS_KEY || !SECRET_KEY) {
  throw new Error("Set HOG_API_ACCESS_KEY and HOG_API_SECRET_KEY");
}

if (!monitorId) {
  throw new Error("Usage: node x-social-listening.mjs <monitor-id> [since-iso]");
}

async function get(path) {
  const response = await fetch(`${API_BASE_URL}${path}`, {
    headers: {
      "X-Access-Key": ACCESS_KEY,
      "X-Secret-Key": SECRET_KEY,
    },
  });

  if (!response.ok) {
    const text = await response.text();
    throw new Error(`${path} failed with ${response.status}: ${text}`);
  }

  return response.json();
}

function pickPost(event) {
  const payload = event.event_json ?? {};
  return {
    detectedAt: event.detected_at,
    url: payload.post_url ?? payload.url,
    author: payload.author_username ?? payload.author ?? payload.author_name,
    text: payload.content ?? payload.text ?? payload.title,
  };
}

function score(post) {
  const text = (post.text ?? "").toLowerCase();
  const positiveIntent = ["alternative", "recommend", "need", "looking for"];
  const competitorRisk = ["expensive", "outage", "broken", "switching"];
  const tags = new Set();

  if (positiveIntent.some((term) => text.includes(term))) {
    tags.add("buying-intent");
  }

  if (competitorRisk.some((term) => text.includes(term))) {
    tags.add("competitor-signal");
  }

  return {
    ...post,
    tags: [...tags],
  };
}

const params = new URLSearchParams({ since, limit: "50" });
const response = await get(`/api/v1/monitors/${monitorId}/events?${params}`);
const posts = (response.data ?? []).map(pickPost).filter((post) => post.url || post.text);

console.log(JSON.stringify(posts.map(score), null, 2));
Run it:
export HOG_API_ACCESS_KEY="ak_xxxxxxxxxxxxxxxx"
export HOG_API_SECRET_KEY="sk_xxxxxxxxxxxxxxxx"

node x-social-listening.mjs mon_01hxyz 2024-01-01T00:00:00.000Z

Production pattern

1

Create narrow monitors

Use one monitor per topic, competitor, campaign, or executive account. Narrow monitors are easier to route and score.
2

Poll on the monitor cadence

Poll GET /api/v1/monitors/:id to check last_run_at, then fetch GET /api/v1/monitors/:id/events?since=....
3

Dedupe by post URL

Store the post URL or source-native ID before routing to Slack, a queue, or a CRM task.
4

Score for actionability

Tag posts that mention buying intent, competitor pain, executive announcements, or urgent support issues.
5

Keep a human in the loop

Use the API to surface opportunities early, then let your social or sales team decide whether and how to join the conversation.