Back to OnchorDashboard

Onchor API

API marketplace on Solana. List your APIs, let developers and AI agents discover and purchase them with USDC. 5% marketplace fee. Fully on-chain deposits via Anchor program with PDA-controlled vaults.

Base URL: https://api.onchor.xyz
All endpoints return JSON. All request bodies must be Content-Type: application/json.

Quickstart

Start selling your API in 3 steps.

1

Create a seller account

Register with your email and set your Solana wallet address to receive USDC payments.

# Register
curl -X POST https://api.onchor.xyz/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My API Company",
    "email": "seller@example.com",
    "password": "securepassword123"
  }'

# Response includes your JWT and API key
{
  "access_token": "eyJhbGciOi...",
  "merchant_id": "uuid-here",
  "api_key": "a1b2c3d4...64chars"
}
2

Set your Solana wallet

Configure the wallet where you want to receive USDC revenue.

curl -X PATCH https://api.onchor.xyz/api/merchants/me \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "wallet_solana": "YourSolanaPublicKey..."
  }'
3

List your API

Create a marketplace listing. Buyers and AI agents can discover and purchase access.

curl -X POST https://api.onchor.xyz/api/marketplace/seller/listings \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Weather API",
    "category": "data",
    "base_url": "https://api.weather.io/v1",
    "pricing_model": "per_call",
    "price_per_call_usdc": 0.01,
    "monthly_call_limit": 10000
  }'
That's it. Your API is now discoverable by developers and AI agents. When they purchase access, USDC is swept to your wallet (minus the 5% marketplace fee) and they receive an API key to call your service through the Onchor gateway.

Authentication

Onchor supports three ways to authenticate:

MethodHeaderUsed For
Wallet AuthConnect wallet → sign message → get JWT + API key. Wallet = merchant identity.
Bearer JWTAuthorization: Bearer {token}Dashboard, browser-based interactions (expires after 24h)
API KeyX-API-Key: {key}Server-to-server automation (no expiry, regeneratable)

JWT tokens expire after 24 hours. API keys don't expire but can be regenerated (which invalidates the previous key). All authenticated endpoints accept either method.

Wallet auth is the primary method. Connect your Solana wallet and your account is created automatically. Your wallet address becomes your merchant wallet — payments are swept there directly. Email/password is available as a fallback.

Server-to-Server (API Key)

Use your API key to call the Onchor API from your own server without a browser or wallet. This is useful for automating listing management, pulling stats into your own dashboard, or integrating Onchor into a CI/CD pipeline.

Find your API key in Dashboard → Settings → Server-to-Server API Key.

# Example: list your API listings from your server
curl https://api.onchor.xyz/api/marketplace/seller/listings \
  -H "X-API-Key: YOUR_API_KEY"
# Example: get your dashboard stats
curl https://api.onchor.xyz/api/dashboard/stats \
  -H "X-API-Key: YOUR_API_KEY"
The API key works on every endpoint that requires authentication (listings, analytics, transactions, profile). You can use X-API-Key or Authorization: Bearer {jwt} — both work interchangeably.

Base URL

https://api.onchor.xyz

Sandbox & Testing

Onchor runs on Solana devnet for testing. All marketplace endpoints work identically — the only difference is USDC deposits use devnet tokens (free, no real money).

How to test without spending real USDC:
1. Use the production API at https://api.onchor.xyz — all listing and discovery endpoints are free
2. Create a listing, make a purchase, and poll the status — you'll get a deposit address on Solana
3. For end-to-end testing with actual USDC transfers, use small amounts ($1-2 USDC) on mainnet

Test the API without auth

These endpoints are fully public and require no setup. Try them now:

# Browse available APIs
curl https://api.onchor.xyz/api/marketplace/listings

# Search by keyword
curl "https://api.onchor.xyz/api/marketplace/listings?q=weather"

# Get available categories
curl https://api.onchor.xyz/api/marketplace/categories

# Get a specific listing
curl https://api.onchor.xyz/api/marketplace/listings/weather-api

Full test flow (seller + buyer)

1

Register a test account

Create a seller account with a test email. No email verification needed.

curl -X POST https://api.onchor.xyz/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Test Seller",
    "email": "test@example.com",
    "password": "testpass123"
  }'
2

Create a listing

List a test API using the JWT from step 1.

curl -X POST https://api.onchor.xyz/api/marketplace/seller/listings \
  -H "Authorization: Bearer YOUR_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Test API",
    "category": "other",
    "base_url": "https://httpbin.org",
    "pricing_model": "per_call",
    "price_per_call_usdc": 0.001,
    "monthly_call_limit": 100
  }'
3

Verify your listing is public

curl https://api.onchor.xyz/api/marketplace/listings?q=test
4

Initiate a purchase

curl -X POST https://api.onchor.xyz/api/marketplace/purchase \
  -H "Content-Type: application/json" \
  -d '{
    "listing_id": "YOUR_LISTING_ID",
    "buyer_identifier": "buyer@test.com"
  }'

# You'll get a deposit_address and subscription_id
# Send USDC to the deposit address to complete the purchase

Code Examples

Ready-to-use snippets for the most common flows. All examples use the public marketplace endpoints (no auth required for discovery and purchase).

Browse & discover APIs

Python

import requests

# Search for APIs
response = requests.get("https://api.onchor.xyz/api/marketplace/listings", params={
    "q": "weather",
    "category": "data",
    "sort_by": "popular"
})
listings = response.json()["listings"]

for api in listings:
    print(f"{api['name']} — {api['pricing_model']} — {api['price_per_call_usdc']} USDC/call")

Node.js

// Browse APIs (Node 18+ with native fetch)
const res = await fetch("https://api.onchor.xyz/api/marketplace/listings?q=weather&category=data");
const { listings } = await res.json();

listings.forEach(api => {
  console.log(`${api.name} — ${api.pricing_model} — ${api.price_per_call_usdc} USDC/call`);
});

PHP

// Browse APIs
$params = http_build_query(['q' => 'weather', 'category' => 'data']);
$response = file_get_contents("https://api.onchor.xyz/api/marketplace/listings?{$params}");
$data = json_decode($response, true);

foreach ($data['listings'] as $api) {
    echo $api['name'] . " — " . $api['price_per_call_usdc'] . " USDC/call\n";
}

Purchase API access & poll for key

Python

import requests, time

# 1. Purchase
purchase = requests.post("https://api.onchor.xyz/api/marketplace/purchase", json={
    "listing_id": "YOUR_LISTING_ID",
    "buyer_identifier": "agent@ai.com"
}).json()

print(f"Send {purchase['amount_usdc']} USDC to {purchase['deposit_address']}")
sub_id = purchase["subscription_id"]

# 2. Poll until payment confirmed
while True:
    status = requests.get(f"https://api.onchor.xyz/api/marketplace/purchase/{sub_id}/status").json()
    if status["status"] == "active":
        api_key = status["api_key"]  # Save this! Only returned once
        print(f"API Key: {api_key}")
        break
    time.sleep(5)

Node.js

// 1. Purchase
const purchase = await fetch("https://api.onchor.xyz/api/marketplace/purchase", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    listing_id: "YOUR_LISTING_ID",
    buyer_identifier: "agent@ai.com"
  })
}).then(r => r.json());

console.log(`Send ${purchase.amount_usdc} USDC to ${purchase.deposit_address}`);

// 2. Poll until payment confirmed
let apiKey;
while (!apiKey) {
  await new Promise(r => setTimeout(r, 5000));
  const status = await fetch(
    `https://api.onchor.xyz/api/marketplace/purchase/${purchase.subscription_id}/status`
  ).then(r => r.json());
  if (status.status === "active") {
    apiKey = status.api_key;  // Save this! Only returned once
    console.log("API Key:", apiKey);
  }
}

Call an API through the gateway

Python

import requests

# Call the Weather API through Onchor gateway
response = requests.get(
    "https://api.onchor.xyz/api/gateway/weather-api/forecast",
    params={"city": "paris"},
    headers={"X-Marketplace-Key": "YOUR_API_KEY"}
)
print(response.json())

Node.js

const res = await fetch(
  "https://api.onchor.xyz/api/gateway/weather-api/forecast?city=paris",
  { headers: { "X-Marketplace-Key": "YOUR_API_KEY" } }
);
console.log(await res.json());

PHP

$opts = ['http' => [
    'header' => "X-Marketplace-Key: YOUR_API_KEY"
]];
$ctx = stream_context_create($opts);
$response = file_get_contents(
    "https://api.onchor.xyz/api/gateway/weather-api/forecast?city=paris",
    false, $ctx
);
$data = json_decode($response, true);
print_r($data);

Seller: Create a listing

Python

import requests

headers = {
    "Authorization": f"Bearer {YOUR_JWT}",
    "Content-Type": "application/json"
}

listing = requests.post("https://api.onchor.xyz/api/marketplace/seller/listings",
    headers=headers,
    json={
        "name": "Weather API",
        "category": "data",
        "base_url": "https://api.weather.io/v1",
        "pricing_model": "per_call",
        "price_per_call_usdc": 0.01,
        "monthly_call_limit": 10000,
        "short_description": "Real-time weather data for any location",
        "tags": ["weather", "forecast", "realtime"]
    }
).json()

print(f"Listed: {listing['name']} ({listing['slug']})")

Node.js

const listing = await fetch("https://api.onchor.xyz/api/marketplace/seller/listings", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${YOUR_JWT}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    name: "Weather API",
    category: "data",
    base_url: "https://api.weather.io/v1",
    pricing_model: "per_call",
    price_per_call_usdc: 0.01,
    monthly_call_limit: 10000,
    short_description: "Real-time weather data for any location",
    tags: ["weather", "forecast", "realtime"]
  })
}).then(r => r.json());

console.log(`Listed: ${listing.name} (${listing.slug})`);

Checkout

Checkout endpoints are used internally by the marketplace purchase flow. Each payment generates a unique Solana deposit address. When USDC arrives, it's automatically swept to the seller's wallet.

Get Checkout Info

GET/api/checkout/{payment_id}Public

Retrieve details of a payment session. Used by the checkout page to display payment info.

Response

{
  "payment_id": "c3f8a2b1-...",
  "merchant_name": "My Store",
  "amount_eur": 25.00,
  "amount_usdc": 27.00,
  "deposit_address": "7xKXtg2Cw...",
  "description": "Order #1234",
  "status": "awaiting_payment",
  "currency": "USDC",
  "success_url": "https://mystore.com/success",
  "cancel_url": "https://mystore.com/cancel",
  "created_at": "2026-03-08T14:00:00",
  "expires_at": "2026-03-08T14:30:00"
}

Submit Payment

POST/api/checkout/{payment_id}/payPublic

Customer submits their email before paying. Transitions the payment to awaiting_payment.

Request Body

FieldTypeRequiredDescription
customer_emailstring (email)YesCustomer's email address

Response

{
  "payment_id": "c3f8a2b1-...",
  "status": "awaiting_payment",
  "amount_usdc": 27.00,
  "deposit_address": "7xKXtg2Cw..."
}

Check Payment Status

GET/api/checkout/{payment_id}/statusPublic

Poll this endpoint to detect when USDC arrives. When the full amount is received, the payment is marked as completed and the sweep is triggered automatically.

Response

{
  "payment_id": "c3f8a2b1-...",
  "status": "completed",
  "amount_usdc": 27.00,
  "deposit_address": "7xKXtg2Cw...",
  "success_url": "https://mystore.com/success"
}
Polling: The checkout page polls this endpoint every 3 seconds. When status becomes completed or swept, the success_url is returned for redirect.

Authentication & Merchants

Wallet Auth (recommended)

Two-step flow: request a nonce, then sign it with your wallet.

POST/api/auth/wallet/noncePublic

Request a nonce message to sign with your Solana wallet.

Request Body

FieldTypeRequiredDescription
wallet_addressstringYesSolana wallet public key (base58)

Response

{
  "nonce": "a1b2c3d4...",
  "message": "Sign in to Onchor\nNonce: a1b2c3d4..."
}
POST/api/auth/wallet/verifyPublic

Submit the signed message. If the wallet is new, an account is created automatically. Returns JWT + API key.

Request Body

FieldTypeRequiredDescription
wallet_addressstringYesSolana wallet public key (base58)
signaturestringYesEd25519 signature of the message (base58-encoded)

Response

{
  "access_token": "eyJhbGciOi...",
  "token_type": "bearer",
  "merchant_id": "uuid-here",
  "api_key": "a1b2c3d4...64chars",
  "is_new": true
}
First connection: is_new: true means the account was just created. The api_key is only returned on first connection — save it immediately. Your wallet address is automatically set as your merchant wallet.

Register (email fallback)

POST/api/auth/registerPublic

Create a new merchant account. Returns a JWT token and a one-time API key.

Request Body

FieldTypeRequiredDescription
namestringYesBusiness or display name
emailstring (email)YesAccount email
passwordstringYesAccount password
webhook_urlstringNoURL for payment notifications

Response — 201 Created

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "merchant_id": "f47ac10b-...",
  "api_key": "a1b2c3d4e5f6...64chars"
}
Save your API key immediately. It is only shown once at registration. If lost, you'll need to regenerate it from your dashboard.

Login

POST/api/auth/loginPublic

Authenticate with email and password. Returns a JWT token valid for 24 hours.

Request Body

FieldTypeRequiredDescription
emailstring (email)YesAccount email
passwordstringYesAccount password

Get Profile

GET/api/merchants/profileBearer JWT

Retrieve authenticated merchant's profile.

Update Profile

PATCH/api/merchants/profileBearer JWT

Update merchant settings. All fields are optional — only provided fields are updated.

Request Body

FieldTypeRequiredDescription
namestringNoDisplay name
webhook_urlstringNoWebhook URL for notifications
wallet_solanastringNoSolana wallet address (base58, validated)
fee_percentagefloatNoCustom merchant fee %

Regenerate API Key

POST/api/merchants/regenerate-keyBearer JWT

Generate a new API key. The previous key is immediately invalidated.

Response

{
  "api_key": "new64charkey...",
  "message": "Save this key now. It will not be shown again."
}

Transactions

List Transactions

GET/api/transactionsBearer JWT

Paginated list of your transactions, newest first.

Query Parameters

ParamTypeDefaultDescription
pageint1Page number
per_pageint20Items per page (max 100)
statusstringFilter by status

Response

{
  "transactions": [
    {
      "id": "c3f8a2b1-...",
      "amount_eur": 25.00,
      "amount_usdc": 27.00,
      "status": "swept",
      "platform_fee": 0.25,
      "net_amount_usdc": 26.73,
      "sweep_tx_signature": "5xYz...",
      "created_at": "2026-03-08T14:00:00",
      "completed_at": "2026-03-08T14:05:32",
      "swept_at": "2026-03-08T14:05:34"
    }
  ],
  "total": 42,
  "page": 1,
  "per_page": 20
}

Get Transaction

GET/api/transactions/{transaction_id}Bearer JWT

Retrieve a single transaction with full details.

Dashboard

Stats

GET/api/dashboard/statsBearer JWT

Aggregated metrics for your account.

Response

{
  "total_transactions": 142,
  "completed_transactions": 128,
  "failed_transactions": 3,
  "pending_transactions": 11,
  "total_volume_eur": 12540.00,
  "total_revenue_eur": 12289.20,
  "total_platform_fees_eur": 125.40
}

Recent Transactions

GET/api/dashboard/recentBearer JWT

Returns the 10 most recent transactions.

Webhooks

Set your webhook_url in your merchant profile. Onchor sends a POST request whenever a payment is completed.

Event: payment.completed

Sent when USDC is detected on the deposit address and the sweep to your wallet is initiated.

POST https://your-server.com/webhook
Content-Type: application/json
X-Webhook-Signature: sha256=...

{
  "event": "payment.completed",
  "data": {
    "payment_id": "c3f8a2b1-...",
    "amount_eur": 25.00,
    "amount_usdc": 27.00,
    "net_amount_usdc": 26.73,
    "currency": "USDC",
    "status": "swept",
    "customer_email": "customer@example.com",
    "sweep_tx": "5xYzAbc...",
    "metadata": { "order_id": "1234" }
  }
}
Signature verification: The X-Webhook-Signature header contains an HMAC-SHA256 signature of the request body, signed with your API key. Always verify this signature to ensure the webhook is from Onchor.

Payment Statuses

pendingawaiting_paymentcompletedswept
StatusDescriptionTerminal?
pendingCheckout created, waiting for customer to submit emailNo
awaiting_paymentCustomer confirmed, waiting for USDC depositNo
completedUSDC received on deposit addressNo
sweptUSDC transferred to merchant wallet. Final state.Yes
expiredPayment window expired (30 minutes)Yes
failedPayment failedYes
refundedPayment refunded by adminYes

Error Handling

All errors return a JSON object with a detail field:

{
  "detail": "Payment not found"
}

HTTP Status Codes

CodeMeaning
400Bad request — invalid input or validation failure
401Unauthorized — missing/invalid JWT, API key, or signature
403Forbidden — account deactivated or insufficient permissions
404Not found — resource doesn't exist
409Conflict — duplicate resource (e.g. email already registered)
410Gone — resource deactivated (e.g. listing deleted)
429Rate limited — too many requests or quota exhausted
502Bad gateway — upstream API unreachable
504Gateway timeout — upstream API too slow

Authentication errors

EndpointCodeDetail message
POST /auth/register409A merchant with this email already exists
POST /auth/login401Invalid email or password
POST /auth/login403Merchant account is deactivated
POST /auth/wallet/nonce400Invalid Solana wallet address
POST /auth/wallet/verify401Nonce expired or invalid. Request a new one.
POST /auth/wallet/verify401Invalid signature
Any authenticated endpoint401Not authenticated (missing/expired JWT)

Checkout errors

EndpointCodeDetail message
GET /checkout/{id}404Payment not found
POST /checkout/{id}/pay404Payment not found
POST /checkout/{id}/pay400Payment cannot be processed (current status: {status})
GET /checkout/{id}/status404Payment not found

Merchant errors

EndpointCodeDetail message
PATCH /merchants/me400Invalid Solana wallet address. Must be a valid base58-encoded 32-byte public key.

Marketplace errors

EndpointCodeDetail message
GET /marketplace/listings/{slug}404Listing not found
POST /marketplace/purchase404Listing not found or inactive
GET /marketplace/purchase/{id}/status404Subscription not found
POST /marketplace/seller/listings400Invalid category. Must be one of: audio, compute, data, ...
PATCH /marketplace/seller/listings/{id}404Listing not found
DELETE /marketplace/seller/listings/{id}404Listing not found
POST /marketplace/keys404Active subscription not found
DELETE /marketplace/keys/{id}404Key not found or not yours

Gateway proxy errors

CodeDetail messageWhat to do
401X-Marketplace-Key header requiredAdd the X-Marketplace-Key header
401Invalid, expired, or exhausted API keyCheck your key is correct and quota not exhausted
403API key does not have {METHOD} permissionYour key is scoped — create a key with the right HTTP methods
403API key not valid for this listingThis key belongs to a different API subscription
410This API listing has been deactivatedThe seller removed this listing — find an alternative
429Daily call limit reached. Resets at midnight UTC.Wait until midnight UTC or upgrade your plan
502Could not connect to upstream APIThe seller's API is down — try again later
504Upstream API timed outThe seller's API is too slow — try again later
Tip: Always check the detail field in error responses — it contains a human-readable description of what went wrong.

API Marketplace

The Onchor marketplace lets sellers list their APIs and buyers (including AI agents) discover, purchase, and consume them. All requests go through the Onchor gateway for authentication, metering, and billing. Onchor takes a 5% fee on marketplace sales.

Designed for AI agents. All discovery and purchase endpoints are public — no auth required. An agent can search for APIs, buy access with USDC, and start making calls in a single session.

Browse Listings

GET/api/marketplace/listingsPublic

Search and filter API listings. No auth required.

ParamTypeDefaultDescription
qstringSearch query (name, description, tags)
categorystringFilter: data, llm, compute, storage, image, audio, search, finance, other
pricing_modelstringFilter: per_call, monthly, one_time
sort_bystringpopularSort: popular, newest, price_low, price_high
pageint1Page number
per_pageint20Items per page (max 100)
// Response (200)
{
  "listings": [
    {
      "id": "uuid",
      "name": "Weather API",
      "slug": "weather-api",
      "category": "data",
      "pricing_model": "per_call",
      "price_per_call_usdc": 0.01,
      "monthly_call_limit": 1000,
      "daily_call_limit": 100,
      "total_subscriptions": 42,
      "total_calls_served": 15000
    }
  ],
  "total": 1,
  "page": 1,
  "per_page": 20
}

Get Listing by Slug

GET/api/marketplace/listings/{slug}Public

Get a single listing by its slug.

Purchase API Access

POST/api/marketplace/purchasePublic

Purchase access to an API listing. Returns a deposit address to send USDC to.

FieldTypeRequiredDescription
listing_idstringYesID of the listing to purchase
buyer_identifierstringNoWallet address or email
// Response (201)
{
  "subscription_id": "sub_uuid",
  "listing_name": "Weather API",
  "pricing_model": "per_call",
  "price_usdc": 10.0,
  "status": "pending_payment",
  "payment_id": "tx_uuid",
  "deposit_address": "7xKXtg2Cw...",
  "amount_usdc": 10.0
}

Check Purchase Status

GET/api/marketplace/purchase/{subscription_id}/statusPublic

Poll this endpoint after sending USDC. When payment is confirmed, the response includes your API key.

The API key is only returned once. Save it immediately. Subsequent polls will not include the key.
// Response when active (API key returned once)
{
  "subscription_id": "sub_uuid",
  "status": "active",
  "api_key": "a1b2c3d4e5f6...64chars",
  "listing_name": "Weather API",
  "listing_slug": "weather-api",
  "gateway_base_url": "https://api.onchor.xyz/api/gateway/weather-api",
  "calls_limit": 1000,
  "daily_call_limit": 100,
  "expires_at": null
}

Gateway Proxy

ANY/api/gateway/{slug}/{path}X-Marketplace-Key

Proxy requests to the seller's API. Supports GET, POST, PUT, PATCH, DELETE. All headers, query params, and body are forwarded.

The gateway validates your key, checks scopes and quotas, forwards the request, logs usage, and returns the response.

# Example: Call the Weather API through the gateway
curl https://api.onchor.xyz/api/gateway/weather-api/forecast?city=paris \
  -H "X-Marketplace-Key: a1b2c3d4e5f6..."

Quota enforcement

QuotaHTTP CodeDetail
Total calls exhausted401Invalid, expired, or exhausted API key
Daily limit reached429Daily call limit reached. Resets at midnight UTC.
Wrong HTTP method for scoped key403API key does not have {METHOD} permission

Create Listing (Seller)

POST/api/marketplace/seller/listingsBearer JWT

Create a new API listing.

FieldTypeRequiredDescription
namestringYesAPI name
descriptionstringNoFull description
short_descriptionstringNoOne-liner (max 500 chars)
categorystringYesdata, llm, compute, storage, image, audio, search, finance, other
tagsstring[]NoSearchable tags
base_urlstringYesYour API's base URL (gateway forwards here)
pricing_modelstringYesper_call, monthly, one_time
price_per_call_usdcfloatif per_callPrice per API call in USDC
price_monthly_usdcfloatif monthlyMonthly price in USDC
price_one_time_usdcfloatif one_timeOne-time price in USDC
monthly_call_limitintNoMax calls per billing period (null = unlimited)
daily_call_limitintNoMax calls per day (null = unlimited)
rate_limit_rpmintNoRequests per minute (default: 60)

List My Listings

GET/api/marketplace/seller/listingsBearer JWT

List all your API listings.

Update Listing

PATCH/api/marketplace/seller/listings/{listing_id}Bearer JWT

Update a listing. All fields optional.

Delete Listing

DELETE/api/marketplace/seller/listings/{listing_id}Bearer JWT

Soft-delete (deactivate) a listing. Existing subscriptions remain active.

Seller Revenue

GET/api/marketplace/seller/revenueBearer JWT

Get total marketplace revenue summary.

// Response
{
  "total_revenue_usdc": 1542.00,
  "total_subscriptions": 23,
  "total_calls": 15420
}

Seller Analytics

GET/api/marketplace/seller/analyticsBearer JWT

Detailed analytics: daily breakdown, per-listing stats, top buyers.

ParamTypeDefaultDescription
daysint30Look-back period (1-365)
// Response
{
  "period_from": "2026-02-07",
  "period_to": "2026-03-09",
  "summary": {
    "total_calls": 15420,
    "total_revenue_usdc": 1542.00,
    "active_subscribers": 23,
    "total_listings": 5
  },
  "daily": [
    { "date": "2026-03-09", "calls": 523, "revenue_usdc": 52.30, "unique_callers": 12 }
  ],
  "by_listing": [
    { "listing_id": "...", "name": "Weather API", "calls": 8000, "revenue_usdc": 800.00, "subscribers": 15 }
  ],
  "top_buyers": [
    { "buyer_identifier": "agent@ai.com", "calls": 3000, "revenue_usdc": 300.00 }
  ]
}

Create API Key (Buyer)

POST/api/marketplace/keysBearer JWT

Create an additional API key for one of your subscriptions. Supports HTTP method scoping.

FieldTypeRequiredDescription
subscription_idstringYesSubscription to create key for
namestringNoKey name (default: "default")
scopesstring[]NoHTTP methods allowed: ["GET", "POST", ...]. Null = all methods.
// Request: Create a read-only key
{
  "subscription_id": "sub_uuid",
  "name": "readonly-production",
  "scopes": ["GET"]
}

// Response (201) — api_key only returned once
{
  "id": "key_uuid",
  "subscription_id": "sub_uuid",
  "name": "readonly-production",
  "api_key_prefix": "a1b2c3d4",
  "scopes": ["GET"],
  "is_active": true,
  "api_key": "a1b2c3d4e5f6...64chars"
}

List API Keys

GET/api/marketplace/keys/{subscription_id}Bearer JWT

List all API keys for a subscription you own.

Revoke API Key

DELETE/api/marketplace/keys/{key_id}Bearer JWT

Revoke an API key immediately. The key becomes unusable. Other keys on the same subscription are not affected.

Marketplace Webhook Events

Set your webhook_url in your merchant profile. The following events are sent for marketplace activity:

EventTrigger
subscription.createdA buyer activates a subscription to your API
quota.warningA subscriber reaches 80% of their total call limit
quota.reachedA subscriber exhausts their total call limit
daily_quota.reachedA subscriber hits the daily call limit
// Example: subscription.created webhook
{
  "event": "subscription.created",
  "data": {
    "listing_id": "uuid",
    "listing_name": "Weather API",
    "subscription_id": "sub_uuid",
    "buyer_identifier": "agent@ai.com",
    "pricing_model": "per_call",
    "price_usdc": 10.0,
    "calls_used": 0,
    "calls_limit": 1000
  }
}

AI Agents

Any AI agent can self-register on Onchor, fund itself, discover APIs, purchase access, and start consuming — all autonomously. No human account needed.

Works with any AI agent. Claude, GPT, Gemini, open-source, custom scripts — anything that can make HTTP requests. One call to register, one header for everything.

Discover (LLM-readable)

GET/api/agents/discoverPublic

Returns a plain-text guide that any LLM can read and understand. Contains all endpoints, auth instructions, pricing, and a complete workflow. Give this single URL to any AI agent.

Usage

curl https://api.onchor.xyz/api/agents/discover
This is the entry point for agents. An LLM cannot browse your website or read your docs page. Give it https://api.onchor.xyz/api/agents/discover and it will know exactly how to register, fund, browse, purchase, and consume APIs.

Self-Register (for agents)

POST/api/agents/registerPublic

An agent calls this once and gets back everything it needs: a token, a wallet address, and a list of endpoints. No human setup required.

Request

{
  "name": "Claude Code Agent"
}

Response (201)

{
  "agent_token": "oat_a1b2c3...",
  "wallet_address": "7xK9...",
  "wallet_explorer_url": "https://solscan.io/account/7xK9...",
  "balance_usdc": 0.0,
  "endpoints": {
    "check_balance": "GET /api/balance/me",
    "browse_apis": "GET /api/marketplace/listings?q=QUERY",
    "purchase_api": "POST /api/balance/me/purchase?listing_id=ID",
    "call_api": "ANY /api/gateway/SLUG/PATH (header: X-Marketplace-Key)",
    ...
  }
}

Detect Deposit

POST/api/agents/depositAgent Token (oat_...)

Check your wallet for USDC and credit your balance. Call this after sending USDC to your wallet address.

Agent Info

GET/api/agents/meAgent Token (oat_...)

Get your wallet address, balance, and status.

Create Token (for merchants)

POST/api/agent-tokensBearer JWT / X-API-Key

Create a new agent token. The raw token is returned once — save it securely.

Request

{
  "name": "My Claude Agent"
}

Response (201)

{
  "id": "uuid...",
  "name": "My Claude Agent",
  "token": "oat_a1b2c3...",
  "token_prefix": "oat_a1b2c3d4",
  "is_active": true,
  "created_at": "2026-03-09T..."
}

List Tokens

GET/api/agent-tokensBearer JWT / X-API-Key

List all your agent tokens (names, prefixes, status). Raw tokens are never shown again.

Revoke Token

DELETE/api/agent-tokens/{token_id}Bearer JWT / X-API-Key

Revoke a token immediately. Any agent using it will get 401.

Authenticated Balance Endpoints

These endpoints let your agent manage its balance without needing to know the buyer_identifier — it is derived from the authenticated merchant.

GET/api/balance/meAgent Token / JWT / X-API-Key

Check your current USDC balance.

POST/api/balance/me/deposit?amount_usdc=50Agent Token / JWT / X-API-Key

Create a deposit — returns a USDC address to fund.

POST/api/balance/me/purchase?listing_id={id}Agent Token / JWT / X-API-Key

Purchase API access from your balance. Returns the API key and gateway URL immediately.

GET/api/balance/me/historyAgent Token / JWT / X-API-Key

View your balance transaction history.

GET/api/balance/me/subscriptionsAgent Token / JWT / X-API-Key

List your active API subscriptions with gateway URLs.

Full Agent Workflow

Here's how an AI agent uses Onchor from zero, completely autonomously:

# 1. Agent self-registers (no human needed)
curl -X POST https://api.onchor.xyz/api/agents/register \
  -H "Content-Type: application/json" \
  -d '{"name": "My AI Agent"}'
# -> { "agent_token": "oat_abc...", "wallet_address": "7xK9...", "endpoints": {...} }

# 2. Fund the wallet (send USDC to wallet_address)
# Then detect the deposit:
curl -X POST https://api.onchor.xyz/api/agents/deposit \
  -H "X-API-Key: oat_abc..."

# 3. Agent discovers APIs it needs
curl "https://api.onchor.xyz/api/marketplace/listings?q=sentiment+analysis"

# 4. Agent purchases API access (instant, from balance)
curl -X POST "https://api.onchor.xyz/api/balance/me/purchase?listing_id=LISTING_ID" \
  -H "X-API-Key: oat_abc..."
# -> { "api_key": "mkt_...", "gateway_base_url": "https://api.onchor.xyz/api/gateway/sentiment" }

# 5. Agent consumes the API
curl https://api.onchor.xyz/api/gateway/sentiment/analyze \
  -H "X-Marketplace-Key: mkt_..." \
  -H "Content-Type: application/json" \
  -d '{"text": "This product is amazing!"}'
Two paths, same result:
Self-registration — agent calls POST /api/agents/register and gets its own token. No human involved.
Merchant-created — merchant creates a token in Dashboard → Settings → Agent Tokens and gives it to the agent.
Both produce an oat_... token that works everywhere.
Two headers: X-API-Key: oat_... = agent identity (balance, purchases, management). X-Marketplace-Key: ... = consume a purchased API through the gateway.

Rate Limiting

ScopeLimit
General (per IP)60 requests / minute
API Key (checkout create)30 requests / minute
Gateway (per subscription)Seller-configured RPM (default: 60)
Gateway (daily quota)Seller-configured daily limit

When rate limited, the API returns 429 Too Many Requests. Wait and retry.