Back to Browse

AgentMarginRouter MCP Server

Developer ToolsLow Risk10.0MCP RegistryRemote
Free

Server data from the Official MCP Registry

18 x402 pay-per-call tools for AI agents (USDC/USDT, Base): web, crypto, DeFi, FX, geo-IP, RSS, ENS

About

18 x402 pay-per-call tools for AI agents (USDC/USDT, Base): web, crypto, DeFi, FX, geo-IP, RSS, ENS

Remote endpoints: streamable-http: https://agent-margin-router-production.up.railway.app/mcp

Security Report

10.0
Low Risk10.0Low Risk

Valid MCP server (1 strong, 0 medium validity signals). No known CVEs in dependencies. Imported from the Official MCP Registry.

18 tools verified · Open access · No issues found

Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.

Permissions Required

This plugin requests these system permissions. Most are normal for its category.

HTTP Network Access

Connects to external APIs or services over the internet.

How to Connect

Remote Plugin

No local installation needed. Your AI client connects to the remote endpoint directly.

Add this to your MCP configuration to connect:

{
  "mcpServers": {
    "io-github-agentmarginrouter-agent-margin-router": {
      "url": "https://agent-margin-router-production.up.railway.app/mcp"
    }
  }
}

Documentation

View on GitHub

From the project's GitHub README.

Agent Margin Router

Live API Docs x402 Python Tests

EN — TL;DR: Pay-per-request data broker for AI agents. One API, 7 upstream providers, automatic routing by quality × cost × latency, paid with x402 micropayments (USDC or USDT on Base) — no account, no API key, no subscription. 3 free requests per wallet.

  • Live API: https://agent-margin-router-production.up.railway.app
  • Docs & examples: https://agentmarginrouter.com
  • Endpoints: POST /extract-clean (URL → clean JSON, $0.02) · POST /market-spread (net spread between two venues after fees, $0.05) · GET /health (free)
# Try it free (3 requests per wallet):
curl -X POST https://agent-margin-router-production.up.railway.app/market-spread \
  -H "Content-Type: application/json" -H "X-WALLET: 0xYourWallet" \
  -d '{"asset":"ethereum","buy_venue":"binance","sell_venue":"coinbase"}'

Full German documentation below · Vollständige deutsche Dokumentation folgt.

Einheitliche, per x402 bezahlte Routing-Schicht für AI-Agenten. Mehrere Daten-Provider → automatische Auswahl (Qualität, Preis, Latenz, Ausfallrisiko) → normalisiertes JSON → Bezahlung pro Request in USDC oder USDT auf Base.

Agent  ──POST /extract-clean──▶  Router ──▶ Provider-Scoring ──▶ bester Provider
  ▲                                │            (Fallback, Circuit-Breaker, Mindestmarge)
  │  402 Payment Required          │
  │  (USDC + USDT Optionen)        ▼
  └──X-PAYMENT (signiert)──▶  Facilitator /verify  ──▶ 200 OK + Daten  ──▶ /settle on-chain

Inhalt

  1. Was ist der Agent Margin Router
  2. Schnellstart (lokal ohne Docker)
  3. Deployment mit Docker-Compose auf Hetzner
  4. Cloudflare Tunnel Setup 4b. Deployment auf Railway
  5. Umgebungsvariablen
  6. API-Endpunkte mit Beispielen 6b. Enterprise: API-Keys, Rate-Limiting, Usage & Stats
  7. x402 Payment Flow
  8. Provider hinzufügen
  9. Tests & Projektstruktur

1. Was ist der Agent Margin Router

Der Router ist ein B2B-Daten-Broker für Maschinen. Ein AI-Agent stellt eine standardisierte Anfrage, der Router

  1. prüft den Cache (Redis),
  2. bewertet alle passenden Provider mit score = quality × success_probability / (price + latency_risk),
  3. führt den Request nur aus, wenn die Mindestmarge (Default 20 %) erhalten bleibt,
  4. fällt bei Fehlern automatisch auf den nächsten Provider zurück (Circuit-Breaker: 3 Fehler → 5 Minuten Pause),
  5. normalisiert die Antwort und liefert sie mit Aktualitäts- und Konfidenzangabe aus,
  6. protokolliert Kosten, Provider und Marge als strukturierte JSON-Logs.

Bezahlt wird pro Request über das x402-Protokoll (HTTP 402). Akzeptiert werden von Anfang an USDC und USDT auf Base. Neue Wallets erhalten 3 kostenlose Requests (Free Tier, in Redis getrackt).

MVP-Endpunkte:

EndpunktFunktionPreis (Default)
POST /extract-cleanURL → sauberes, schema-konformes JSON0,02 USD
POST /market-spreadNetto-Spread zwischen zwei Handelsplätzen nach Gebühren, Slippage & Transfer0,05 USD
GET /healthStatus, Redis, Provider, Uptime, Fehlerrate, Cache-Hit-Ratekostenlos

Provider (echte APIs):

NameCapabilityAnbieterKosten/Call (Default)Qualität
apify_web_scraperextractApify Actor apify/web-scraper (Headless-Browser, optional Proxy)~0,005 USD (wird aus usageTotalUsd nachjustiert)0.90
httpx_directextractdirekter HTTP-Abruf, kein JS~0,0002 USD0.60
binance_publicmarket_dataBinance Public API /ticker/24hr (Bid/Ask) via data-api.binance.visionapi.binance.com ist regional geo-blockiert0 USD, kein Key0.95
coinbase_publicmarket_dataCoinbase Public API /prices/{pair}/spot0 USD, kein Key0.90
coingeckomarket_dataCoinGecko /simple/price, /coins/{id}/tickers, optional /market_chart0 USD (Demo-Key, Rate-Limit)0.88
defillamamarket_dataDeFi Llama coins.llama.fi/prices/current/coingecko:{id} (Referenzpreis) + api.llama.fi/tvl/{slug} (DEX-Tiefe)0 USD, kein Key0.75
coinmarketcapmarket_dataCMC /cryptocurrency/quotes/latest (Fallback, nur Referenzpreis)~0,0004 USD (Credits)0.70

Ohne gesetzten API-Key wird ein Provider nicht geroutet (is_configured == False), taucht aber in /health mit configured: false auf. Der Free-Tier nutzt nur Provider mit cost_per_request <= FREE_TIER_MAX_PROVIDER_COST_USD (Default 0,001 USD) – also httpx_direct bzw. binance_public/coinbase_public/coingecko/defillama; /market-spread holt dabei jedes Leg vom eigenen Exchange-Feed (Binance Bid/Ask, Coinbase Spot), wenn Binance oder Coinbase Teil des Venue-Paars ist; teure Provider sind für kostenlose Requests gesperrt. /extract-clean prüft Ziel-URLs gegen private/interne Netze (SSRF-Guard: nur http/https, keine RFC-1918-, Loopback-, Link-Local- oder Metadata-Adressen, Prüfung pro Redirect-Hop); abgelehnte URLs liefern 422 und zählen nicht als Provider-Fehler.

Einschränkung /market-spread: Das Ergebnis ist eine Analyse, keine ausführbare Quote. Preise sind Last-Trade-Werte der Venue-Ticker (CoinGecko) bzw. ein Referenzpreis (CMC); „Liquidität“ wird aus 24h-Volumen und einem statischen Depth-Anteil je Venue geschätzt, nicht aus dem Orderbuch. Das Feld data_source/venue_data_source (ticker, mixed, reference) und note im Response kennzeichnen die Datenbasis.


2. Schnellstart (lokal ohne Docker)

Voraussetzungen: Python ≥ 3.11, optional ein lokaler Redis (ohne Redis nutzt die App im Dev-Modus automatisch einen In-Memory-Fallback).

git clone <dein-repo> agent_margin_router
cd agent_margin_router

python -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt

cp .env.example .env
# Für lokale Tests ohne Wallet/Zahlung:
sed -i 's/^PAYMENT_ENABLED=.*/PAYMENT_ENABLED=false/' .env
sed -i 's#^REDIS_URL=.*#REDIS_URL=redis://localhost:6379/0#' .env

uvicorn app.main:app --reload --port 8000

Dann:

curl -s localhost:8000/health | jq .status
curl -s -X POST localhost:8000/extract-clean \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://example.com/produkt/123"}' | jq

Interaktive API-Doku: http://localhost:8000/docs

Tests:

pytest -q

3. Deployment mit Docker-Compose auf Hetzner

Getestet für einen Hetzner Cloud Server (CX22 reicht für den Start, Ubuntu 24.04).

3.1 Server vorbereiten

ssh root@<SERVER-IP>

# System aktualisieren, Docker installieren
apt update && apt upgrade -y
curl -fsSL https://get.docker.com | sh

# Nicht-Root-User für den Betrieb
adduser --disabled-password --gecos "" deploy
usermod -aG docker deploy

# Firewall: nur SSH offen lassen (HTTP kommt über Cloudflare Tunnel, s. Abschnitt 4)
apt install -y ufw
ufw allow OpenSSH
ufw --force enable

3.2 Projekt deployen

su - deploy
git clone <dein-repo> agent_margin_router
cd agent_margin_router

cp .env.example .env
nano .env        # RECEIVER_WALLET, ENVIRONMENT=production, REDIS_REQUIRED=true, Preise, Provider-Keys

Wichtige Produktionswerte in .env:

ENVIRONMENT=production
REDIS_URL=redis://redis:6379/0
REDIS_REQUIRED=true
PAYMENT_ENABLED=true
RECEIVER_WALLET=0xDeineEchteWallet

Starten:

docker compose up -d --build
docker compose ps
docker compose logs -f app
curl -s localhost:8000/health | jq

Der App-Port 8000 ist im Compose-File nur auf 127.0.0.1 gebunden – von außen ist der Dienst ausschließlich über den Cloudflare Tunnel (oder einen eigenen Reverse Proxy) erreichbar.

3.3 Betrieb

docker compose pull && docker compose up -d --build   # Update
docker compose logs --tail=200 app                    # JSON-Logs
docker compose exec redis redis-cli INFO memory       # Redis prüfen
docker compose down                                   # Stoppen (Redis-Daten bleiben im Volume)

Beide Services haben Healthchecks und restart: unless-stopped; nach einem Server-Reboot startet der Stack automatisch.


4. Cloudflare Tunnel Setup

Mit einem Cloudflare Tunnel braucht der Hetzner-Server keinen offenen HTTP-Port; TLS, DDoS-Schutz und WAF übernimmt Cloudflare.

  1. Domain bei Cloudflare verwalten (Nameserver umstellen).

  2. Cloudflare Dashboard → Zero TrustNetworksTunnelsCreate a tunnel (Typ „Cloudflared“).

  3. Namen vergeben (z. B. agent-margin-router) und den angezeigten Token kopieren.

  4. Im Tunnel unter Public Hostname einen Eintrag anlegen:

    • Subdomain: api, Domain: deine-domain.tld
    • Service: HTTPapp:8000 (Docker-Servicename, da cloudflared im selben Compose-Netz läuft)
  5. Token in .env eintragen:

    CLOUDFLARE_TUNNEL_TOKEN=eyJhIjoi...
    
  6. Stack mit Tunnel-Profil starten:

    docker compose --profile tunnel up -d
    docker compose logs -f cloudflared      # "Registered tunnel connection" = erfolgreich
    curl -s https://api.deine-domain.tld/health | jq .status
    

Empfohlen zusätzlich im Cloudflare-Dashboard: Rate Limiting Rule auf /extract-clean und /market-spread (z. B. 60 Requests/Minute pro IP) sowie Bot Fight Mode deaktivieren, damit Agenten nicht blockiert werden.


4b. Deployment auf Railway

Alternative zu Hetzner/Docker-Compose. Das Repo enthält railway.toml (Dockerfile-Build, Healthcheck /health, Restart-Policy) und ein Procfile.

  1. Neues Railway-Projekt → Deploy from GitHub repo (oder railway up per CLI).
  2. Redis-Service hinzufügen (Datenbanken → Redis) und im App-Service die Variable REDIS_URL=${{Redis.REDIS_URL}} setzen; REDIS_REQUIRED=true.
  3. Variablen aus .env.example im Railway-Dashboard eintragen (RECEIVER_WALLET, APIFY_API_TOKEN, COINGECKO_API_KEY, CMC_API_KEY, ENVIRONMENT=production, ggf. ENABLED_TOKENS=USDC). PORT setzt Railway selbst – nicht überschreiben.
  4. Deploy abwarten, dann curl https://<railway-domain>/health – im Feld providers müssen die konfigurierten Provider configured: true melden.

Hinweis: Der Dockerfile-Start (uvicorn ... --port ${PORT:-8000}) läuft unverändert auch lokal und in docker-compose (dort bleibt Port 8000).


5. Umgebungsvariablen

Alle Variablen sind in .env.example mit Kommentaren dokumentiert. Die wichtigsten:

VariableBedeutungDefault
REDIS_URLRedis-Verbindung (Cache, Free-Tier-Zähler, Replay-Schutz)redis://redis:6379/0
REDIS_REQUIREDtrue: kein Start ohne Redis; false: In-Memory-Fallback (nur Dev)false
PAYMENT_ENABLEDfalse schaltet x402 komplett ab (nur lokal!)true
FACILITATOR_URLx402-Facilitator /verify-Endpunkt (PayAI)facilitator.payai.network
USDC_CONTRACT_ADDRESSUSDC auf Base0x8335…2913
USDT_CONTRACT_ADDRESSUSDT auf Base0xfde4…9bb2
PAYMENT_NETWORKx402-Netzwerkname (base, base-sepolia)base
RECEIVER_WALLETDeine Einnahmen-Wallet
EXTRACT_PRICE / MARKET_SPREAD_PRICEVerkaufspreise in USD0.02 / 0.05
FREE_TIER_LIMITKostenlose Requests pro Wallet3
MIN_MARGIN_PERCENTMindestmarge; darunter wird nicht ausgeführt20
DAILY_PROVIDER_BUDGET_USDTages-Kostendeckel pro Provider50
CACHE_TTL_SECONDSStandard-Cache-Dauer60
CIRCUIT_FAILURE_THRESHOLD / CIRCUIT_OPEN_SECONDSCircuit-Breaker3 / 300
ENABLED_TOKENSAkzeptierte Zahl-Token, kommagetrennt (USDC bis USDT real E2E getestet ist)USDC,USDT
APIFY_API_TOKENApify-Token für apify_web_scraper (leer = Provider aus)leer
APIFY_ACTOR_ID / APIFY_TIMEOUT_SECONDS / APIFY_USE_PROXYActor, Run-Timeout, Apify-Proxyapify~web-scraper / 90 / true
APIFY_COST_PER_REQUESTStartannahme Kosten/Call (wird aus Run-Usage nachgeführt)0.005
COINGECKO_API_KEYCoinGecko Demo/Pro-Key für coingeckoleer
COINGECKO_USE_MARKET_CHARTzusätzlich 24h-Volatilität abrufen (1 Call mehr)true
CMC_API_KEYCoinMarketCap-Key für Fallback coinmarketcapleer
HTTPX_FALLBACK_ENABLEDgünstigen httpx_direct-Fallback registrierentrue
FREE_TIER_MAX_PROVIDER_COST_USDKostendeckel je Call für Free-Tier-Requests0.001
PAID_MIN_QUALITYbezahlte Requests bevorzugen Provider ab dieser Qualität0.8
API_KEY_RATE_LIMIT_PER_MINUTE / API_KEY_RATE_LIMIT_MAXStandard-Limit pro API-Key je Fenster / harter Deckel100 / 1000
ANON_RATE_LIMIT_PER_MINUTELimit pro IP ohne API-Key und ohne X-WALLET10
RATE_LIMIT_WINDOW_SECONDSLänge des Sliding Windows60
API_KEY_ADMIN_SECRETWenn gesetzt: X-ADMIN-SECRET nötig für Key-Erzeugung/-Deaktivierung (in Produktion setzen)leer
USAGE_RECENT_LIMIT / USAGE_DAILY_TTL_SECONDSLetzte Requests pro Identität / Aufbewahrung der Tageswerte20 / 172800
CLOUDFLARE_TUNNEL_TOKENNur für --profile tunnelleer

Für Testnet-Betrieb (empfohlen vor Mainnet): PAYMENT_NETWORK=base-sepolia, PAYMENT_NETWORK_CHAIN_ID=84532, FACILITATOR_URL=https://x402.org/facilitator/verify und die Sepolia-Token-Adressen eintragen.


6. API-Endpunkte

GET /health (kostenlos)

curl -s https://api.deine-domain.tld/health | jq
{
  "status": "ok",
  "version": "0.1.0",
  "environment": "production",
  "timestamp": "2026-09-10T12:00:00Z",
  "uptime_seconds": 86400.2,
  "redis_connected": true,
  "redis_backend": "redis",
  "request_count": 1520,
  "error_count": 3,
  "error_rate": 0.002,
  "cache_hits": 610,
  "cache_misses": 900,
  "cache_hit_rate": 0.404,
  "providers": [
    {"name": "apify_web_scraper", "capability": "extract", "healthy": true, "circuit_open": false,
     "consecutive_failures": 0, "success_rate": 0.98, "avg_latency_ms": 61.2, "requests": 700}
  ]
}

status ist ok, degraded (z. B. eine Capability ohne gesunden Provider oder Redis im Memory-Fallback) oder down (HTTP 503, Redis fehlt und ist Pflicht).

POST /extract-clean (0,02 USD)

Request:

curl -s -X POST https://api.deine-domain.tld/extract-clean \
  -H 'Content-Type: application/json' \
  -H 'X-WALLET: 0xDeineWallet' \
  -d '{
        "url": "https://shop.example.com/produkt/123",
        "schema": {"type":"object","properties":{"title":{},"price":{},"currency":{},"availability":{}}},
        "max_age_seconds": 60
      }' | jq

Response:

{
  "data": {"title": "Shop – Product Overview", "price": 149.9, "currency": "EUR", "availability": "in_stock"},
  "sources_checked": 1,
  "freshness_seconds": 0,
  "confidence": 0.91,
  "cost_usdc": 0.02,
  "token": "USDC",
  "fetched_at": "2026-09-10T12:00:01Z",
  "routing": {"provider": "apify_web_scraper", "fallback_used": false,
              "providers_tried": ["apify_web_scraper"], "cache_hit": false, "latency_ms": 74}
}

Felder: schema (optional) formt die Ausgabe auf die Top-Level-Properties; max_age_seconds: 0 erzwingt einen frischen Abruf.

POST /market-spread (0,05 USD)

curl -s -X POST https://api.deine-domain.tld/market-spread \
  -H 'Content-Type: application/json' \
  -H 'X-WALLET: 0xDeineWallet' \
  -d '{"asset":"ETH","buy_venue":"binance","sell_venue":"coinbase","size_usd":5000}' | jq
{
  "asset": "ETH", "quote": "USDT", "buy_venue": "binance", "sell_venue": "coinbase",
  "buy_price": 4247.31, "sell_price": 4258.02,
  "gross_spread_bps": 25.2, "fees_bps": 70.0, "slippage_bps": 3.4,
  "transfer_cost": 1.31, "transfer_cost_bps": 2.62,
  "net_spread_bps": -50.8, "estimated_net_profit_usd": -25.4,
  "confidence": 0.86, "execution_risk": "high",
  "freshness_seconds": 4, "cost_usdc": 0.05, "token": "USDT",
  "fetched_at": "2026-09-10T12:00:02Z",
  "routing": {"provider": "coingecko", "fallback_used": false, "providers_tried": ["coingecko"], "cache_hit": false, "latency_ms": 58}
}

net_spread_bps = gross − fees − slippage − transfer_cost_bps. execution_risk berücksichtigt Nettospread, Slippage und Transferzeit. Der Dienst verkauft die Prüfung, nicht den Trade – es gibt keine risikofreie Arbitrage.

Fehlerformat

{"error": "http_error", "detail": "No extraction provider available: ..."}
StatusBedeutung
402Zahlung fehlt/ungültig oder Free Tier erschöpft (Body enthält Zahlungsoptionen)
422Validierungsfehler im Request
502Facilitator nicht erreichbar / Provider lieferte unbrauchbare Daten
401X-API-KEY unbekannt oder deaktiviert ({"error": "invalid_api_key"})
429Rate-Limit überschritten ({"error": "rate_limit_exceeded", "retry_after": …}, Header Retry-After)
503Kein Provider verfügbar oder Mindestmarge verletzt

6b. Enterprise: API-Keys, Rate-Limiting, Usage & Stats

Alle Endpunkte in diesem Abschnitt sind kostenlos (kein x402). Persistenz komplett in Redis; ohne Redis (REDIS_REQUIRED=false) greift derselbe In-Memory-Fallback wie beim Cache.

Authentifizierung per API-Key

Ein API-Key (UUID4) ist fest an eine Wallet gebunden. Wird der Header X-API-KEY gesendet, ermittelt der Router die Wallet automatisch – X-WALLET ist dann nicht mehr nötig. Free Tier und x402-Zahlung gelten unverändert (der Key ersetzt nur die Identifikation und bringt ein eigenes Rate-Limit mit).

# Key erzeugen (Label und Limit optional; Limit wird auf API_KEY_RATE_LIMIT_MAX gedeckelt)
curl -s -X POST https://<host>/api-keys/generate \
  -H 'Content-Type: application/json' \
  -d '{"wallet": "0xabc…", "label": "trading-bot-1", "rate_limit_per_minute": 300}'
# → 201 {"api_key": "6f1c…", "wallet": "0xabc…", "label": "trading-bot-1", "rate_limit_per_minute": 300, "active": true, …}

# Verwenden
curl -s -X POST https://<host>/extract-clean -H 'X-API-KEY: 6f1c…' \
  -H 'Content-Type: application/json' -d '{"url": "https://example.com"}'

# Key ansehen / deaktivieren (Deaktivierung ist idempotent; Usage-Historie bleibt erhalten)
curl -s https://<host>/api-keys/6f1c…
curl -s -X DELETE https://<host>/api-keys/6f1c…

Ist API_KEY_ADMIN_SECRET gesetzt, verlangen POST /api-keys/generate und DELETE /api-keys/{key} zusätzlich den Header X-ADMIN-SECRET: <secret> (sonst 403). Ohne Secret kann jeder Keys anlegen – nur für Entwicklung.

Rate-Limiting (Sliding Window, Redis ZSET)

AufruferIdentifikationLimitQuelle
API-KeyX-API-KEYrate_limit_per_minute des Keys (Default API_KEY_RATE_LIMIT_PER_MINUTE = 100)pro Key
WalletX-WALLETFree Tier: FREE_TIER_LIMIT (3) Requests pro Wallet, danach x402pro Wallet
AnonymkeinsANON_RATE_LIMIT_PER_MINUTE (10) pro Client-IPpro IP

Jede Antwort eines bezahlten Endpunkts (/extract-clean, /market-spread) – auch 402/429 – trägt:

X-RateLimit-Limit: 100        # Limit des Fensters (bei X-WALLET: Free-Tier-Limit)
X-RateLimit-Remaining: 97     # verbleibende Requests im aktuellen Fenster
X-RateLimit-Reset: 1789000000 # Unix-Timestamp, ab dem wieder Kontingent frei wird
Retry-After: 42               # nur bei 429

Das Fenster ist ein echtes Sliding Window (RATE_LIMIT_WINDOW_SECONDS, Default 60 s): jeder Request wird mit Zeitstempel in ein Redis-ZSET geschrieben, ältere Einträge fallen heraus. Bei Redis-Fehlern öffnet der Limiter (fail-open) und loggt eine Warnung – zahlende Kunden werden nie wegen eines Cache-Problems blockiert. Ein 429 verbraucht kein Free-Tier-Kontingent.

Usage-Tracking

Jeder erfolgreiche Request auf einem bezahlten Endpunkt wird pro Identität (wallet:0x… bzw. key:<uuid>) und global gezählt: Requests (heute/gesamt, bezahlt/frei, Cache-Hits), gezahlte Kosten, Provider-Kosten, Provider- und Endpunkt-Verteilung sowie die letzten USAGE_RECENT_LIMIT Requests.

curl -s 'https://<host>/usage?wallet=0xabc…'      # oder ?api_key=6f1c…
{
  "identity_type": "api_key", "wallet": "0xabc…", "api_key": "6f1c…", "label": "trading-bot-1", "api_key_active": true,
  "requests_today": 12, "requests_total": 340, "paid_requests_total": 337, "free_requests_total": 3, "cache_hits_total": 41,
  "cost_usd_today": 0.24, "cost_usd_total": 6.74,
  "providers": {"coingecko": 300, "coinmarketcap": 40}, "endpoints": {"/market-spread": 340},
  "free_tier": {"limit": 3, "used": 3, "remaining": 0},
  "rate_limit": {"limit": 300, "remaining": 298, "window_seconds": 60, "reset": 1789000060},
  "last_request_at": "2026-09-10T10:00:00+00:00",
  "last_requests": [{"at": "…", "path": "/market-spread", "provider": "coingecko", "mode": "paid", "token": "USDC",
                     "price_usd": 0.05, "provider_cost_usd": 0.0, "cache_hit": false, "latency_ms": 412, "status": 200}]
}

Hinweise: Usage per API-Key und per nackter Wallet werden getrennt geführt (der Key ist die Identität), der Free Tier ist jedoch wallet-gebunden und damit geteilt. Anonyme x402-Zahlungen werden der Payer-Wallet aus der Facilitator-Verifikation zugeordnet.

Globale Statistiken

curl -s https://<host>/stats
{
  "requests_total": 10234, "requests_today": 311, "paid_requests_total": 9800, "free_requests_total": 434,
  "cache_hits_total": 1200, "active_wallets_total": 87, "active_wallets_today": 14, "active_api_keys": 23,
  "revenue_usd_total": 312.55, "revenue_usd_today": 9.10, "provider_cost_usd_total": 41.02,
  "gross_margin_usd_total": 271.53, "providers": {"coingecko": 7000, "httpx_direct": 2100, "apify_web_scraper": 1134},
  "endpoints": {"/market-spread": 7000, "/extract-clean": 3234}, "last_request_at": "…", "generated_at": "…"
}

revenue_usd_* summiert nur tatsächlich per x402 bezahlte Requests; active_wallets_* zählt eindeutige Wallets (API-Key-Wallets, X-WALLET-Wallets und Payer-Wallets).


7. x402 Payment Flow

1) Agent → POST /extract-clean                       (ohne X-PAYMENT)
2) Router → 402 Payment Required
   {
     "x402Version": 1,
     "error": "payment_required",
     "accepts": [
       {"scheme":"exact","network":"base","asset":"0x8335…2913","asset_symbol":"USDC",
        "max_amount_required":"20000","pay_to":"0xDeineWallet","resource":"https://…/extract-clean", …},
       {"scheme":"exact","network":"base","asset":"0xfde4…9bb2","asset_symbol":"USDT",
        "max_amount_required":"20000", …}
     ],
     "facilitator": "https://facilitator.payai.network/verify",
     "free_tier": {"limit": 3, "how_to": "Send header X-WALLET: 0x… for free requests"}
   }
3) Agent wählt USDC ODER USDT, signiert eine EIP-3009 transferWithAuthorization
   (from = Agent, to = pay_to, value ≥ max_amount_required, nonce = zufällig)
4) Agent → POST /extract-clean  mit  X-PAYMENT: <base64(JSON-Payload)>
   optional: X-PAYMENT-TOKEN: USDT   (spart einen Verify-Roundtrip)
5) Router: lokale Checks (Netzwerk, Empfänger, Betrag, Replay-Nonce in Redis)
           → Facilitator /verify (erst USDC-Requirements, dann USDT, bzw. gemäß Hint)
6) Router führt Request aus → 200 OK + Daten
7) Router → Facilitator /settle (Transfer wird on-chain ausgeführt)
           Ergebnis im Header X-PAYMENT-RESPONSE (base64 JSON: success, transaction, payer)

Free Tier: Header X-WALLET: 0x… gewährt FREE_TIER_LIMIT Requests pro Wallet (Zähler in Redis, 30 Tage). Zusätzlich gilt ein IP-Deckel von 20 Free-Requests/Tag gegen Wallet-Enumeration. Der Header X-Free-Tier-Remaining zeigt den Reststand.

Sicherheit: Nonce-Replay-Schutz (Redis, PAYMENT_REPLAY_TTL_SECONDS), Betrags- und Empfängerprüfung vor dem Facilitator-Call, Secrets erscheinen nie in Logs (Scrubbing im JSON-Logger), Settlement erst nach erfolgreicher Auslieferung – ein fehlgeschlagenes Settlement wird als payment.settlement_failed_after_delivery geloggt.

Beispiel mit dem offiziellen x402-Client (TypeScript):

import { wrapFetchWithPayment } from "x402-fetch";
import { privateKeyToAccount } from "viem/accounts";

const account = privateKeyToAccount(process.env.AGENT_PK as `0x${string}`);
const fetchWithPay = wrapFetchWithPayment(fetch, account);

const res = await fetchWithPay("https://api.deine-domain.tld/market-spread", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ asset: "ETH", buy_venue: "binance", sell_venue: "coinbase" }),
});
console.log(await res.json());

8. Provider hinzufügen

  1. Adapter anlegen, z. B. app/services/providers/firecrawl_provider.py:

    import httpx
    from app.models.provider import ProviderResult
    from app.services.providers.base import BaseProvider, ProviderError
    
    
    class FirecrawlProvider(BaseProvider):
        @property
        def is_configured(self) -> bool:
            return bool(self.api_key)  # ohne Key wird der Provider nicht geroutet
    
        async def _execute(self, request: dict) -> ProviderResult:
            r = await self.http.post(  # gemeinsamer httpx.AsyncClient aus BaseProvider
                "https://api.firecrawl.dev/v1/scrape",
                headers={"Authorization": f"Bearer {self.api_key}"},
                json={"url": request["url"], "formats": ["extract"], "extract": {"schema": request.get("schema")}},
            )
            if r.status_code >= 400:
                raise ProviderError(self.name, f"HTTP {r.status_code}", retryable=r.status_code >= 500)
            payload = r.json()
            return ProviderResult(
                provider=self.name,
                data=payload["data"]["extract"],
                confidence=0.9,
                sources_checked=1,
                freshness_seconds=0,
            )
    
        async def _health_probe(self) -> bool:
            return bool(self.api_key)  # oder ein günstiger Ping-Endpunkt
    
  2. In app/services/providers/__init__.pybuild_default_providers() registrieren:

    FirecrawlProvider(
        ProviderConfig(
            name="firecrawl",
            capability=Capability.EXTRACT,
            cost_per_request=0.005,  # echte Kosten pro Call → fließt in Scoring + Margenprüfung
            base_quality=0.93,
            expected_latency_ms=1500,
            timeout_seconds=15,
            license=LicensePolicy(
                allowed_use=["derived_output"], max_cache_seconds=300, commercial_resale=True, contract_expiry=None
            ),
        ),
        api_key=settings.firecrawl_api_key,
    )
    
  3. Neues Feld in app/config.py anlegen (z. B. firecrawl_api_key: str = ""), Key in .env setzen (FIRECRAWL_API_KEY=...), Tests mit dem UpstreamStub aus tests/conftest.py ergänzen, deployen.

Der Router übernimmt automatisch: Scoring, Fallback-Reihenfolge, Circuit-Breaker, Tagesbudget, Lizenzprüfung (commercial_resale, contract_expiry) und Cache-TTL-Begrenzung (max_cache_seconds).

Für /market-spread muss data die Felder buy_price, sell_price, buy_fee_bps, sell_fee_bps, buy_liquidity_usd, sell_liquidity_usd, transfer_cost, transfer_minutes liefern (siehe Docstring in defi_provider.py).


9. Tests & Projektstruktur

pip install -r requirements-dev.txt
pytest -q            # 80 Tests: kostenlose Exchange-Feeds (Binance/Coinbase/DeFi Llama), Payment (USDC/USDT/Free Tier/Replay), Routing, Circuit-Breaker, Marge, Cache, Spread-Mathematik,
                     #           API-Keys, Sliding-Window-Rate-Limiting, Usage-Tracking, /stats
ruff check .

Tests benötigen keinen laufenden Redis (fakeredis) und keinen echten Facilitator (httpx MockTransport).

agent_margin_router/
├── app/
│   ├── main.py                 # App-Factory, CORS, Lifespan (Redis connect/disconnect), Metriken
│   ├── config.py               # pydantic-settings (.env)
│   ├── routers/                # /extract-clean, /market-spread, /health, enterprise.py (/api-keys, /usage, /stats)
│   ├── middleware/x402.py      # API-Key-Auflösung, Rate-Limits, 402-Challenge, Free Tier, Verify/Settle
│   ├── services/
│   │   ├── provider_router.py  # Scoring, Fallback, Circuit-Breaker, Mindestmarge, Tagesbudget
│   │   ├── cache.py            # redis.asyncio Wrapper, Hit/Miss, Free-Tier-Zähler, Replay-Schutz
│   │   ├── payment.py          # x402 Facilitator-Client (USDC + USDT)
│   │   ├── api_keys.py         # API-Key-Erzeugung/-Auflösung/-Deaktivierung (Redis)
│   │   ├── rate_limit.py       # Sliding-Window-Limiter (Redis ZSET), X-RateLimit-Header
│   │   ├── usage.py            # Usage-Tracking pro Wallet/Key + globale Stats
│   │   └── providers/          # base.py, scraping_provider.py, defi_provider.py
│   ├── models/                 # Pydantic v2 Request/Response/Provider-Modelle
│   └── utils/logging.py        # JSON-Logs mit Secret-Scrubbing
├── tests/
├── docker-compose.yml          # app + redis (+ cloudflared via --profile tunnel)
├── Dockerfile                  # Multi-Stage, non-root, Healthcheck
├── .env.example
├── requirements.txt / requirements-dev.txt / pyproject.toml
└── README.md

Rechtlicher Hinweis: Dieses Projekt ist eine technische Infrastruktur. Provider-Lizenzen, Datenschutz, Steuer- und Zahlungsrecht sind vor dem Produktivbetrieb eigenständig zu prüfen. Es werden keine Umsätze garantiert.

Reviews

No reviews yet

Be the first to review this server!