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 offindByEmail() - 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:
// Claude Code generated this - looks correct but it's wrongconst 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:
// Real API signatures from my codebaseconst 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 APIconst user = await UserAPI.findByEmail(email);
// fileB.ts - Same session, different file, hallucinated methodconst 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.
// 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:
{ "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 services2. Cross-reference each with the provided documentation3. Flag any methods not explicitly documented4. Provide alternatives for any flagged methodsExample 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 tokenSpecificity eliminates ambiguity, which eliminates hallucination.
5. Create Skills with Correct API Patterns
I encode correct API patterns in reusable skills:
# API Patterns for This Project
## UserAPIAll 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
## AuthAPIAuthentication 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 fieldsNow 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 findByEmail2. Verify password using PasswordHasher.verify3. Update lastLogin using UserAPI.update4. 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 availableSummary
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:
- Explicit API Documentation — Provide exact method signatures before requesting code
- MCP Servers for Validation — Use real-time documentation access to verify APIs
- Verification Checkpoints — Ask Claude to list and verify all external calls
- Scoped Changes — Break large requests into focused, verifiable tasks
- 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:
- 👨💻 Anthropic Documentation: Claude Code Best Practices
- 👨💻 Model Context Protocol (MCP) Specification
- 👨💻 Reddit Discussion: Claude Code API Accuracy Issues
- 👨💻 API Documentation Best Practices
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments