Skip to content
Riadh Mnasri
← Back to blog
3 min read

Claude Code is not the Claude API: what MissionPilot taught me by combining them

Claude Code is the tool I write this blog with. The Claude API is what MissionPilot, the tool that scores my freelance opportunities found on LinkedIn, Malt and Comet, calls in production, every time a user loads a new listing. Both use the same model, but they solve two completely different problems, and confusing them when designing a product leads to bad architecture decisions.

Two execution contexts, not a nuance#

Claude Code runs in my terminal, with access to my filesystem, my tools, my personal configuration. The Claude API runs server-side in MissionPilot, called by code I wrote, with no access to anything beyond what that code explicitly passes it in the request. The difference isn't one of degree, it's one of kind: one is a development environment augmented by an agent, the other is a service a product consumes the way it would consume any third-party API.

What that changes for prompt design#

Claude CodeAPI in MissionPilot
Who writes the promptMe, whenever I want, iterativelyThe code, on every request, identically
Available contextRepo files, session historyOnly what the code explicitly passes
Tolerance to variabilityHigh, I can rephrase if the result doesn't fitLow, the output must be machine-usable
Expected output formatFree text, code, discussionStructured, generally schema-validated JSON

The most important row is the last one. A badly formatted mission score silently breaks the display if nothing catches it: asking for structured output, with an explicit schema, isn't a convenience option on the API side, it's what makes the result usable by code without human supervision on every call.

A typical API call#

typescript
const response = await anthropic.messages.create({
  model: "claude-sonnet-4-5",
  max_tokens: 1024,
  system: "You score a freelance mission offer from 0 to 100 against the "
    + "user's stack. Respond only with valid JSON.",
  messages: [
    {
      role: "user",
      content: `Stack: ${userStack}\n\nOffer: ${missionDescription}`,
    },
  ],
});

Nothing exotic in that call. What separates a prototype from a reliable component of MissionPilot happens elsewhere: in validating the output, handling formatting errors, and how the product behaves when the call fails or returns something unexpected.

Structured output isn't a detail#

Tip

Never trust JSON generated by an LLM without validating it against an explicit schema before using it. A model can produce syntactically valid but semantically incomplete JSON (a missing field, an unexpected type), and it's almost never a prompting problem, it's a missing-validation problem on the code side.

On MissionPilot, every response goes through schema validation before reaching the interface. A score outside expected bounds, a missing field, and the response gets rejected rather than displayed as is. That's not paranoia, it's the difference between an API call that runs once in testing and one that runs every day unsupervised.

Cost, a design constraint from day one#

A Claude Code session I pay for once, in personal usage tokens. An API call inside MissionPilot repeats for every scanned offer, for every user. Per-call cost becomes a design constraint from the start: limiting the size of the context sent to what's genuinely needed for scoring, rather than shipping an entire raw job listing out of implementation convenience.

What this generalizes to#

Warning

Building a product on the assumption that the API will behave like Claude Code in an interactive session, with room to rephrase and iterate, leads to systematically underestimating the validation and error-handling work needed server-side.

The overlap between Claude Code and the Claude API stops at the underlying model. Everything else, handling uncertainty, output format, cost, error tolerance, depends entirely on the execution context. Designing a product that calls the API without having thought through those constraints upfront means treating a production service like an interactive terminal, and that shows up as bugs that never appear in manual testing, only in production.