Use draconic21 as a LangChain tool
LangChain has no official x402 integration in JS or Python. Both examples below wrap the same official x402 client libraries used elsewhere on this page as an ordinary LangChain tool — no unofficial third-party package required.
What's official and what isn't
Checked before writing this: the LangChain.js feature request for x402 support (langchainjs#11747) is still open and unmerged, and the Python equivalent (langchain#36306) is closed without an implementation. Several small third-party packages exist on PyPI/npm claiming x402-LangChain integration; none are official or vetted here, so this guide skips them in favor of a thin wrapper around the same official clients used elsewhere on this page.
Prerequisites
- A wallet on the Base network (chain id 8453) funded with a little USDC — a few dollars covers hundreds of calls at these prices.
- An x402 v2 client library (installed per snippet below) that can sign the "exact" EVM payment scheme (EIP-3009, gasless — no ETH for gas needed on your side).
- Your wallet's private key available to your own process/agent — never share it, never commit it, never paste it into a chat with an AI you don't fully trust with funds.
LangChain.js
npm install @langchain/core zod @x402/fetch @x402/core @x402/evm viem
// LangChain.js tool for a draconic21 x402 paid route.
//
// LangChain has no official x402 integration (checked: langchainjs issue
// #11747 requesting one is still open/unmerged as of writing). This wraps
// the same official `@x402/fetch` payment-aware fetch used in the plain
// TypeScript example as a LangChain `tool()` -- no extra glue package
// needed, since the 402/sign/retry logic already lives in the wrapped
// fetch, not in anything LangChain-specific.
//
// Install:
// npm install @langchain/core zod @x402/fetch @x402/core @x402/evm viem
//
// Standalone dry run (no LLM, no key -- prints the 402 challenge and stops):
// npx tsx tool.ts
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { x402Client } from "@x402/core/client";
import { wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
const ORIGIN = process.env.DRACONIC21_ORIGIN ?? "https://draconic21-x402-api.onrender.com";
function buildFetcher() {
const privateKey = process.env.BUYER_PRIVATE_KEY;
if (!privateKey) return null;
const account = privateKeyToAccount(privateKey as `0x${string}`);
const client = new x402Client();
registerExactEvmScheme(client, { signer: account });
return wrapFetchWithPayment(fetch, client);
}
export const edgarFilingsTool = tool(
async ({ ticker, form, limit }) => {
const fetchWithPayment = buildFetcher();
if (!fetchWithPayment) {
return "No BUYER_PRIVATE_KEY configured -- this tool needs a Base wallet funded with USDC to pay the $0.02 x402 charge per call.";
}
const res = await fetchWithPayment(`${ORIGIN}/v1/edgar_filings`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ ticker, form, limit }),
});
if (!res.ok) throw new Error(`edgar_filings failed: ${res.status} ${await res.text()}`);
return JSON.stringify(await res.json());
},
{
name: "edgar_filings",
description:
"Look up recent SEC EDGAR filings (10-K, 10-Q, 8-K, etc.) for a US-listed company by ticker. " +
"Paid x402 call: $0.02 USDC on Base per lookup.",
schema: z.object({
ticker: z.string().describe("US stock ticker, e.g. AAPL"),
form: z.string().optional().describe("Form type filter, e.g. 10-K, 8-K"),
limit: z.number().int().min(1).max(200).optional(),
}),
}
);
async function dryRun() {
const res = await fetch(`${ORIGIN}/v1/edgar_filings`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ ticker: "AAPL", limit: 3 }),
});
console.log("Unpaid call status:", res.status, "(402 = tool would need BUYER_PRIVATE_KEY to proceed)");
}
if (import.meta.url === `file://${process.argv[1]}`) {
dryRun();
}
LangChain (Python)
pip install langchain-core "x402[evm,requests]"
"""LangChain (Python) tool for a draconic21 x402 paid route.
LangChain has no official x402 integration for Python either (the tracking
feature request, langchain-ai/langchain#36306, is closed/unimplemented).
This wraps the official `x402` PyPI package's `requests` session wrapper as
a LangChain `@tool`-decorated function -- no extra glue package needed.
Install:
pip install langchain-core "x402[evm,requests]"
Run it standalone (no LLM; dry run with no key -- prints the 402 challenge):
python tool.py
"""
import json
import os
import requests
from langchain_core.tools import tool
ORIGIN = os.environ.get("DRACONIC21_ORIGIN", "https://draconic21-x402-api.onrender.com")
def _build_session():
"""Return a payment-aware requests.Session, or None if no wallet is set."""
private_key = os.environ.get("BUYER_PRIVATE_KEY")
if not private_key:
return None
from eth_account import Account
from x402 import x402ClientSync
from x402.http.clients import x402_requests
from x402.mechanisms.evm.signers import EthAccountSigner
from x402.mechanisms.evm.exact.register import register_exact_evm_client
client = x402ClientSync()
register_exact_evm_client(client, EthAccountSigner(Account.from_key(private_key)))
return x402_requests(client)
@tool
def sanctions_screen(name: str = "", wallet: str = "") -> str:
"""Screen a name or crypto wallet address against the U.S. Treasury OFAC
sanctions list (SDN). Paid x402 call: $0.006 USDC on Base per lookup.
At least one of name/wallet must be given."""
session = _build_session()
if session is None:
return (
"No BUYER_PRIVATE_KEY configured -- this tool needs a Base wallet "
"funded with USDC to pay the $0.006 x402 charge per call."
)
resp = session.post(
f"{ORIGIN}/v1/sanctions_screen",
json={k: v for k, v in {"name": name or None, "wallet": wallet or None}.items() if v},
timeout=15,
)
resp.raise_for_status()
return json.dumps(resp.json())
def _dry_run() -> None:
resp = requests.post(f"{ORIGIN}/v1/sanctions_screen", json={"name": "Banco Nacional de Cuba"}, timeout=15)
print(
"Unpaid call status:",
resp.status_code,
"(402 = tool would need BUYER_PRIVATE_KEY to proceed)",
)
if __name__ == "__main__":
_dry_run()
Routes you can call this way
Every example below targets one specific route to keep the snippet runnable, but the same client works against any draconic21 x402 route. Full list and schemas: /openapi.json.
| Route | Price | What it does |
|---|---|---|
POST /v1/sanctions_screen | $0.006 | OFAC SDN name/wallet screening |
POST /v1/sanctions_delta | $0.008 | OFAC SDN list changes since a date |
POST /v1/edgar_filings | $0.02 | Recent SEC filings by ticker/CIK |
POST /v1/edgar_company_facts | $0.02 | Key XBRL facts by ticker/CIK |
POST /v1/edgar_fulltext_search | $0.02 | Full-text search of SEC filings since 2001 |
POST /v1/signal_latest | $0.03 | Daily agent-economy signal brief |
POST /v1/signal_delta | $0.05 | What changed in Signal since a date |