← Blog

How to Test Claude API Prompts Locally

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

If you're iterating on a prompt, you don't want to redeploy an app every time you change a word. Testing Claude API prompts locally means running requests from your own machine against a script or test harness, so you can tweak system prompts, inputs, and parameters in seconds and see the raw response immediately.

The short answer: set up a small script (curl or a Node/Python file) that reads your prompt from a file or variable, sends it to the API, and prints the output. Add a loop or a test runner once you have more than a couple of prompts, and use fixtures so you can re-run the same inputs after every change. Below is a setup that works whether you're calling Claude directly or through a proxy like SubToAPI.

Why local testing matters

Prompt behavior shifts with small wording changes, temperature, max tokens, and system instructions. If every test requires a full app deploy or a UI click-through, you'll test less often and ship worse prompts. A local loop gives you:

Set up a minimal test script

The fastest local setup is a single curl command with your prompt in a variable:

export API_KEY="sub_live_xxx"

curl https://api.subtoapi.app/v1/messages \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet",
    "max_tokens": 500,
    "system": "You are a concise technical reviewer.",
    "messages": [
      {"role": "user", "content": "Summarize this changelog in 3 bullets: Added dark mode, fixed login bug, improved load time."}
    ]
  }'

Save variations of this as shell scripts (test-summary.sh, test-classify.sh) and run them individually as you tweak the prompt text. This works with any Messages-API-compatible endpoint — see /docs/messages for the full request shape.

Build a reusable local test harness

Once you have more than a few prompts, move to a small Node script that loads prompt variants from files and prints results side by side:

import fs from "fs";

const API_KEY = process.env.SUBTOAPI_KEY;
const prompts = JSON.parse(fs.readFileSync("./prompts.json", "utf8"));

async function run(prompt) {
  const res = await fetch("https://api.subtoapi.app/v1/messages", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      model: "claude-3-5-sonnet",
      max_tokens: 500,
      system: prompt.system,
      messages: [{ role: "user", content: prompt.input }]
    })
  });
  const data = await res.json();
  console.log(`\n--- ${prompt.name} ---`);
  console.log(data.content?.[0]?.text ?? data);
}

for (const prompt of prompts) {
  await run(prompt);
}

prompts.json holds your test cases:

[
  { "name": "classify-v1", "system": "Classify the ticket.", "input": "My invoice is wrong." },
  { "name": "classify-v2", "system": "Classify the ticket into billing, technical, or other.", "input": "My invoice is wrong." }
]

Run node test.js, compare outputs, adjust, repeat. No app, no server, no deploy.

Use fixtures instead of live data

Hardcode a handful of representative inputs — edge cases, short inputs, long inputs, ambiguous ones — into a fixtures file. Re-run the full fixture set every time you change a prompt so you catch regressions, not just improvements on the one example you were staring at. This is the same discipline as unit testing, applied to prompts.

Test streaming locally too

If your production app streams responses, test the streaming behavior locally rather than assuming it matches the non-streaming output. A quick Node script with stream: true and reading chunks as they arrive will surface formatting issues (partial JSON, cut-off sentences) that only show up in streaming mode. Full details are in /docs/streaming.

const res = 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-3-5-sonnet",
    max_tokens: 500,
    stream: true,
    messages: [{ role: "user", content: "Write a one-line commit message for a typo fix." }]
  })
});

const reader = res.body.getReader();
const decoder = new TextDecoder();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  process.stdout.write(decoder.decode(value));
}

Test tool use locally before wiring it into your app

If your prompt triggers tool calls, test the tool-call logic in isolation first: send the request, print the raw tool_use block, and manually verify the arguments Claude extracted are correct before you connect it to a real function that writes to a database or calls an external service. This catches malformed arguments early, without side effects. See /docs/tools for the request/response shape.

Keep an eye on token usage while testing

Every response includes usage metadata (input and output token counts). Log it alongside your test output so you notice if a prompt change quietly doubles your token cost — that's easy to miss when you're only looking at the text output.

console.log(`tokens: in=${data.usage.input_tokens} out=${data.usage.output_tokens}`);

A note on API keys during testing

Use a separate key for local testing versus production if your provider supports it, and never commit the key to your repo — load it from an environment variable. If you're testing against SubToAPI, you can generate a dedicated sub_live_... key for local work from the dashboard and rotate it separately from your production key. Get started at /signup, or check /pricing if you're deciding between Solo and Team tiers for a growing test suite.

Putting it together

A solid local testing setup for Claude prompts looks like: a fixtures file with representative inputs, a small script that runs each fixture against the current prompt version, console output you can diff visually (or save to a file and diff against the previous run), and usage/token logging so cost regressions don't sneak in. None of this requires a UI or a deployed app — just a terminal and an API key. Full request and response reference is in /docs/messages, and a step-by-step first call is in /docs/quickstart.

Questions

Do I need a special SDK to test Claude prompts locally? No. A plain curl command or a fetch call in Node/Python is enough. SDKs help with larger apps but add no benefit for quick local iteration.

How do I avoid burning tokens while testing? Use short fixture inputs, cap max_tokens low during early iteration, and log token usage on every call so you catch expensive prompts before they reach production.

Can I test streaming and tool use the same way? Yes — the same local script pattern works, just set stream: true for streaming or inspect the tool_use block in the response for tool calls, as shown in /docs/streaming and /docs/tools.

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 →