Skip to content

Why Does Claude Code Hallucinate APIs? And How to Fix It

Problem

Claude Code kept inventing APIs that didn’t exist. I’d ask it to implement a feature, and it would generate confident, well-structured code using methods that simply weren’t there. The code looked perfect, passed syntax checks, and would crash immediately at runtime.

The frustration came from specific patterns I kept seeing:

  • Plausible method names: getUserByEmail() instead of findByEmail()
  • Confident presentation: The code looked professionally written
  • Runtime surprises: Everything compiled, nothing worked
  • Wasted debugging: I’d spend hours hunting for methods that didn’t exist

I needed to understand why this happened and how to prevent it.

What Happened?

Let me walk through a typical scenario.

I asked Claude to implement a login function for my application:

Implement a login function that authenticates users by email and returns a JWT token.

Claude generated this code:

Hallucinated API (Wrong)
// Claude Code generated this - looks correct but it's wrong
const user = await UserAPI.getUserByEmail(email);
await UserAPI.saveUser(user);
const token = AuthAPI.generateToken(user.id);

The code looked reasonable. Clean variable names, proper async/await, good structure. I committed it.

Then I ran the tests. They failed. The actual API looked like this:

Actual API (Correct)
// Real API signatures from my codebase
const user = await UserAPI.findByEmail(email);
await UserAPI.update(user.id, { lastLogin: new Date() });
const token = AuthAPI.createToken({ userId: user.id, role: user.role });

Claude had invented three methods that didn’t exist: getUserByEmail, saveUser, and generateToken. Each one looked plausible. None of them worked.

When I asked Claude about this, it apologized and “fixed” the code—introducing two new hallucinated methods.

Why Claude Hallucinates APIs

I started researching and experimenting. The causes became clear.

Insufficient Context Grounding

Claude Code operates on training data patterns rather than real-time documentation. When I asked for a login function, it generated what a typical login function might look like based on patterns it learned from millions of codebases.

Claude's reasoning (reconstructed):
"I've seen thousands of login implementations. They usually have:
- A method to find users by email → getUserByEmail seems right
- A way to save user state → saveUser is common
- Token generation → generateToken is standard"
None of these existed in MY codebase.

Pattern Matching Over Verification

The model generates code that “looks right” based on learned patterns. A method name like getUserByEmail() seems plausible for a user service, even if my actual API uses findByEmail().

Pattern: UserAPI + email lookup → getUserByEmail() [HALLUCINATED]
Reality: UserAPI + email lookup → findByEmail() [ACTUAL]

The problem is that Claude optimizes for plausible completions, not verified accuracy.

Lost State Across Edits

When making multiple changes across several files, Claude loses track of earlier decisions. In one session, it used findByEmail in file A, then invented getUserByEmail in file B—both for the same API.

// fileA.ts - Claude correctly used actual API
const user = await UserAPI.findByEmail(email);
// fileB.ts - Same session, different file, hallucinated method
const existing = await UserAPI.getUserByEmail(email); // Wrong!

Confidence Without Validation

The most frustrating aspect isn’t the errors themselves, but the confident presentation. Code appears well-structured and follows best practices, making hallucinated APIs blend in naturally.

Hallucinated code looks professional
// This looks like production-ready code:
const sanitized = Validator.sanitizeEmail(email);
const hashed = await CryptoService.hashPassword(password);
const session = SessionManager.create(user);
// But none of these methods exist in my codebase.
// They're all plausible inventions.

How I Solved It

I implemented a set of practices that dramatically reduced API hallucination.

1. Provide Explicit API Documentation

Before requesting code generation, I now supply the actual API signatures:

Use only these UserAPI methods:
- findById(id: string): Promise<User | null>
- findByEmail(email: string): Promise<User | null>
- create(data: CreateUserDTO): Promise<User>
- update(id: string, data: UpdateUserDTO): Promise<User>
Use only these AuthAPI methods:
- createToken(payload: TokenPayload): Promise<string>
- validateToken(token: string): Promise<TokenPayload | null>
- revokeToken(token: string): Promise<void>
Do not invent or assume any other methods exist.

This reduced hallucination significantly because Claude has concrete ground truth to work from.

2. Use MCP Servers for Real-Time Validation

Model Context Protocol (MCP) servers provide live documentation access. I configured:

+------------------+ +------------------+ +------------------+
| Claude Code | --> | MCP Server | --> | Actual API |
| requests code | | validates | | documentation |
+------------------+ +------------------+ +------------------+
| | |
v v v
"Implement login" "Check method exists" "findByEmail exists"
"Return verified API" "getUserByEmail doesn't"

Example MCP configuration for documentation access:

MCP Server Configuration
{
"mcpServers": {
"docs": {
"command": "mcp-docs-server",
"args": ["--project", "./docs/api"]
},
"context7": {
"command": "context7-mcp",
"args": ["--frameworks", "react,typescript"]
}
}
}

With MCP servers, Claude can verify APIs in real-time instead of guessing.

3. Implement Verification Checkpoints

After code generation, I ask Claude to verify:

After generating code, verify each external API call:
1. List all method calls made to external services
2. Cross-reference each with the provided documentation
3. Flag any methods not explicitly documented
4. Provide alternatives for any flagged methods

Example workflow:

+-------------------+ +-------------------+ +-------------------+
| Generate code | | List external | | Verify against |
| as usual | --> | method calls | --> | documentation |
+-------------------+ +-------------------+ +-------------------+
|
v
+-------------------+
| Hallucinated? |
| YES → Fix |
| NO → Accept |
+-------------------+

4. Scope Changes Deliberately

I break large requests into focused, verifiable tasks:

BAD: Implement the entire authentication flow
GOOD: Create a login function using these exact methods:
- UserAPI.findByEmail(email) to find the user
- PasswordHasher.verify(password, user.hash) to check password
- AuthAPI.createToken({ userId, role }) to generate token

Specificity eliminates ambiguity, which eliminates hallucination.

5. Create Skills with Correct API Patterns

I encode correct API patterns in reusable skills:

api-patterns.md (Skill file)
# API Patterns for This Project
## UserAPI
All user operations go through UserAPI.
### Available Methods
- findById(id: string) → User | null
- findByEmail(email: string) → User | null
- create(data: CreateUserDTO) → User
- update(id: string, data: UpdateUserDTO) → User
### Common Mistakes to Avoid
- Do NOT use getUserById, getUser, fetchUser, or any variant
- Do NOT use saveUser, updateUser directly - use update() method
- Email lookup always uses findByEmail, never getUserByEmail
## AuthAPI
Authentication token management.
### Available Methods
- createToken(payload: TokenPayload) → string
- validateToken(token: string) → TokenPayload | null
- revokeToken(token: string) → void
### Common Mistakes to Avoid
- Do NOT use generateToken, createJwt, or signToken
- Token payload must include userId and role fields

Now when I reference this skill, Claude has authoritative documentation.

Before and After

Here’s how my requests changed:

Before (hallucination-prone request):

Implement a login function for my application.

Claude invented methods, I wasted time debugging.

After (structured request):

Implement a login function with these exact APIs:
UserAPI:
- findByEmail(email: string): Promise<User | null>
- update(id: string, data: object): Promise<User>
AuthAPI:
- createToken(payload: { userId: string, role: string }): Promise<string>
PasswordHasher:
- verify(plain: string, hashed: string): Promise<boolean>
Requirements:
1. Find user by email using findByEmail
2. Verify password using PasswordHasher.verify
3. Update lastLogin using UserAPI.update
4. Return token from AuthAPI.createToken
Do not use any methods not listed above.

The structured request takes longer to write but eliminates hallucination entirely.

Common Mistakes I Made

Looking back, I see patterns in my failures.

Mistake 1: Assuming Claude Knows My Codebase

BAD: "Use the UserAPI to implement login" (Claude doesn't know what UserAPI has)
GOOD: "Use UserAPI.findByEmail and UserAPI.update to implement login"

Mistake 2: Skipping Verification

I now always run verification after accepting code:

List all external API calls in this code and verify each exists.

Mistake 3: Requesting Too Much at Once

BAD: "Implement the entire auth system"
GOOD: "Implement just the login function using these specific methods..."

Mistake 4: Not Updating Context After Changes

When my APIs change, I tell Claude:

API Update: UserAPI.saveUser is deprecated.
Use UserAPI.update instead for all modifications.

Mistake 5: Ignoring the Confidence Trap

Just because code looks professional doesn’t mean it’s correct. I verify unfamiliar API usage before accepting.

Verification Checklist

I now follow this checklist after every code generation:

[ ] List all external method calls
[ ] Cross-reference with actual API documentation
[ ] Verify method names match exactly (including case)
[ ] Verify parameter types match
[ ] Verify return types match
[ ] Check for deprecated methods
[ ] Run tests if available

Summary

In this post, I explained why Claude Code hallucinates APIs and how to prevent it. The key insight is that Claude generates code based on learned patterns, not verified knowledge of your specific APIs.

The practices that work:

  1. Explicit API Documentation — Provide exact method signatures before requesting code
  2. MCP Servers for Validation — Use real-time documentation access to verify APIs
  3. Verification Checkpoints — Ask Claude to list and verify all external calls
  4. Scoped Changes — Break large requests into focused, verifiable tasks
  5. Skills with API Patterns — Encode correct APIs in reusable skill files

Since implementing these practices, API hallucination dropped from a daily frustration to a rare occurrence. Claude Code went from an unreliable code generator to a trustworthy coding partner.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments