Tickd.ai
← The Tickd Guide

Tutorials & Guides

How to Build a Schema-First Mock API Generator Using Claude 3.5 Sonnet and FastAPI

Stop wasting hours hardcoding mock JSON files for your frontend prototypes. Learn how to build a dynamic, schema-first mock API generator using Claude 3.5 Sonnet and FastAPI that generates realistic mock data on the fly.

Updated 9/25/2026

The Problem with Static Mock Data

If you have ever spent a Tuesday evening manually writing mock JSON objects for a frontend dashboard, you know the pain. You start with good intentions, typing out a few users: John Doe, Jane Smith, and maybe Testy McTestface. But by the time you need to simulate a nested, paginated list of modern financial transactions with realistic merchant names, timestamps, and ISO currency codes, your brain turns to mush. You end up with repetitive data that fails to stress-test your UI components.

Static mock tools are rigid. If your schema changes, you have to rewrite your static files. If you want to test edge cases—like extremely long strings, null states, or internationalised characters—you have to write even more boilerplate.

We can do better. By combining FastAPI with the spatial reasoning and structural accuracy of Claude 3.5 Sonnet (which you can learn more about on our [/platforms/claude] hub), we can build an on-demand mock API generator. It accepts any standard JSON schema, talks to Claude, and spits back perfectly formatted, contextually rich mock data that conforms exactly to your data types.

The Architecture

Our tool is going to run a local lightweight FastAPI server. It will expose a wild-card route that intercepts requests, checks if you have registered a JSON schema for that path, and if so, asks Claude to generate data matching that schema.

To make this fast and save on your API bill, we will implement a simple disk-based caching system. This ensures that once Claude generates a set of mock users or transactions, they stay cached on your local machine until you explicitly tell the server to refresh them.

Step 1: Setting Up Your Environment

First, let's get our Python virtual environment configured. Create a new directory and install the necessary dependencies:

`bash mkdir schema-mock-api cd schema-mock-api python3 -m venv venv source venv/bin/activate pip install fastapi uvicorn anthropic pydantic `

Make sure you have your Anthropic API key exported to your environment:

`bash export ANTHROPIC_API_KEY="your-api-key-here" `

Step 2: Designing the Dynamic Route Engine

We will create a file named main.py. This script will load JSON schemas from a local ./schemas directory. When you query GET /api/users, the app will look for ./schemas/users.json, pass it to Claude, and return the mock data.

Let’s start by writing the skeleton of our FastAPI server and the utility function that communicates with Claude 3.5 Sonnet. To get the best structural reliability, we will use Anthropic's system prompt features to enforce JSON formatting. If you need inspiration for structuring complex JSON templates, our [/prompts] builder has excellent blueprints for formatting.

`python import os import json from pathlib import Path from fastapi import FastAPI, HTTPException, Request from fastapi.responses import JSONResponse from anthropic import Anthropic

app = FastAPI(title="Dynamic Mock API Engine") client = Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

SCHEMA_DIR = Path("./schemas") CACHE_DIR = Path("./cache") SCHEMA_DIR.mkdir(exist_ok=True) CACHE_DIR.mkdir(exist_ok=True) `

Step 3: Structuring the LLM Prompt

To ensure Claude does not output conversational filler (like "Here is your JSON:"), we must use a strict system prompt. We want raw, valid JSON that strictly adheres to the provided schema.

Here is how we will structure our generator function:

`python def generate_mock_data(schema_name: str, schema_content: dict, count: int = 5) -> dict: prompt = f""" You are a precise mock data generation engine. Generate an array containing exactly {count} realistic data items that strictly conform to the following JSON Schema: {json.dumps(schema_content, indent=2)} Guidelines for high fidelity: 1. Use highly realistic, varied values. Do not repeat names or dates. 2. Ensure relations make logical sense (e.g., timestamps should be sequential, status codes should match standard workflows). 3. Return ONLY valid JSON as a direct array. Do not write markdown formatting, blockquotes, or introductory text. Start your response directly with [ and end with ]. """ try: message = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=4000, temperature=0.7, system="You output pure JSON arrays matching schemas. You write zero conversational text.", messages=[{"role": "user", "content": prompt}] ) # Parse the output safely response_text = message.content[0].text.strip() # Strip markdown block wraps if Claude occasionally hallucinates them if response_text.startswith("`json"): response_text = response_text.split("`json")[1].split("`")[0].strip() elif response_text.startswith("`"): response_text = response_text.split("`")[1].split("`")[0].strip() return json.loads(response_text) except Exception as e: raise HTTPException(status_code=500, detail=f"LLM generation failed: {str(e)}") `

If you find Claude's output formatting is throwing unexpected parser exceptions, take a look at our troubleshooting tips in [/platforms/claude/articles] to handle tricky tokenisations.

Step 4: The Wildcard API Endpoint

Now, we create a dynamic route handler that captures all GET requests directed to /api/{endpoint}. This handler checks if a schema file named {endpoint}.json exists, handles caching to keep our API spend low, and serves the generated mock data.

`python @app.get("/api/{endpoint}") async def handle_mock_endpoint(endpoint: str, count: int = 5, refresh: bool = False): schema_path = SCHEMA_DIR / f"{endpoint}.json" cache_path = CACHE_DIR / f"{endpoint}_{count}.json" if not schema_path.exists(): raise HTTPException( status_code=404, detail=f"Schema '{endpoint}.json' not found in ./schemas/ directory. Please define it first." ) # Read schema with open(schema_path, "r") as f: try: schema_content = json.load(f) except json.JSONDecodeError: raise HTTPException(status_code=400, detail="Invalid JSON formatting inside your schema file.") # Check Cache first if cache_path.exists() and not refresh: with open(cache_path, "r") as f: return json.load(f) # Generate new mock data via Claude data = generate_mock_data(endpoint, schema_content, count=count) # Write to local cache with open(cache_path, "w") as f: json.dump(data, f, indent=2) return data `

Step 5: Testing with a Sample Schema

Let’s put this system to the test. Create a schema directory and add a new database schema file for a mock SaaS analytics platform.

`bash mkdir schemas touch schemas/metrics.json `

Paste the following JSON schema into schemas/metrics.json:

`json { "type": "object", "properties": { "id": { "type": "string", "pattern": "^uuid_[a-f0-9]{32}$" }, "metricName": { "type": "string", "enum": ["cpu_utilisation", "memory_leak_bytes", "active_websocket_connections"] }, "value": { "type": "number" }, "status": { "type": "string", "enum": ["nominal", "warning", "critical"] }, "capturedAt": { "type": "string", "format": "date-time" } }, "required": ["id", "metricName", "value", "status", "capturedAt"] } `

Now, run your FastAPI server:

`bash uvicorn main:app --reload `

Navigate to http://127.0.0.1:8000/api/metrics?count=3 in your browser. You will see three highly realistic, structured mock metrics conforming exactly to your pattern constraints and status options.

This workflow keeps your prototyping pipeline ticking along without hitting rate limits. To update the values or trigger fresh mock datasets, simply pass the ?refresh=true parameter in your API query, and Claude will re-evaluate the schemas and populate your local storage cache with brand new instances.

Next Steps

This simple setup is incredibly easy to expand. You can add default schemas to your codebase to model everything from nested comments to multi-currency payment intents. Because Claude 3.5 Sonnet understands standard JSON schemas out of the box, you can feed complex types and constraints straight to the endpoint, saving you hours of tedious configuration.

claudefastapipythontutorialsdev-workflow

Keep going

Build something with the prompt generator, decode the jargon in the glossary, or compare the tools on our platform deep-dives.