Social media analysis turns noisy feeds into structured intelligence. In this tutorial we will build a batch analyzer that ingests raw posts and extracts sentiment, themes, and urgency using an LLM. I run a variant of this script every week to monitor brand mentions, and because it pushes large batches through a single API request, Oxlo.ai's flat per-request pricing keeps the cost predictable even when the input grows.
What you'll need
- Python 3.10 or higher
- An Oxlo.ai API key from https://portal.oxlo.ai
- The OpenAI SDK installed with
pip install openai - A handful of sample posts (we will create dummy data inline)
Step 1: Bootstrap the client and sample data
I like to keep dependencies minimal. We only need the openai package and a JSON list of posts. Grab your API key from the Oxlo.ai portal and hardcode it for local testing, or load it from an environment variable.
import json
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.oxlo.ai/v1",
api_key=os.getenv("OXLO_AI_API_KEY", "YOUR_OXLO_AI_API_KEY")
)
raw_posts = [
{"id": "101", "text": "Just tried the new API and it is blazing fast. Huge improvement over the old version.", "platform": "twitter", "timestamp": "2024-05-20T14:00:00Z"},
{"id": "102", "text": "Customer support has been unresponsive for three days. Extremely frustrated.", "platform": "reddit", "timestamp": "2024-05-20T15:30:00Z"},
{"id": "103", "text": "Does anyone know if the enterprise plan includes SSO? Documentation is unclear.", "platform": "twitter", "timestamp": "2024-05-20T16:00:00Z"},
{"id": "104", "text": "Love the new dashboard design. Dark mode is finally here!", "platform": "linkedin", "timestamp": "2024-05-20T17:00:00Z"},
{"id": "105", "text": "Experiencing intermittent 500 errors on the webhook endpoint since yesterday.", "platform": "github", "timestamp": "2024-05-20T18:00:00Z"},
]
Step 2: Write the system prompt
The system prompt is the only training the analyzer gets. I force JSON output with a strict schema so parsing is trivial. I also ask for a global trends object so the model does the aggregation for us.
SYSTEM_PROMPT = """You are a social media intelligence analyst. Your job is to analyze a batch of raw social media posts and return a structured JSON object.
For each post, extract:
- sentiment: one of [positive, negative, neutral, mixed]
- themes: a list of up to 3 short keywords or phrases
- urgency: one of [low, medium, high] based on whether the post signals an immediate issue or opportunity
- summary: a one-sentence summary of the post
Additionally, provide a global object called "trends" containing:
- top_themes: the 3 most frequent themes across all posts
- overall_sentiment: the dominant sentiment
- recommended_action: a single concrete recommendation for the communications team
Return only valid JSON in this exact shape:
{
"posts": [
{"id": "...", "sentiment": "...", "themes": ["..."], "urgency": "...", "summary": "..."}
],
"trends": {
"top_themes": ["...", "...", "..."],
"overall_sentiment": "...",
"recommended_action": "..."
}
}
Do not include markdown formatting or explanation outside the JSON."""
Step 3: Build the analyzer function
This is where we call Oxlo.ai. I use kimi-k2.6 because its 131K context window easily holds dozens of posts in a single request. Because Oxlo.ai charges a flat rate per request rather than per token, stuffing a large batch into one call is the most cost-effective way to analyze long threads or weekly dumps.
def analyze_posts(posts: list[dict]) -> dict:
user_message = "Analyze the following posts:\n" + json.dumps(posts, ensure_ascii=False, indent=2)
response = client.chat.completions.create(
model="kimi-k2.6",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
)
content = response.choices[0].message.content.strip()
if content.startswith("
```json"):
content = content.split("```
json")[1].split("
```")[0].strip()
elif content.startswith("```
"):
content = content.split("
```")[1].split("```
")[0].strip()
return json.loads(content)
Step 4: Add the report wrapper
A small pretty-printer keeps the output readable when I run this from a cron job or a GitHub Action.
def run_analysis(posts: list[dict]) -> None:
print(f"Analyzing {len(posts)} posts...")
result = analyze_posts(posts)
print("\n=== Per-Post Breakdown ===")
for p in result.get("posts", []):
print(f"[{p['urgency'].upper()}] {p['sentiment']} | {p['summary']}")
print(f" Themes: {', '.join(p['themes'])}\n")
trends = result.get("trends", {})
print("=== Trends ===")
print(f"Top themes: {', '.join(trends.get('top_themes', []))}")
print(f"Overall sentiment: {trends.get('overall_sentiment')}")
print(f"Recommended action: {trends.get('recommended_action')}")
Run it
Call the wrapper with our sample data. The first time I ran this it surfaced a support complaint and a webhook outage in the same batch.
if __name__ == "__main__":
run_analysis(raw_posts)
Example output:
Analyzing 5 posts...
=== Per-Post Breakdown ===
[LOW] positive | User praises new API speed and improvement.
Themes: product speed, API, improvement
[HIGH] negative | Customer complains about unresponsive support for three days.
Themes: customer support, frustration, responsiveness
[MEDIUM] neutral | User asks about SSO inclusion in enterprise plan.
Themes: enterprise plan, SSO, documentation
[LOW] positive | User expresses love for new dashboard dark mode.
Themes: dashboard design, dark mode, UI
[HIGH] negative | User reports intermittent 500 errors on webhook endpoint.
Themes: webhook, 500 errors, reliability
=== Trends ===
Top themes: customer support, webhook, product speed
Overall sentiment: mixed
Recommended action: Prioritize engineering investigation into webhook 500 errors and assign a support lead to the unresolved ticket thread.
Wrap-up and next steps
To make this production-ready, wire it to a real data source such as the Reddit or Twitter API and schedule the script on a cron job. If your volume grows into the thousands of posts, split them into chunks of 50 and process each chunk in parallel. With Oxlo.ai's request-based pricing, each chunk costs the same regardless of how long the individual posts are, which keeps long-context workloads far cheaper than token-based alternatives. For current plan details, see https://oxlo.ai/pricing.
Top comments (0)