Build a Web3 KYC Agent API

Build a FastAPI service where Claude screens blockchain wallets for fraud risk.

Introduction

30 Second Summary

Every time you sign up for a bank account or crypto exchange, someone (or something) checks whether you're trustworthy. In Web3, that check happens by analyzing a wallet's on-chain history for fraud, money laundering, and sanctions violations.

In this project, you will build an API where Claude acts as an intelligent compliance agent. It screens blockchain wallets against real forensic data and returns structured KYC decisions that any application can consume.

What You'll Build

Imagine sending any Ethereum wallet address to your API and getting back, in seconds, a clear PASS, REVIEW, or FAIL decision backed by real blockchain forensic intelligence from sanctions lists and 18 fraud categories.

By the end of this project, you'll have:

  • A running FastAPI endpoint where you submit any wallet address and watch Claude autonomously call a blockchain risk API, interpret the raw data, and return a structured KYC decision.
  • Hands-on experience with Claude's tool use pattern, the foundational mechanism behind all production AI agents, proven by seeing Claude go from helpless (no tools) to capable (with tools) in a single step.
  • A reusable REST API that any frontend, Discord bot, or authentication flow can integrate with a single POST request.
  • Secret Mission: Add batch screening and a supported-chains endpoint to make your API production-ready.

Are there any prerequisites?

This project assumes familiarity with Python and REST APIs. You'll need a free Anthropic API key (with $5 minimum credits loaded) and a free GetBlock API key (no credit card required, 5 requests per day).

Before We Start

Before we dive in, let's lock in what you're about to build and why it matters for Web3 applications.

Build Claude Without Tools

Your dependencies are installed and both API keys are ready. Now it's time to build the first version of your KYC screening endpoint and see what Claude can actually do with a wallet address.

Before giving Claude any tools, you need to witness what happens when it tries to assess a wallet using only its training data. This exposes the core limitation that tool use solves.

In this step, get ready to:
  • Create main.py with a FastAPI endpoint that sends wallet addresses to Claude.
  • Test the endpoint and observe Claude's limitation without real-time data access.
Create the FastAPI app and request model

Your app needs a FastAPI server, an Anthropic client to talk to Claude, and a request model that defines what data callers send in.

  • In Cursor, create a new file called main.py inside your web3-kyc-agent folder.
  • Add the app foundation by pasting this code:
import os
import anthropic
from fastapi import FastAPI
from pydantic import BaseModel

# Create the FastAPI application
app = FastAPI(title="Web3 KYC Agent API")

# Initialize the Anthropic client (reads ANTHROPIC_API_KEY from .env)
client = anthropic.Anthropic()

# Define the request body shape for screening requests
class KYCRequest(BaseModel):
    wallet_address: str
    network: str = "ETH"

What does this code do?

  • FastAPI(title="Web3 KYC Agent API") creates a web server that automatically generates interactive API docs.
  • anthropic.Anthropic() creates a client that reads your ANTHROPIC_API_KEY from the environment automatically.
  • KYCRequest is a Pydantic model that validates incoming requests. It requires a wallet_address string and defaults network to "ETH".
Add the screening endpoint

The endpoint sends the wallet address to Claude with a system prompt asking it to assess risk. Crucially, it gives Claude no tools to look up real data.

  • Below the KYCRequest class in main.py, add the endpoint function:
@app.post("/kyc/screen")
def screen_wallet_endpoint(request: KYCRequest):
    """Screen a wallet address and return a KYC decision."""
    messages = [
        {
            "role": "user",
            "content": f"Screen this wallet address for KYC compliance: {request.wallet_address} on the {request.network} network.",
        }
    ]

    # Send to Claude with NO tools available
    response = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=1024,
        system="You are a Web3 KYC compliance agent. Assess the given wallet address for risk.",
        messages=messages,
    )

    # Return Claude's raw text response
    return {"response": response.content[0].text}

What does this code do?

  • The @app.post("/kyc/screen") decorator creates a POST endpoint at /kyc/screen.
  • The messages list sends the wallet address to Claude as a user message asking for a compliance screening.
  • client.messages.create() calls Claude claude-haiku-4-5 with a short system prompt but no tools parameter. Claude has no way to fetch real blockchain data.
  • Save main.py.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file before starting the server.

ⓧ I'd like to double check the full code

import os
import anthropic
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI(title="Web3 KYC Agent API")

client = anthropic.Anthropic()

class KYCRequest(BaseModel):
    wallet_address: str
    network: str = "ETH"

@app.post("/kyc/screen")
def screen_wallet_endpoint(request: KYCRequest):
    """Screen a wallet address and return a KYC decision."""
    messages = [
        {
            "role": "user",
            "content": f"Screen this wallet address for KYC compliance: {request.wallet_address} on the {request.network} network.",
        }
    ]

    response = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=1024,
        system="You are a Web3 KYC compliance agent. Assess the given wallet address for risk.",
        messages=messages,
    )

    return {"response": response.content[0].text}
Test the endpoint and observe Claude's limitation

FastAPI automatically generates interactive API docs at /docs using Swagger UI. You can test your endpoint directly in the browser without needing a separate tool.

  • Start the FastAPI development server by running this command in your terminal:
fastapi dev main.py

What does this command do?

fastapi dev main.py starts a development server with auto-reload. It also loads your .env file automatically so Claude can read your API key.

  • You should see output in your terminal showing the server running at http://127.0.0.1:8000.
  • Open your browser and navigate to http://127.0.0.1:8000/docs to access the interactive Swagger UI.
  • Click the POST /kyc/screen row to expand it.
  • Click Try it out.
  • Replace the request body with this JSON:
{
  "wallet_address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
  "network": "ETH"
}

Before you click Execute, think about this: Claude has been told to assess this wallet for risk, but it has no tools to look up blockchain data. What do you think Claude will say?

  • Click Execute.

Scroll down to the response body. Claude responds acknowledging it cannot access real-time blockchain data and cannot make a reliable KYC determination.

Why does Claude refuse?

Claude knows it has no way to verify on-chain activity, sanctions status, or transaction history for a live wallet address. Its training data is static and outdated for compliance purposes.

This is the exact gap that tool use fills. In the next step, you'll give Claude a real blockchain risk-scoring tool so it can fetch live forensic data and make an actual KYC decision.

Not seeing a response from Claude?

  • Check your terminal for errors. If you see AuthenticationError, your ANTHROPIC_API_KEY in .env may be incorrect or missing credits.
  • Make sure your .env file is in the same folder as main.py (the web3-kyc-agent directory).
  • Confirm the server is still running in your terminal. If it crashed, restart it with fastapi dev main.py.

Still stuck? Help me debug why my FastAPI endpoint isn't returning a response from Claude.

You've just proven the problem. Claude is smart enough to know it needs real data, but without tools, it's stuck giving disclaimers instead of decisions. Next up, you'll give Claude a real blockchain screening tool and watch it transform from helpless to capable.

Give Claude the Wallet Screening Tool

In the last step, Claude admitted it cannot verify real-time blockchain data. It knows what KYC means, but without access to actual on-chain forensics, it cannot make a reliable decision.

Now you fix that gap by giving Claude a tool it can call autonomously. This is the pattern behind every production AI agent: define a tool, hand it to the model, and let the model decide when to use it.

In this step, get ready to:
  • Define the wallet screening tool schema that Claude can invoke.
  • Write the function that calls GetBlock's risk API when Claude requests it.
  • Implement the tool-use loop so Claude can act on real blockchain data.
Define the screen_wallet tool and execution function

Claude's tool use feature works like a contract. You describe a tool with a JSON schema (its name, what it does, what inputs it needs), and Claude decides at runtime whether to call it.

  • In main.py, find the imports at the top of the file:
import os
import anthropic
from fastapi import FastAPI
from pydantic import BaseModel
  • Replace those import lines with the following expanded imports:
import os
import json
import httpx
import anthropic
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

What are the new imports for?

  • json lets you serialize and deserialize API responses.
  • httpx is the HTTP client you will use to call the GetBlock API. It is already installed as a dependency of the anthropic SDK.
  • HTTPException lets you return proper error responses from your endpoint.
  • Below the client = anthropic.Anthropic() line, add the GetBlock API key variable and the tool definition:
GETBLOCK_API_KEY = os.environ.get("GETBLOCK_API_KEY", "your-getblock-api-key-here")

wallet_screening_tool = {
    "name": "screen_wallet",
    "description": "Screen a blockchain wallet address for fraud risk, sanctions, and AML violations using on-chain behavioral analysis. Returns a fraud probability score, forensic risk flags across 18 categories, and sanction status.",
    "input_schema": {
        "type": "object",
        "properties": {
            "address": {
                "type": "string",
                "description": "The blockchain wallet address to screen",
            },
            "network": {
                "type": "string",
                "description": "The blockchain network identifier",
                "enum": ["ETH", "BNB", "POLY", "TRON", "BASE"],
            },
        },
        "required": ["address", "network"],
    },
}

What does this tool definition do?

This dictionary is the contract between your code and Claude. Claude reads the name, description, and input_schema to understand what the tool does and what arguments it needs.

When Claude decides a wallet needs screening, it generates a structured request with the address and network fields filled in. Your code then executes the actual API call on Claude's behalf.

  • Save main.py.
  • Check your terminal where the dev server is running. The server auto-reloads and you should see Application startup complete with no errors.

Seeing an import error for httpx?

The httpx package is included as a dependency of fastapi[standard]. If you installed plain fastapi without the [standard] extra, run pip install httpx in your terminal to fix it.

help me fix my httpx import error in my FastAPI project

  • Below the wallet_screening_tool dictionary, add the system prompt that tells Claude how to make KYC decisions:
SYSTEM_PROMPT = """You are a Web3 KYC compliance agent. Your role is to assess blockchain wallet addresses for risk.

When a user submits a wallet address for screening:
1. Use the screen_wallet tool to retrieve real-time on-chain risk data
2. Analyze the fraud probability, forensic flags, and sanction status
3. Make a KYC decision based on the following thresholds:

Decision criteria:
- PASS: Fraud probability below 0.3, not sanctioned, no critical forensic flags triggered
- REVIEW: Fraud probability between 0.3 and 0.7, OR minor forensic flags present (gas_abuse, blacklist_doubt, fake_standard_interface)
- FAIL: Fraud probability above 0.7, OR wallet is sanctioned, OR critical forensic flags triggered (money_laundering, cybercrime, sanctioned, stealing_attack, darkweb_transactions, phishing_activities)

Always respond with your analysis."""

Why spell out decision criteria in the prompt?

Claude is powerful, but it needs clear thresholds to make consistent decisions. Without explicit criteria, Claude might PASS a wallet one request and REVIEW it the next. Pinning the thresholds (0.3, 0.7) and naming the critical flags gives you reproducible compliance logic.

  • Below the SYSTEM_PROMPT, add the function that actually calls the GetBlock API when Claude requests a screening:
def execute_wallet_screening(address: str, network: str) -> str:
    """Call GetBlock Wallet Risk API and return the response as a string."""
    url = "https://services.getblock.io/v1/wallet-audit/check"
    headers = {
        "Authorization": f"Bearer {GETBLOCK_API_KEY}",
        "Content-Type": "application/json",
    }
    payload = {"network": network, "address": address}

    response = httpx.post(url, headers=headers, json=payload, timeout=30.0)

    if response.status_code != 200:
        return json.dumps(
            {"error": f"Screening API returned status {response.status_code}"}
        )

    return response.text

What does this function do?

  • It sends a POST request to GetBlock's POST /v1/wallet-audit/check endpoint with the wallet address and network.
  • The Authorization: Bearer header authenticates with your GetBlock API key.
  • If the call succeeds, it returns the raw JSON text. If it fails, it returns a JSON error message that Claude can interpret.
  • Save main.py.
  • Confirm the server reloads in your terminal with no errors.

Server showing a syntax error?

Check that the SYSTEM_PROMPT string uses triple quotes (""") and that the wallet_screening_tool dictionary has matching braces. A missing comma or unmatched bracket is the most common cause.

help me find the syntax error in my main.py

Update the endpoint with the tool-use loop

The tool and function are defined, but your endpoint still calls Claude without passing any tools. Now you wire them together using the tool-use loop pattern.

  • In main.py, find the existing screen_wallet_endpoint function (starting at @app.post("/kyc/screen")).
  • Delete the entire function (from the decorator through the return statement).
  • Replace it with this updated version that passes the tool to Claude:
@app.post("/kyc/screen")
def screen_wallet_endpoint(request: KYCRequest):
    """Screen a wallet address and return a KYC decision."""
    messages = [
        {
            "role": "user",
            "content": f"Screen this wallet address for KYC compliance: {request.wallet_address} on the {request.network} network.",
        }
    ]

    response = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=1024,
        system=SYSTEM_PROMPT,
        tools=[wallet_screening_tool],
        messages=messages,
    )

What changed in the API call?

  • system=SYSTEM_PROMPT now uses your detailed prompt with KYC decision thresholds.
  • tools=[wallet_screening_tool] tells Claude this tool is available. Claude can now choose to call it when it needs real data.
  • Directly below the client.messages.create() call (still inside the function), add the tool-use loop:
    while response.stop_reason == "tool_use":
        tool_use = next(
            block for block in response.content if block.type == "tool_use"
        )

        tool_result = execute_wallet_screening(
            address=tool_use.input["address"],
            network=tool_use.input["network"],
        )

        messages.append({"role": "assistant", "content": response.content})
        messages.append(
            {
                "role": "user",
                "content": [
                    {
                        "type": "tool_result",
                        "tool_use_id": tool_use.id,
                        "content": tool_result,
                    }
                ],
            }
        )

        response = client.messages.create(
            model="claude-haiku-4-5",
            max_tokens=1024,
            system=SYSTEM_PROMPT,
            tools=[wallet_screening_tool],
            messages=messages,
        )

How does the tool-use loop work?

This is the core pattern behind all Claude-powered agents. When Claude decides it needs to use a tool, its response has a stop_reason of "tool_use" instead of "end_turn".

The loop extracts Claude's tool request, executes your function with the arguments Claude chose, sends the result back as a tool_result message, and asks Claude to continue. Claude then interprets the real data and responds with its analysis.

  • After the while loop (still inside the function), add the final text extraction and return:
    final_text = next(
        (block.text for block in response.content if block.type == "text"),
        None,
    )

    if not final_text:
        raise HTTPException(status_code=500, detail="No response from KYC agent")

    return {"response": final_text}
  • Save main.py.
  • Check the terminal. The server should auto-reload and show Application startup complete with no errors.

Server crashing with IndentationError?

The while loop and final_text extraction must be indented inside the screen_wallet_endpoint function (4 spaces in from the function definition). Make sure you did not accidentally place them at the module level.

help me fix the indentation in my endpoint function

Test the upgraded agent

Your agent now has the full pipeline: tool definition, execution function, and the loop that connects them. Time to see the difference.

  • Open http://127.0.0.1:8000/docs in your browser.
  • Click on the POST /kyc/screen endpoint, then click Try it out.
  • Enter this request body:
{
  "wallet_address": "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
  "network": "ETH"
}

Before you click Execute, think about what you expect to happen this time. Last step, Claude said it could not verify real-time data. What should be different now that it has the screening tool?

  • Click Execute.

Watch your terminal while the request processes. You should see Claude requesting the screen_wallet tool, the GetBlock API response with forensic data, and Claude generating its final KYC analysis based on real on-chain intelligence.

In the Swagger UI response body, Claude now returns a detailed analysis with fraud probability, forensic flags, and a KYC determination. The agent is alive.

What just happened?

Claude autonomously decided to call your screen_wallet tool, passed in the address and network, received real forensic data from GetBlock, and then interpreted 18 risk categories plus sanctions data to produce a KYC decision.

This is the fundamental difference between a chatbot and an agent. A chatbot generates text from training data. An agent takes action in the real world and reasons about the results.

Getting a 500 error or empty response?

  • Check that your GETBLOCK_API_KEY in .env is correct. The free tier allows 5 requests per day.
  • Make sure ANTHROPIC_API_KEY in .env is valid and has credits loaded.
  • Verify the while loop and final_text extraction are both inside the screen_wallet_endpoint function, not at module level.

help me debug why my KYC agent is returning a 500 error

✔️ Awesome, I've got everything!

Great. Make sure you have saved main.py and the server is running without errors.

ⓧ I'd like to double check the full code

Your main.py is over 30 lines, so confirm each section is present in this order:

  • Imports: os, json, httpx, anthropic, FastAPI, HTTPException, BaseModel.
  • app and client initialization.
  • GETBLOCK_API_KEY read from environment.
  • wallet_screening_tool dictionary with name, description, and input_schema.
  • SYSTEM_PROMPT with decision criteria (PASS/REVIEW/FAIL thresholds).
  • execute_wallet_screening() function calling GetBlock's API.
  • KYCRequest model with wallet_address and network.
  • screen_wallet_endpoint function with tools=[wallet_screening_tool] in the API call, the while response.stop_reason == "tool_use" loop, and final_text extraction at the end.

Your agent can now screen any wallet on five blockchains using real forensic data. Next up, you will structure Claude's free-text response into predictable JSON that any application can consume.

Structure the KYC Decision Output

In the last step, Claude returned a free-form text response explaining its KYC analysis. That's fine for reading in a terminal, but any real application consuming this API needs predictable, structured JSON.

A React frontend, a Discord bot, or an authentication flow all need to parse the response programmatically. In this step, you'll add a response model so every consumer gets a consistent PASS/REVIEW/FAIL decision with typed fields.

In this step, get ready to:
  • Define a KYCDecision Pydantic model for structured output.
  • Update the system prompt and endpoint to return validated JSON.
  • Add a health check endpoint and test the full flow end-to-end.
Define the KYCDecision response model

Right now your endpoint returns a plain dictionary with a response key containing whatever text Claude produced. You need a Pydantic model that enforces the exact shape every consumer expects.

  • In main.py, find the KYCRequest class and add the following KYCDecision model directly below it:
class KYCDecision(BaseModel):
    decision: str
    risk_score: float
    reasoning: str
    flags: list[str]

What does this model do?

  • decision holds the KYC verdict: "PASS", "REVIEW", or "FAIL".
  • risk_score is the fraud probability as a float (0.0 to 1.0).
  • reasoning gives Claude's human-readable explanation of the decision.
  • flags lists any forensic risk categories that were triggered.
  • Save main.py.
  • Check the terminal where your FastAPI server is running. It should auto-reload with no errors.

Seeing a syntax error on reload?

  • Make sure the new class is placed after the KYCRequest class and before the @app.post decorator.
  • Check that you typed list[str] with a lowercase l. Python 3.9+ supports this syntax natively.

Help me fix a syntax error after adding my KYCDecision model.

Update the prompt and endpoint to return structured JSON

The response model defines what Python validates, but Claude still needs explicit instructions to output JSON in that exact shape. You'll update both the system prompt and the endpoint decorator.

  • In main.py, scroll to the SYSTEM_PROMPT string and find this line at the very end:
Always respond with your analysis."""
  • Replace that line with the following JSON instruction:
Always respond with ONLY a JSON object (no markdown, no code fences) containing:
{"decision": "PASS" or "REVIEW" or "FAIL", "risk_score": <fraud probability as float>, "reasoning": "<brief explanation>", "flags": ["<list of triggered forensic flags>"]}"""

Why instruct Claude this explicitly?

LLMs tend to wrap JSON in markdown code fences or add explanatory text before it. The "no markdown, no code fences" instruction prevents that so json.loads() can parse the output directly.

  • Now find the endpoint decorator line:
@app.post("/kyc/screen")
  • Replace it with this version that declares the response model:
@app.post("/kyc/screen", response_model=KYCDecision)

What does response_model do?

FastAPI uses response_model to validate, serialize, and document your endpoint's output. The Swagger UI at /docs will now show the exact JSON schema any consumer can expect.

Now you need to replace the raw return statement with JSON parsing logic that validates Claude's output against your model.

  • Scroll to the bottom of the screen_wallet_endpoint function and find these lines:
    if not final_text:
        raise HTTPException(status_code=500, detail="No response from KYC agent")

    return {"response": final_text}
  • Replace the return {"response": final_text} line (keep the if not final_text check) with this JSON parsing block:
    try:
        result = json.loads(final_text)
        return KYCDecision(**result)
    except (json.JSONDecodeError, TypeError, KeyError):
        return KYCDecision(
            decision="REVIEW",
            risk_score=0.5,
            reasoning=final_text,
            flags=[],
        )

What does this code do?

  • json.loads(final_text) parses Claude's text response into a Python dictionary.
  • KYCDecision(**result) validates the dictionary against your model, ensuring all four fields exist with the correct types.
  • The except block is a safety net. If Claude returns malformed JSON, your API still responds with a REVIEW decision instead of crashing.
  • Save main.py.
  • Check that the server reloads successfully in your terminal.

Before you test, what do you think the response will look like now compared to the raw text from the previous step?

  • Open your browser to http://127.0.0.1:8000/docs.
  • Click the POST /kyc/screen endpoint to expand it.
  • Click Try it out.
  • Enter an Ethereum address in the wallet_address field (for example, 0x0000000000000000000000000000000000000000).
  • Click Execute.

You should see a clean JSON response with decision, risk_score, reasoning, and flags fields instead of the freeform text you saw before.

Getting a 500 Internal Server Error?

  • Check your terminal for the traceback. If it mentions json.loads, make sure your SYSTEM_PROMPT includes the "no markdown, no code fences" instruction. Without it, Claude may wrap the JSON in backticks.
  • If the error mentions KYCDecision, confirm it is defined before the endpoint function in your file.
  • If you see a ValidationError from Pydantic, the except block might not be catching the right exception. Double-check you have all three exception types: json.JSONDecodeError, TypeError, KeyError.

Help me debug my KYC endpoint returning a 500 error after adding the response model.

Add a health check endpoint

Production APIs need a lightweight endpoint that monitoring tools can ping to confirm the service is alive.

  • At the very bottom of main.py, add this health check endpoint:
@app.get("/health")
def health_check():
    return {"status": "healthy", "service": "web3-kyc-agent"}

What does this endpoint do?

The /health endpoint returns a simple JSON object confirming the service is running. Load balancers and uptime monitors use endpoints like this to detect outages without triggering expensive AI calls.

  • Save main.py.
  • In your browser, visit http://127.0.0.1:8000/health.

You should see {"status":"healthy","service":"web3-kyc-agent"} in the browser.

Not seeing the health response?

  • Make sure @app.get("/health") is at the module level (not indented inside another function).
  • Confirm the server reloaded by checking your terminal for the reload log message.

Help me figure out why my /health endpoint isn't returning a response.

✔️ Awesome, I've got everything!

Great. Double check you've saved main.py and that the server is running without errors.

ⓧ I'd like to double check the full code

Your main.py should contain these sections in order. Scroll through your file and confirm each is present:

  • Imports (os, json, httpx, anthropic, FastAPI, HTTPException, BaseModel).
  • App creation and client setup.
  • GETBLOCK_API_KEY environment variable.
  • wallet_screening_tool dictionary with the tool schema.
  • SYSTEM_PROMPT ending with the JSON instruction (not "Always respond with your analysis").
  • execute_wallet_screening() function.
  • KYCRequest model.
  • KYCDecision model with decision, risk_score, reasoning, flags.
  • @app.post("/kyc/screen", response_model=KYCDecision) endpoint with the tool-use loop and try/except JSON parsing at the end.
  • @app.get("/health") endpoint at the bottom.

Your KYC agent now returns structured, predictable JSON that any application can consume directly. Whether a React frontend or a Telegram bot calls this endpoint, they'll always get a consistent response shape they can parse without guesswork.

Secret mission

Batch Screening and Chain Discovery

Your KYC agent can screen one wallet at a time. A real compliance service needs to handle multiple wallets in a single request and tell consumers which blockchain networks are supported. Can you extend the API to handle both?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. Both APIs are pay-per-use and cost nothing when idle, so there are no ongoing charges while the server is stopped.

Resources you used:

  • Anthropic API account with credits (pay-per-use, pennies consumed).
  • GetBlock account with free API key (no charges, 5 requests/day).
  • Local web3-kyc-agent project folder on your Desktop (includes virtual environment, main.py, and .env).

Keep everything running

No action needed. Choose this if you are still actively building or experimenting with the KYC agent.

  • The Anthropic API only charges when you send requests. No requests means no cost.
  • The GetBlock free tier renews 5 requests daily at no charge.
  • Your local project files remain on your Desktop and use minimal disk space.

Pause - I'll come back to this later

Shut down running processes to free up memory. Your files and API keys stay intact for when you return.

  • Switch to the terminal where the FastAPI dev server is running and press Ctrl+C to stop it.
  • Deactivate your virtual environment by running:
deactivate
  • Your API keys remain valid. You can reactivate the virtual environment and restart the server whenever you return.

Delete - I don't want to use this again

Remove all project resources and revoke your API keys. This cannot be undone.

  • Stop the FastAPI dev server by pressing Ctrl+C in the terminal where it is running.
  • Delete the project folder from your Desktop by running:

macOS

cd ~/Desktop
rm -rf web3-kyc-agent

Windows

cd ~\Desktop
Remove-Item -Recurse -Force web3-kyc-agent
  • Delete your Anthropic API key. Open console.anthropic.com in your browser, navigate to Settings, then API Keys. Locate the key you created for this project and delete it.
  • Delete your GetBlock API key. Open account.getblock.io in your browser, go to the Address Audit product, open the API Key tab, click the three-dot icon next to your key, and select the option to delete it.

Nice Work!

Nice Work!

You just built a fully functional AI-powered KYC agent API from scratch. Send it any blockchain wallet address, and Claude autonomously screens it against real forensic data and returns a structured compliance decision.

You've learned how to:

  • Build a FastAPI service where Claude acts as an intelligent compliance agent, autonomously deciding when to call external tools and interpreting raw data into actionable decisions.
  • Implement Claude's tool use pattern: define a tool schema, detect stop_reason == "tool_use", execute the tool, send the result back, and loop until Claude responds with text.
  • Integrate real blockchain forensics from GetBlock's Wallet Risk API, screening wallets across 18 fraud categories and sanctions lists (OFAC/EU/UN) to produce structured PASS/REVIEW/FAIL decisions.
  • Secret Mission: Added batch screening and a chain discovery endpoint to make the API production-ready.

Ready to quiz yourself?