competitive-intel
Scannednpx machina-cli add skill XPOZpublic/xpoz-agent-skills/competitive-intel --openclawCompetitive Intelligence
Overview
Compare multiple brands or products side by side across Twitter/X, Reddit, and Instagram. Measure share of voice, compare sentiment, identify positioning differences, and discover competitive advantages from real social conversations.
When to Use
Activate when the user asks:
- "Compare [BRAND A] vs [BRAND B] on social media"
- "Share of voice: [BRAND] vs competitors"
- "Competitive analysis for [PRODUCT]"
- "How does [BRAND A] sentiment compare to [BRAND B]?"
- "What are people saying about [BRAND] vs [COMPETITOR]?"
Setup & Authentication
Before fetching data, ensure Xpoz access is configured. Follow these checks in order.
Check 1: Already authenticated?
If you have MCP tools, try calling any Xpoz tool (e.g., checkAccessKeyStatus). If it works → skip to Step 1.
If you have the SDK, try:
from xpoz import XpozClient
client = XpozClient() # reads XPOZ_API_KEY env var
If this succeeds without error → skip to Step 1.
If neither works, you need to authenticate. Choose the path that fits your environment:
Path A: MCP via mcporter (OpenClaw agents)
If mcporter is available:
mcporter call xpoz.checkAccessKeyStatus
If hasAccessKey: true → ready. If not:
mcporter config add xpoz https://mcp.xpoz.ai/mcp --auth oauth
Then authenticate — generate the OAuth URL and send it to the user:
Step 1: Generate authorization URL
import secrets, hashlib, base64, urllib.parse, json, urllib.request, os
verifier = secrets.token_urlsafe(64)
challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode()).digest()).rstrip(b'=').decode()
state = secrets.token_urlsafe(32)
# Dynamic client registration
reg_req = urllib.request.Request(
'https://mcp.xpoz.ai/oauth/register',
data=json.dumps({
'client_name': 'Agent Skills',
'redirect_uris': ['https://www.xpoz.ai/oauth/openclaw'],
'grant_types': ['authorization_code'],
'response_types': ['code'],
'token_endpoint_auth_method': 'none',
}).encode(),
headers={'Content-Type': 'application/json'},
)
reg_resp = json.loads(urllib.request.urlopen(reg_req).read())
params = urllib.parse.urlencode({
'response_type': 'code',
'client_id': reg_resp['client_id'],
'code_challenge': challenge,
'code_challenge_method': 'S256',
'redirect_uri': 'https://www.xpoz.ai/oauth/openclaw',
'state': state,
'scope': 'mcp:tools',
'resource': 'https://mcp.xpoz.ai/',
})
auth_url = 'https://mcp.xpoz.ai/oauth/authorize?' + params
# Save state for token exchange
os.makedirs(os.path.expanduser('~/.cache/xpoz-oauth'), exist_ok=True)
with open(os.path.expanduser('~/.cache/xpoz-oauth/state.json'), 'w') as f:
json.dump({'verifier': verifier, 'state': state, 'client_id': reg_resp['client_id'],
'redirect_uri': 'https://www.xpoz.ai/oauth/openclaw'}, f)
print(auth_url)
Step 2: Send the URL to the user
Tell them:
"I need to connect to Xpoz for social media data. Please open this link and sign in:
[auth_url]
After authorizing, you'll see a code. Paste it back to me here."
Step 3: WAIT for the user to reply with the code. Do not proceed until they respond.
Step 4: Exchange the code for a token
Once the user provides the code (either a raw code or a URL containing ?code=...), extract the code and exchange it:
import json, urllib.request, urllib.parse, subprocess, os
with open(os.path.expanduser('~/.cache/xpoz-oauth/state.json')) as f:
oauth = json.load(f)
code = "THE_CODE_FROM_USER" # Extract from user's reply
data = urllib.parse.urlencode({
'grant_type': 'authorization_code',
'code': code,
'redirect_uri': oauth['redirect_uri'],
'client_id': oauth['client_id'],
'code_verifier': oauth['verifier'],
}).encode()
req = urllib.request.Request(
'https://mcp.xpoz.ai/oauth/token',
data=data,
headers={'Content-Type': 'application/x-www-form-urlencoded'},
)
resp = json.loads(urllib.request.urlopen(req).read())
token = resp['access_token']
# Configure mcporter with the token (token is never printed)
subprocess.run(['mcporter', 'config', 'remove', 'xpoz'], capture_output=True)
subprocess.run(['mcporter', 'config', 'add', 'xpoz', 'https://mcp.xpoz.ai/mcp',
'--header', f'Authorization=Bearer {token}'], check=True)
# Clean up
os.remove(os.path.expanduser('~/.cache/xpoz-oauth/state.json'))
print("Xpoz configured successfully")
Step 5: Verify with mcporter call xpoz.checkAccessKeyStatus → should return hasAccessKey: true.
Path B: MCP via Claude Code config
For Claude Code users without mcporter, add to ~/.claude.json:
{
"mcpServers": {
"xpoz": {
"url": "https://mcp.xpoz.ai/mcp",
"transport": "http-stream"
}
}
}
Claude Code handles OAuth automatically on first tool call — the user just needs to authorize in their browser when prompted.
Path C: SDK (Python or TypeScript)
Ask the user:
"I need a Xpoz API key to access social media data. Please go to https://xpoz.ai/get-token (it's free, no credit card needed) and paste the key back to me."
WAIT for the user to reply with the key. Then:
Python:
pip install xpoz
from xpoz import XpozClient
client = XpozClient("THE_KEY_FROM_USER")
TypeScript:
npm install @xpoz/xpoz
import { XpozClient } from "@xpoz/xpoz";
const client = new XpozClient({ apiKey: "THE_KEY_FROM_USER" });
await client.connect();
Or set the environment variable and use the default constructor:
export XPOZ_API_KEY=THE_KEY_FROM_USER
Auth Errors
| Problem | Solution |
|---|---|
| MCP: "Unauthorized" | Re-run the OAuth flow above |
SDK: AuthenticationError | Verify key at xpoz.ai/settings |
| Token exchange fails | Ask user to re-authorize — codes are single-use |
Step-by-Step Instructions
Step 1: Parse the Request
Extract:
- Primary brand and competitors (2-5 brands total)
- Platforms (default: Twitter + Reddit)
- Time period (default: last 7 days)
- Industry context for better analysis
Build expanded queries for each brand:
"Slack"→"Slack" NOT "cut some slack" NOT "slack off""Discord"→"Discord" NOT "sow discord" NOT "discord between"- For stocks: include ticker symbols
Step 2: Fetch Data for Each Brand
Run parallel searches — one per brand, per platform.
Via MCP
For each brand, call:
Twitter posts:
Call getTwitterPostsByKeywords:
query: "<brand query>"
fields: ["id", "text", "authorUsername", "createdAtDate", "likeCount", "retweetCount", "impressionCount"]
startDate: "<7 days ago>"
endDate: "<today>"
language: "en"
Twitter users discussing the brand:
Call getTwitterUsersByKeywords:
query: "<brand query>"
fields: ["id", "username", "name", "followersCount", "relevantTweetsCount", "relevantTweetsLikesSum"]
startDate: "<7 days ago>"
Reddit (for each brand):
Call getRedditPostsByKeywords:
query: "<brand query>"
fields: ["id", "title", "text", "score", "numComments", "subreddit", "createdAtDate"]
startDate: "<7 days ago>"
CRITICAL: Each call returns an operationId — poll checkOperationStatus until "completed".
Tip: Launch all brand searches in sequence, collect all operationIds, then poll them. This is faster than waiting for each one.
Via Python SDK
from xpoz import XpozClient
client = XpozClient()
brands = {
"Slack": '"Slack" NOT "cut some slack"',
"Discord": '"Discord" NOT "sow discord"',
"Teams": '"Microsoft Teams" OR "MS Teams"',
}
brand_data = {}
for brand_name, query in brands.items():
# Twitter posts
twitter = client.twitter.search_posts(
query,
start_date="2026-02-16",
end_date="2026-02-23",
language="en",
fields=["id", "text", "author_username", "like_count", "retweet_count", "impression_count", "created_at_date"]
)
# Twitter users (for influencer overlap analysis)
users = client.twitter.get_users_by_keywords(
query,
start_date="2026-02-16",
fields=["username", "followers_count", "relevant_tweets_count", "relevant_tweets_likes_sum"]
)
# Reddit posts
reddit = client.reddit.search_posts(
query,
start_date="2026-02-16",
fields=["id", "title", "text", "score", "num_comments", "subreddit", "created_at_date"]
)
brand_data[brand_name] = {
"twitter_posts": twitter,
"twitter_users": users,
"reddit_posts": reddit,
"tweet_count": twitter.pagination.total_rows,
"reddit_count": reddit.pagination.total_rows,
}
client.close()
Via TypeScript SDK
import { XpozClient } from "@xpoz/xpoz";
const client = new XpozClient();
await client.connect();
const brands: Record<string, string> = {
Slack: '"Slack" NOT "cut some slack"',
Discord: '"Discord" NOT "sow discord"',
Teams: '"Microsoft Teams" OR "MS Teams"',
};
const brandData: Record<string, any> = {};
for (const [name, query] of Object.entries(brands)) {
const twitter = await client.twitter.searchPosts(query, {
startDate: "2026-02-16",
endDate: "2026-02-23",
language: "en",
fields: ["id", "text", "authorUsername", "likeCount", "retweetCount", "createdAtDate"],
});
const reddit = await client.reddit.searchPosts(query, {
startDate: "2026-02-16",
fields: ["id", "title", "text", "score", "numComments", "subreddit"],
});
brandData[name] = { twitter, reddit };
}
await client.close();
Step 3: Analyze and Compare
Share of Voice (SOV):
SOV for Brand A = (Brand A mentions) / (Total mentions across all brands) × 100
Calculate separately for Twitter and Reddit.
Sentiment Comparison: For each brand, classify posts into positive/neutral/negative (see social-sentiment-analyzer skill for classification method) and compare:
- Overall sentiment score (0-100)
- Positive/negative ratio
- Sentiment trend over the time period
Engagement Comparison:
- Average likes per post
- Average comments/replies per post
- Total impressions (Twitter)
- Total Reddit score
Audience Overlap:
- Find users who posted about multiple brands (common usernames across datasets)
- These users are particularly valuable for understanding switching behavior
Positioning Analysis:
- What attributes does each brand's audience associate with it?
- What are the unique strengths/weaknesses mentioned for each?
- Common comparison contexts ("I switched from X to Y because...")
Step 4: Generate Report
## Competitive Intelligence: [BRAND] vs Competitors
**Period:** [date range] | **Platforms:** Twitter, Reddit
### Share of Voice
| Brand | Twitter Posts | Reddit Posts | Total | SOV |
|-------|-------------|-------------|-------|-----|
| Slack | 1,234 | 456 | 1,690 | 42% |
| Discord | 890 | 678 | 1,568 | 39% |
| Teams | 456 | 321 | 777 | 19% |
### Sentiment Comparison
| Brand | Score | Positive | Neutral | Negative | Trend |
|-------|-------|----------|---------|----------|-------|
| Slack | 62 | 38% | 42% | 20% | → Stable |
| Discord | 71 | 48% | 35% | 17% | ↑ Improving |
| Teams | 45 | 22% | 45% | 33% | ↓ Declining |
### Engagement Comparison
| Brand | Avg Likes (Twitter) | Avg Score (Reddit) | Avg Comments |
|-------|--------------------|--------------------|--------------|
| ... | ... | ... | ... |
### Key Findings
#### [Brand A] Strengths
- [What people praise, with example quotes]
#### [Brand A] Weaknesses
- [What people complain about, with example quotes]
#### [Brand B] Strengths / Weaknesses
...
### Competitive Positioning Map
- **[Brand A]:** Positioned as [description]
- **[Brand B]:** Positioned as [description]
- **Switching signals:** [users switching from X to Y, with reasons]
### Audience Overlap
[X users posted about multiple brands — analysis of their preferences]
### Recommendations
[3-5 actionable insights based on the competitive landscape]
Example Prompts
- "Compare Tesla vs Rivian vs Lucid on Twitter sentiment"
- "Share of voice: Figma vs Sketch vs Adobe XD"
- "Competitive analysis for Notion vs Obsidian vs Roam Research on Reddit"
- "How does Claude sentiment compare to ChatGPT and Gemini?"
Notes
- Expand brand names carefully to avoid false positives (common words need exclusions)
- Reddit provides qualitative depth; Twitter provides quantitative breadth
- Free tier: 100K results/month at xpoz.ai
- For large comparisons (5+ brands), use CSV exports and analyze locally with pandas/Excel
Source
git clone https://github.com/XPOZpublic/xpoz-agent-skills/blob/main/skills/competitive-intel/SKILL.mdView on GitHub Overview
Competitive Intelligence lets you compare multiple brands or products side by side across Twitter/X, Reddit, and Instagram. It measures share of voice, contrasts sentiment, and reveals positioning differences and competitive advantages from real social conversations.
How This Skill Works
The tool pulls social data via Xpoz for the selected brands, then computes share of voice, sentiment, and audience overlap. It presents side-by-side metrics and highlights where brands diverge in positioning, helping you spot gaps and opportunities.
When to Use It
- Compare BRAND A vs BRAND B on social media
- Share of voice: BRAND vs competitors
- Competitive analysis for PRODUCT
- How does BRAND A sentiment compare to BRAND B?
- What are people saying about BRAND vs COMPETITOR?
Quick Start
- Step 1: Ensure Xpoz access is configured in your environment
- Step 2: Enter brands or products to compare
- Step 3: Run the analysis and review the side-by-side results
Best Practices
- Define the brands or products clearly and use consistent identifiers
- Run comparisons over matching time windows to avoid bias
- Segment results by platform to understand channel differences
- Annotate findings with actionable insights for marketing strategy
- Revisit results after significant campaigns to detect shifts
Example Use Cases
- Brand A vs Brand B on Twitter/X sentiment gaps
- Share of voice vs competitors during a major product launch
- Positioning differences between brands in Reddit discussions
- Audience overlap analysis for the same product across platforms
- Tracking competitive shifts over quarterly periods