← Blog

How to Build a Claude-Powered Sentiment Analysis Tool

2026-11-05 · 5 min read · SubToAPI Team

Building a Claude-powered sentiment analysis tool means sending text to Claude with a prompt that forces structured output (label, score, confidence), then parsing that output in your application. Unlike traditional sentiment models trained on fixed categories, Claude understands nuance, sarcasm, mixed emotions, and domain-specific language without any training data — you just need the right prompt and a reliable way to call the API.

This guide walks through the full process: designing the prompt, enforcing structured JSON output, handling batches of reviews or support tickets, and deploying it as a reusable service. By the end you'll have a working tool you can drop into a product review pipeline, support inbox, or social listening dashboard.

Why use Claude instead of a classic sentiment model

Traditional sentiment libraries (VADER, TextBlob, or small fine-tuned classifiers) are fast and cheap but brittle. They struggle with:

Claude handles all of this natively because it reads text the way a human would. The tradeoff is latency and cost per call, which matters if you're scoring millions of rows — but for most product feedback, support ticket, or review-analysis use cases, the accuracy gain is worth it.

Step 1: Design a prompt that returns structured output

The biggest mistake people make is asking Claude to "analyze the sentiment" in free text. You want machine-readable output every time, so be explicit about the schema and ask for JSON only.

You are a sentiment analysis engine. Given a piece of text, return ONLY valid JSON
with this exact shape, no extra commentary:

{
  "sentiment": "positive" | "negative" | "neutral" | "mixed",
  "score": number from -1.0 (very negative) to 1.0 (very positive),
  "confidence": number from 0.0 to 1.0,
  "key_phrases": string[] (up to 3 phrases that drove the score)
}

Text to analyze:
"""
{{input_text}}
"""

Keep the schema small. Every extra field increases the chance of malformed output. If you need categories like "complaint", "praise", or "question", add them as an optional intent field rather than overloading sentiment.

Step 2: Call the API

Here's a minimal implementation using SubToAPI, which exposes Claude through a standard HTTPS endpoint with an application API key instead of requiring you to manage provider credentials directly:

async function analyzeSentiment(text) {
  const response = await fetch("https://api.subtoapi.app/v1/messages", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.SUBTOAPI_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: "claude-sonnet-4-5",
      max_tokens: 300,
      messages: [
        {
          role: "user",
          content: `Return ONLY JSON: {"sentiment": "positive|negative|neutral|mixed", "score": number, "confidence": number, "key_phrases": string[]}\n\nText: """${text}"""`
        }
      ]
    })
  });

  const data = await response.json();
  const raw = data.content[0].text;
  return JSON.parse(raw);
}

Run it against a sample review:

const result = await analyzeSentiment(
  "Shipping was fast but the packaging was crushed and support never replied."
);
console.log(result);
// { sentiment: "mixed", score: -0.2, confidence: 0.85, key_phrases: ["crushed packaging", "support never replied"] }

For the exact request/response shape, check /docs/messages.

Step 3: Handle malformed JSON gracefully

Even with explicit instructions, Claude occasionally wraps output in markdown fences or adds a trailing sentence. Build a small parser that strips fences before calling JSON.parse:

function parseModelJSON(raw) {
  const cleaned = raw.trim().replace(/^```json\s*/i, "").replace(/```$/i, "");
  try {
    return JSON.parse(cleaned);
  } catch (err) {
    throw new Error(`Failed to parse sentiment JSON: ${cleaned}`);
  }
}

If parsing fails consistently, tighten the prompt further — add "Do not use markdown code fences" explicitly, which usually fixes it.

Step 4: Score batches efficiently

If you're processing a backlog of reviews or tickets, send texts one at a time but run them concurrently rather than sequentially. A simple concurrency-limited loop works well for a few hundred items:

async function scoreBatch(texts, concurrency = 5) {
  const results = [];
  for (let i = 0; i < texts.length; i += concurrency) {
    const chunk = texts.slice(i, i + concurrency);
    const chunkResults = await Promise.all(chunk.map(analyzeSentiment));
    results.push(...chunkResults);
  }
  return results;
}

For larger volumes, cache results by a hash of the input text so you never pay to re-score identical reviews, and monitor token usage per request so costs stay predictable — SubToAPI's dashboard reports usage metadata per key, which makes this easy to track without building your own logging layer.

Step 5: Turn it into a small service

Wrap the logic behind a lightweight endpoint so other parts of your product (a dashboard, a Slack bot, a CSV uploader) can call it without knowing anything about prompts:

app.post("/api/sentiment", async (req, res) => {
  const { text } = req.body;
  if (!text) return res.status(400).json({ error: "text is required" });
  try {
    const result = await analyzeSentiment(text);
    res.json(result);
  } catch (err) {
    res.status(502).json({ error: "sentiment analysis failed" });
  }
});

This separation matters: your frontend or reporting tools never touch the model prompt directly, so you can tweak scoring logic, swap models, or adjust the schema without breaking callers.

Getting started quickly

If you want to skip provider setup, API key management, and billing dashboards, SubToAPI gives you a Claude-backed endpoint behind a single sub_live_ key in a few minutes. Start with /docs/quickstart, review request formats in /docs/messages, and check /pricing for Solo, Team, and Scale plans — all with a free trial at /signup.

Can Claude detect sentiment in languages other than English?

Yes. Claude handles sentiment analysis across most major languages without any extra configuration — just send the text as-is and it will respond correctly in the JSON schema you define.

How accurate is Claude compared to a dedicated sentiment classifier?

For nuanced text — sarcasm, mixed emotions, domain-specific language — Claude generally outperforms small classifiers. For extremely high-volume, low-latency use cases where simple positive/negative labels suffice, a lightweight classifier may still be cheaper and faster.

How do I keep sentiment scoring costs predictable at scale?

Cache results for duplicate text, batch concurrent requests instead of running them sequentially, and track token usage per endpoint. Tools like SubToAPI report usage metadata per API key so you can monitor cost trends without building custom logging.

Turn your Claude access into an HTTPS API

SubToAPI gives you application API keys, streaming, tool use and usage insights on top of your existing Claude access — set up in minutes.

Start free  Read the quickstart →