Documentation Index
Fetch the complete documentation index at: https://docs.x402.org/llms.txt
Use this file to discover all available pages before exploring further.
Think of it as a search index for payable APIs and MCP tools, enabling the autonomous discovery and consumption of services.
The x402 Bazaar is in early development. While our vision is to build the “Google for agentic endpoints,” we’re currently more like “Yahoo search” - functional but evolving. Features and APIs may change as we gather feedback and expand capabilities.
Overview
The Bazaar solves a critical problem in the x402 ecosystem: discoverability. Without it, x402-compatible endpoints and MCP tools are like hidden stalls in a vast market. The Bazaar provides:
- For Buyers (API Consumers): Programmatically discover available x402-enabled services (HTTP endpoints and MCP tools), understand their capabilities, pricing, and schemas
- For Sellers (API Providers): Automatic visibility for your x402-enabled services to a global audience of developers and AI agents
- For AI Agents: Dynamic service discovery without pre-baked integrations - query, find, pay, and use
How It Works
Facilitators that support the Bazaar extension may provide a /discovery/resources endpoint that returns all x402-compatible services registered through the respective facilitator. Services (HTTP endpoints and MCP tools) are discoverable when they include the bazaar extension in their route configuration.
Note: The spec for marketplace items is open and part of the x402 scheme, meaning any facilitator can implement their own discovery layer.
After processing a payment that includes the bazaar extension, facilitators may return an EXTENSION-RESPONSES HTTP header to communicate extension-specific outcomes to the client.
Header name: EXTENSION-RESPONSES
Header value: A base64-encoded JSON object keyed by extension name. The bazaar key contains the bazaar extension’s response:
| Field | Type | Required | Description |
|---|
bazaar.status | string | Yes | One of "success", "processing", or "rejected" |
bazaar.rejectedReason | string | No | Human-readable explanation. Only present when status is "rejected" |
Status values:
| Value | Meaning |
|---|
"success" | The discovery info was validated and successfully cataloged |
"processing" | The discovery info was accepted and is being cataloged asynchronously |
"rejected" | The discovery info was rejected (e.g., failed schema validation). See rejectedReason for details |
Example (success):
EXTENSION-RESPONSES: eyJiYXphYXIiOnsic3RhdHVzIjoic3VjY2VzcyJ9fQ==
(base64 of {"bazaar":{"status":"success"}})
Example (rejected):
EXTENSION-RESPONSES: eyJiYXphYXIiOnsic3RhdHVzIjoicmVqZWN0ZWQiLCJyZWplY3RlZFJlYXNvbiI6ImluZm8gZmFpbGVkIHNjaGVtYSB2YWxpZGF0aW9uIn19
(base64 of {"bazaar":{"status":"rejected","rejectedReason":"info failed schema validation"}})
Clients that understand the bazaar extension should read the bazaar key of this header to confirm cataloging succeeded and surface any rejection reason for debugging.
Basic Flow
- Discovery: Clients query the
/discovery/resources endpoint to find available services
- Selection: Choose a service based on price, capabilities, and requirements
- Execution: Use x402 to pay for and access the selected service
- No Manual Setup: No API keys, no account creation, just discover and pay
API Reference
List Endpoint
Retrieve all available x402-compatible endpoints and MCP tools:
Coinbase
GET https://api.cdp.coinbase.com/platform/v2/x402/discovery/resources
PayAI
GET https://facilitator.payai.network/discovery/resources
Note: The recommended way to use this endpoint is to use the useFacilitator hook as described below.
Response Schema
Each resource in the list contains the following fields:
{
"accepts": [
{
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"description": "",
"extra": {
"name": "USD Coin",
"version": "2"
},
"maxAmountRequired": "200",
"maxTimeoutSeconds": 60,
"mimeType": "",
"network": "eip155:8453",
"outputSchema": {
"input": {
"method": "GET",
"type": "http"
},
"output": null
},
"payTo": "0xa2477E16dCB42E2AD80f03FE97D7F1a1646cd1c0",
"resource": "https://api.example.com/x402/weather",
"scheme": "exact"
}
],
"lastUpdated": "2025-08-09T01:07:04.005Z",
"metadata": {},
"resource": "https://api.example.com/x402/weather",
"type": "http",
"x402Version": 2
}
Resource Types
The type field indicates the resource type:
"http" - HTTP endpoints (GET, POST, PUT, PATCH, DELETE, HEAD)
"mcp" - MCP (Model Context Protocol) tools
For MCP tools, the outputSchema.input object will contain:
type: "mcp" - Identifies this as an MCP tool
toolName - The MCP tool name (used in tools/call requests)
inputSchema - JSON Schema for the tool’s arguments (follows MCP Tool.inputSchema format)
description - Human-readable description of the tool (optional)
transport - MCP transport protocol: "streamable-http" (default) or "sse" (optional)
example - Example arguments object (optional)
Note: For MCP tools, the unique resource identifier is the tuple (resource, input.toolName) since MCP multiplexes multiple tools over a single server endpoint.
Quickstart for Buyers
Step 1: Discover Available Services
Fetch the list of available x402 services using the facilitator client:
import { HTTPFacilitatorClient } from "@x402/core/http";
import { withBazaar } from "@x402/extensions";
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator"
});
const client = withBazaar(facilitatorClient);
// List HTTP services; use type: "mcp" to list MCP tools instead
const response = await client.extensions.bazaar.listResources({ type: "http" });
// Filter services under $0.10
const usdcAsset = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
const maxPrice = 100000; // $0.10 in USDC atomic units (6 decimals)
const affordableServices = response.items.filter(item =>
item.accepts.find(paymentRequirements =>
paymentRequirements.asset === usdcAsset &&
Number(paymentRequirements.maxAmountRequired) < maxPrice
)
);
package main
import (
"context"
"fmt"
"github.com/x402-foundation/x402/go/extensions/bazaar"
x402http "github.com/x402-foundation/x402/go/http"
)
func main() {
ctx := context.Background()
// Create facilitator client and extend with bazaar
facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
URL: "https://x402.org/facilitator",
})
client := bazaar.WithBazaar(facilitatorClient)
// List discovery resources
response, err := client.ListDiscoveryResources(ctx, &bazaar.ListDiscoveryResourcesParams{
Type: "http",
Limit: 20,
Offset: 0,
})
if err != nil {
panic(err)
}
// Filter services under $0.10
usdcAsset := "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
maxPrice := 100000 // $0.10 in USDC atomic units (6 decimals)
for _, item := range response.Items {
for _, paymentReq := range item.Accepts {
// Parse asset and maxAmountRequired from payment requirements
if asset, ok := paymentReq["asset"].(string); ok && asset == usdcAsset {
if maxAmount, ok := paymentReq["maxAmountRequired"].(string); ok {
// Convert and compare price
fmt.Printf("Found service: %s\n", item.Resource)
}
}
}
}
}
from x402.http import FacilitatorConfig, HTTPFacilitatorClient
from x402.extensions.bazaar import with_bazaar, ListDiscoveryResourcesParams
facilitator_client = HTTPFacilitatorClient(
FacilitatorConfig(url="https://x402.org/facilitator")
)
client = with_bazaar(facilitator_client)
# List HTTP services; use type="mcp" to list MCP tools instead
response = client.extensions.bazaar.list_resources(
ListDiscoveryResourcesParams(type="http")
)
# Filter services under $0.10
usdc_asset = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
max_price = 100000 # $0.10 in USDC atomic units (6 decimals)
affordable_services = [
item
for item in response.items
if any(
payment_req.get("asset") == usdc_asset
and int(payment_req.get("amount", 0)) < max_price
for payment_req in (item.accepts or [])
)
]
Step 1b: Search for Services
You can also search for services using a natural-language query. Pagination is facilitator-defined: some facilitators may include top-level limit and cursor, and others may omit them.
const results = await client.extensions.bazaar.search({
query: "weather APIs",
type: "http",
});
// Some facilitators return a pagination object like the list endpoint
if (results.pagination) {
console.log("Next page cursor:", results.pagination.cursor);
}
console.log(`Found ${results.resources.length} services`);
results, err := client.SearchDiscoveryResources(ctx, &bazaar.SearchDiscoveryResourcesParams{
Query: "weather APIs",
Type: "http",
})
if err != nil {
panic(err)
}
fmt.Printf("Found %d services\n", len(results.Resources))
if results.Pagination != nil {
fmt.Println("Next cursor:", results.Pagination.Cursor)
}
from x402.extensions.bazaar import SearchDiscoveryResourcesParams
results = client.extensions.bazaar.search(
SearchDiscoveryResourcesParams(query="weather APIs", type="http")
)
print(f"Found {len(results.resources)} services")
if results.pagination is not None:
print(f"Next cursor: {results.pagination.cursor}")
Step 2: Call a Discovered Service
Once you’ve found a suitable service, use an x402 client to call it:
import { wrapAxiosWithPayment, x402Client } from "@x402/axios";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import axios from "axios";
import { privateKeyToAccount } from "viem/accounts";
// Set up your payment account
const account = privateKeyToAccount("0xYourPrivateKey");
const client = new x402Client();
client.register("eip155:*", new ExactEvmScheme(account));
// Select a service from discovery
const selectedService = affordableServices[0];
// Create a payment-enabled client for that service
const api = wrapAxiosWithPayment(
axios.create({ baseURL: selectedService.resource }),
client
);
// Select the payment method of your choice
const selectedPaymentRequirements = selectedService.accepts[0];
const inputSchema = selectedPaymentRequirements.outputSchema.input;
// Build the request using the service's schema
const response = await api.request({
method: inputSchema.method,
url: inputSchema.resource,
params: { location: "San Francisco" } // Based on inputSchema
});
console.log("Response data:", response.data);
import asyncio
from eth_account import Account
from x402 import x402Client
from x402.http.clients import x402HttpxClient
from x402.mechanisms.evm import EthAccountSigner
from x402.mechanisms.evm.exact.register import register_exact_evm_client
async def main() -> None:
account = Account.from_key("0xYourPrivateKey")
client = x402Client()
register_exact_evm_client(client, EthAccountSigner(account))
# Select a service from discovery (from Step 1)
selected_service = affordable_services[0]
# Select the payment method of your choice
selected_payment_requirements = selected_service.accepts[0]
input_schema = selected_payment_requirements.output_schema.input
# Make the request using httpx client
async with x402HttpxClient(client) as http:
response = await http.request(
method=input_schema.method,
url=input_schema.resource,
params={"location": "San Francisco"} # Based on input_schema
)
await response.aread()
print(f"Response data: {response.json()}")
asyncio.run(main())
For discovered MCP tools, use the x402 MCP client to call the tool after paying:
import { createx402MCPClient } from "@x402/mcp";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { privateKeyToAccount } from "viem/accounts";
const account = privateKeyToAccount("0xYourPrivateKey");
// Discover MCP tools from the Bazaar
const mcpItems = await client.extensions.bazaar.listResources({ type: "mcp" });
const selectedTool = mcpItems.items[0];
const { toolName } = selectedTool.discoveryInfo.input;
// Connect the x402 MCP client to the tool's server
const x402Mcp = createx402MCPClient({
name: "my-mcp-client",
schemes: [{ network: "eip155:8453", scheme: new ExactEvmScheme(account) }],
});
// selectedTool.resource is an identifier like mcp://tool/get_weather, not a transport URL
const mcpServerUrl = "https://your-mcp-server.example.com";
const transport = new SSEClientTransport(new URL(`${mcpServerUrl}/sse`));
await x402Mcp.connect(transport);
// Call the paid tool — x402Mcp handles payment automatically
const result = await x402Mcp.callTool(toolName, { city: "San Francisco" });
console.log("Tool result:", result);
await x402Mcp.close();
Quickstart for Sellers
Listing with Bazaar Extension
Add the bazaar extension to your route configuration to make your API or MCP tools discoverable.
Supported Resource Types:
- HTTP Endpoints - Standard REST APIs (GET, POST, PUT, PATCH, DELETE, HEAD)
- MCP Tools - Model Context Protocol tools for AI agent integration
Dynamic Routes
The Bazaar extension supports parameterized routes (e.g., /users/[userId], /weather/[country]/[city]). When you use route parameters:
- The extension automatically extracts concrete parameter values into
pathParams (e.g., { "userId": "123" })
- A
routeTemplate field is added using :param syntax (e.g., /users/:userId)
- Facilitators use
routeTemplate as the catalog key, consolidating all requests to the same route pattern into a single discovery entry
This means /users/123, /users/456, and /users/789 all map to the same catalog entry: /users/:userId.
Note: Wildcard segments (*) are automatically converted to named parameters (:var1, :var2, etc.) for catalog normalization.
To enhance your listing with descriptions and schemas, include them when setting up your x402 middleware. You should include descriptions for each parameter to make it clear for agents to call your endpoints:
import { paymentMiddleware } from "@x402/express";
import { x402ResourceServer, HTTPFacilitatorClient } from "@x402/core/server";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { bazaarResourceServerExtension } from "@x402/extensions";
const facilitatorClient = new HTTPFacilitatorClient({
url: "https://x402.org/facilitator"
});
const server = new x402ResourceServer(facilitatorClient);
server.register("eip155:*", new ExactEvmScheme());
const routes = {
"GET /weather": {
price: "$0.001",
network: "eip155:8453",
resource: "0xYourAddress",
description: "Get current weather data for any location",
extensions: {
bazaar: {
discoverable: true,
inputSchema: {
queryParams: {
location: {
type: "string",
description: "City name or coordinates",
required: true
}
}
},
outputSchema: {
type: "object",
properties: {
temperature: { type: "number" },
conditions: { type: "string" },
humidity: { type: "number" }
}
}
}
}
}
};
app.use(paymentMiddleware(routes, server));
from fastapi import FastAPI
from x402.http import FacilitatorConfig, HTTPFacilitatorClient, PaymentOption
from x402.http.middleware.fastapi import PaymentMiddlewareASGI
from x402.http.types import RouteConfig
from x402.mechanisms.evm.exact import ExactEvmServerScheme
from x402.server import x402ResourceServer
app = FastAPI()
# Create facilitator client (CDP mainnet)
facilitator = HTTPFacilitatorClient(
FacilitatorConfig(url="https://x402.org/facilitator")
)
# Create resource server and register EVM scheme
server = x402ResourceServer(facilitator)
server.register("eip155:8453", ExactEvmServerScheme())
# Define routes with Bazaar discovery metadata
routes: dict[str, RouteConfig] = {
"GET /weather": RouteConfig(
accepts=[
PaymentOption(
scheme="exact",
pay_to="0xYourAddress",
price="$0.001",
network="eip155:8453",
),
],
mime_type="application/json",
description="Get current weather data for any location",
extensions={
"bazaar": {
"discoverable": True,
"inputSchema": {
"queryParams": {
"location": {
"type": "string",
"description": "City name or coordinates",
"required": True,
}
}
},
"outputSchema": {
"type": "object",
"properties": {
"temperature": {"type": "number"},
"conditions": {"type": "string"},
"humidity": {"type": "number"},
},
},
}
},
),
}
app.add_middleware(PaymentMiddlewareASGI, routes=routes, server=server)
package main
import (
x402 "github.com/x402-foundation/x402/go"
"github.com/x402-foundation/x402/go/extensions/bazaar"
"github.com/x402-foundation/x402/go/extensions/types"
x402http "github.com/x402-foundation/x402/go/http"
ginmw "github.com/x402-foundation/x402/go/http/gin"
evm "github.com/x402-foundation/x402/go/mechanisms/evm/exact/server"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
facilitatorClient := x402http.NewHTTPFacilitatorClient(&x402http.FacilitatorConfig{
URL: "https://x402.org/facilitator",
})
// Create Bazaar Discovery Extension with input/output schemas
discoveryExtension, _ := bazaar.DeclareDiscoveryExtension(
bazaar.MethodGET,
map[string]interface{}{"location": "San Francisco"},
types.JSONSchema{
"properties": map[string]interface{}{
"location": map[string]interface{}{
"type": "string",
"description": "City name or coordinates",
},
},
"required": []string{"location"},
},
"",
&types.OutputConfig{
Schema: types.JSONSchema{
"properties": map[string]interface{}{
"temperature": map[string]interface{}{"type": "number"},
"conditions": map[string]interface{}{"type": "string"},
"humidity": map[string]interface{}{"type": "number"},
},
},
},
)
routes := x402http.RoutesConfig{
"GET /weather": {
Accepts: x402http.PaymentOptions{{
Scheme: "exact",
PayTo: "0xYourAddress",
Price: "$0.001",
Network: x402.Network("eip155:8453"),
}},
Description: "Get current weather data for any location",
Extensions: map[string]interface{}{
types.BAZAAR: discoveryExtension,
},
},
}
r.Use(ginmw.X402Payment(ginmw.Config{
Routes: routes,
Facilitator: facilitatorClient,
Schemes: []ginmw.SchemeConfig{{
Network: x402.Network("eip155:8453"),
Server: evm.NewExactEvmScheme(),
}},
}))
r.GET("/weather", func(c *gin.Context) {
location := c.DefaultQuery("location", "San Francisco")
c.JSON(200, gin.H{
"location": location,
"temperature": 70,
"conditions": "sunny",
"humidity": 45,
})
})
r.Run(":4021")
}
To make paid MCP tools discoverable, pass the discovery extension in your payment wrapper config:
import { createPaymentWrapper } from "@x402/mcp";
import { declareDiscoveryExtension } from "@x402/extensions/bazaar";
const paid = createPaymentWrapper(resourceServer, {
accepts,
resource: {
url: "mcp://tool/get_weather",
description: "Get current weather for a city",
},
extensions: declareDiscoveryExtension({
toolName: "get_weather",
description: "Get current weather for a city",
transport: "sse",
inputSchema: {
properties: { city: { type: "string", description: "City name" } },
required: ["city"],
},
example: { city: "San Francisco" },
}),
});
mcpServer.tool("get_weather", "Get current weather ($0.001)", { city: z.string() },
paid(async ({ city }) => ({ content: [{ type: "text", text: JSON.stringify(await fetchWeather(city)) }] }))
);
from x402.mcp import create_payment_wrapper
from x402.extensions.bazaar import DeclareMcpDiscoveryConfig, declare_mcp_discovery_extension
weather_discovery = declare_mcp_discovery_extension(
DeclareMcpDiscoveryConfig(
tool_name="get_weather",
description="Get current weather for a city",
transport="sse",
input_schema={
"properties": {"city": {"type": "string", "description": "City name"}},
"required": ["city"],
},
example={"city": "San Francisco"},
)
)
weather_wrapper = create_payment_wrapper(
resource_server,
accepts=weather_accepts,
resource=ResourceInfo(
url="mcp://tool/get_weather",
description="Get current weather for a city",
),
extensions=weather_discovery,
)
@mcp.tool(name="get_weather", description="Get current weather ($0.001)")
@weather_wrapper
async def get_weather(city: str) -> str:
return json.dumps(await fetch_weather(city))
import (
"github.com/x402-foundation/x402/go/extensions/bazaar"
mcp402 "github.com/x402-foundation/x402/go/mcp"
"github.com/x402-foundation/x402/go/types"
)
weatherDiscovery, _ := bazaar.DeclareMcpDiscoveryExtension(bazaar.DeclareMcpDiscoveryConfig{
ToolName: "get_weather",
Description: "Get current weather for a city",
Transport: bazaar.TransportSSE,
InputSchema: bazaar.JSONSchema{
"properties": map[string]interface{}{
"city": map[string]interface{}{
"type": "string",
"description": "City name",
},
},
"required": []string{"city"},
},
Example: map[string]interface{}{"city": "San Francisco"},
})
paymentWrapper := mcp402.NewPaymentWrapper(resourceServer, mcp402.PaymentWrapperConfig{
Accepts: weatherAccepts,
Resource: &types.ResourceInfo{
URL: "mcp://tool/get_weather",
Description: "Get current weather for a city",
},
Extensions: map[string]interface{}{
bazaar.BAZAAR.Key(): weatherDiscovery,
},
})
Coming Soon
The x402 Bazaar is rapidly evolving, and your feedback helps us prioritize features.
Support
FAQ
Q: How do I get my service listed?
A: Add the bazaar extension to your route configuration. See the examples above.
Q: How can I make endpoint calls more accurate?
A: Include descriptions clearly stating what each parameter does and how to call your endpoint, but do so as succinctly as possible.
Q: How does pricing work?
A: Listing is free. Services set their own prices per API call, paid via x402.
Q: What networks are supported?
A: Currently Base (eip155:8453), Base Sepolia (eip155:84532), Solana (solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp), and Solana Devnet (solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1) with USDC payments.
Q: Can I list non-x402 services?
A: No, only x402-compatible endpoints can be listed. See our Quickstart for Sellers to make your API x402-compatible.
Q: What are MCP tools and how do they work with Bazaar?
A: MCP (Model Context Protocol) tools are AI-agent-friendly interfaces that can be discovered and paid for through x402. When listing an MCP tool, include type: "mcp" in the bazaar extension along with the tool name and input schema. The unique identifier for MCP tools is the combination of the resource URL and tool name, since multiple tools can be served from a single MCP endpoint.