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
Workflow API pattern Example Track hot topics x_keyword monitorNew posts mentioning your category, feature, launch, or pain point Monitor competitors x_keyword monitorMentions of competitor names, pricing, launches, outages, or customer complaints Watch company leaders x_profile monitorNew posts from a CEO, founder, product leader, or analyst Find conversation opportunities x_keyword search or monitorPosts where people are asking for tools, recommendations, or alternatives Build a lightweight social inbox monitor events polling Store 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.
Hot topic monitor
Competitor monitor
Executive profile monitor
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:
Field Why 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
Create narrow monitors
Use one monitor per topic, competitor, campaign, or executive account. Narrow monitors are easier to route and score.
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=....
Dedupe by post URL
Store the post URL or source-native ID before routing to Slack, a queue, or a CRM task.
Score for actionability
Tag posts that mention buying intent, competitor pain, executive announcements, or urgent support issues.
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.