We are building a real-time social media opinion miner that classifies sentiment, extracts topics, and flags urgent complaints. It helps community managers and product teams cut through noise without reading every thread.
What you'll need
- Python 3.10 or newer
- The OpenAI SDK:
pip install openai - An Oxlo.ai API key from https://portal.oxlo.ai
Step 1: Connect to Oxlo.ai and test the endpoint
Before we process any social data, we need to verify the client can reach Oxlo.ai and that our key works. We will make a simple call to Llama 3.3 70B. If you later need to handle multilingual streams, Oxlo.ai lets you swap the model string to qwen-3-32b with no other changes.
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "user", "content": "Reply with OK if you are online."},
],
)
print(response.choices[0].message.content)
Step 2: Craft the system prompt for structured sentiment analysis
Social media text is noisy. A strict system prompt forces the model to return uniform JSON with sentiment labels, confidence scores, mentioned topics, and urgency flags.
SYSTEM_PROMPT = """You are a social media opinion mining engine. Analyze the provided post and return a single JSON object with these exact keys:
- sentiment: one of "positive", "neutral", "negative"
- confidence: integer 1-10
- topics: array of up to 3 strings mentioned in the post
- urgent: boolean, true only if the post signals a bug, outage, safety issue, or requires immediate response
- summary: one sentence summarizing the user's core opinion
Rules:
- Output ONLY valid JSON. No markdown fences, no explanations.
- If the post is sarcastic, classify by the intended meaning.
- Topics should be lowercase with no spaces, use underscores if needed."""
Step 3: Build the analysis function with JSON mode
Now we wire the prompt into a reusable function. We use Oxlo.ai's JSON mode to guarantee parseable output, which lets us skip regex cleanup.
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are a social media opinion mining engine. Analyze the provided post and return a single JSON object with these exact keys:
- sentiment: one of "positive", "neutral", "negative"
- confidence: integer 1-10
- topics: array of up to 3 strings mentioned in the post
- urgent: boolean, true only if the post signals a bug, outage, safety issue, or requires immediate response
- summary: one sentence summarizing the user's core opinion
Rules:
- Output ONLY valid JSON. No markdown fences, no explanations.
- If the post is sarcastic, classify by the intended meaning.
- Topics should be lowercase with no spaces, use underscores if needed."""
def analyze_post(text: str) -> dict:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": text},
],
response_format={"type": "json_object"},
temperature=0.1,
)
return json.loads(response.choices[0].message.content)
sample = "Love the new update, but the login page keeps crashing every time I try 2FA."
print(analyze_post(sample))
Step 4: Batch process a feed
In production, posts arrive in bursts. We process a list of raw messages sequentially and collect the results into a list of dictionaries for downstream reporting.
import json
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are a social media opinion mining engine. Analyze the provided post and return a single JSON object with these exact keys:
- sentiment: one of "positive", "neutral", "negative"
- confidence: integer 1-10
- topics: array of up to 3 strings mentioned in the post
- urgent: boolean, true only if the post signals a bug, outage, safety issue, or requires immediate response
- summary: one sentence summarizing the user's core opinion
Rules:
- Output ONLY valid JSON. No markdown fences, no explanations.
- If the post is sarcastic, classify by the intended meaning.
- Topics should be lowercase with no spaces, use underscores if needed."""
def analyze_post(text: str) -> dict:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": text},
],
response_format={"type": "json_object"},
temperature=0.1,
)
return json.loads(response.choices[0].message.content)
raw_posts = [
"Just switched to your pro plan. The export feature is a lifesaver!",
"ugh... the mobile app is so laggy after the latest patch. fix it already.",
"Does anyone know if webhook retries are configurable? docs are unclear.",
"Shoutout to support team for fixing my billing issue in under 5 mins.",
"CRITICAL: payment webhook fired but invoice shows unpaid. losing revenue here."
]
results = []
for post in raw_posts:
try:
parsed = analyze_post(post)
parsed["original"] = post
results.append(parsed)
except Exception as e:
print(f"Failed on post: {post[:50]}... Error: {e}")
print(f"Processed {len(results)} posts successfully.")
Step 5: Aggregate and surface insights
Raw classifications are not enough. We need a small aggregator that counts sentiment distribution, lists top topics, and surfaces any urgent posts that need immediate attention.
from collections import Counter
import json
# Assumes `results` is populated from Step 4
def aggregate(results: list[dict]) -> dict:
sentiments = Counter([r["sentiment"] for r in results])
topics = Counter()
urgent = []
for r in results:
topics.update(r.get("topics", []))
if r.get("urgent"):
urgent.append({
"summary": r["summary"],
"original": r["original"],
"confidence": r["confidence"]
})
return {
"total": len(results),
"sentiment_counts": dict(sentiments),
"top_topics": topics.most_common(5),
"urgent_posts": urgent
}
report = aggregate(results)
print(json.dumps(report, indent=2))
Run it
Putting it all together, the script ingests five realistic social posts and prints a structured report. Here is the complete script and the output it produces.
import json
from collections import Counter
from openai import OpenAI
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
SYSTEM_PROMPT = """You are a social media opinion mining engine. Analyze the provided post and return a single JSON object with these exact keys:
- sentiment: one of "positive", "neutral", "negative"
- confidence: integer 1-10
- topics: array of up to 3 strings mentioned in the post
- urgent: boolean, true only if the post signals a bug, outage, safety issue, or requires immediate response
- summary: one sentence summarizing the user's core opinion
Rules:
- Output ONLY valid JSON. No markdown fences, no explanations.
- If the post is sarcastic, classify by the intended meaning.
- Topics should be lowercase with no spaces, use underscores if needed."""
def analyze_post(text: str) -> dict:
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": text},
],
response_format={"type": "json_object"},
temperature=0.1,
)
return json.loads(response.choices[0].message.content)
raw_posts = [
"Just switched to your pro plan. The export feature is a lifesaver!",
"ugh... the mobile app is so laggy after the latest patch. fix it already.",
"Does anyone know if webhook retries are configurable? docs are unclear.",
"Shoutout to support team for fixing my billing issue in under 5 mins.",
"CRITICAL: payment webhook fired but invoice shows unpaid. losing revenue here."
]
results = []
for post in raw_posts:
try:
parsed = analyze_post(post)
parsed["original"] = post
results.append(parsed)
except Exception as e:
print(f"Failed: {e}")
def aggregate(results: list[dict]) -> dict:
sentiments = Counter([r["sentiment"] for r in results])
topics = Counter()
urgent = []
for r in results:
topics.update(r.get("topics", []))
if r.get("urgent"):
urgent.append({
"summary": r["summary"],
"original": r["original"],
"confidence": r["confidence"]
})
return {
"total": len(results),
"sentiment_counts": dict(sentiments),
"top_topics": topics.most_common(5),
"urgent_posts": urgent
}
print(json.dumps(aggregate(results), indent=2))
Example output:
{
"total": 5,
"sentiment_counts": {
"positive": 2,
"neutral": 1,
"negative": 2
},
"top_topics": [
["webhook", 2],
["payment", 1],
["mobile_app", 1],
["export_feature", 1],
["billing", 1]
],
"urgent_posts": [
{
"summary": "Payment webhook fired but invoice remains unpaid causing revenue loss.",
"original": "CRITICAL: payment webhook fired but invoice shows unpaid. losing revenue here.",
"confidence": 10
}
]
}
Wrap-up and next steps
This pipeline gives you a working opinion miner on Oxlo.ai. Because Oxlo.ai uses flat request-based pricing, feeding it long threads or multi-turn conversations costs the same as short tweets, which makes it a practical choice for high-volume social monitoring.
Concrete next steps:
- Wire the analyzer to a live firehose and write results to Postgres.
- Add a severity threshold that pages the on-call engineer when urgent posts appear.
Top comments (0)