Integrations-Beispiele
Drei End-to-End-Beispiele, die die häufigsten Integrationsmuster mit Owning.pro abdecken. Jedes Beispiel enthält Implementierungen in curl, TypeScript und Python.
| # | Beispiel | Authentifizierung | Werkzeuge / Endpoints |
|---|---|---|---|
| 1 | Search & Evaluate | None (public) | GET /api/listings, GET /api/listings/:id.md |
| 2 | Publish a Listing | API key (write) | POST /api/api-keys, POST /api/upload, POST /api/listings, POST /api/listings/:id/publish |
| 3 | Use MCP Server | API key (optional) | search_listings, get_listing_markdown, create_listing, publish_listing |
| 4 | Track Analytics Events | None (public) | POST /api/track |
| 5 | Promote a Listing (Premium) | JWT (owner) | POST /api/listings/:id/premium, GET /api/listings/:id/premium-status *(planned)* |
Beispiel 1: Suchen & Auswerten
Szenario: Ein KI-Agent sucht nach Booten in einer bestimmten Kategorie, filtert nach Preis, ruft die Markdown-Detailansicht für das beste Ergebnis ab und bewertet, ob es ein gutes Angebot ist.
Dieses Beispiel verwendet nur öffentliche Lese-Endpoints. Es wird kein API-Schlüssel oder JWT benötigt. Ratenbegrenzung: 100 Anfragen/Minute pro IP.
Ablauf
1. Search listings (category=boats, max_price=200000)
→ GET /api/listings?category=boats&max_price=200000&limit=10
2. Pick the first result
3. Get the Markdown detail
→ GET /api/listings/{slug}.md
4. Parse and evaluatecurl
# Step 1: Search for boats under 200k
curl -s "https://api.owning.pro/api/listings?category=boats&max_price=200000&limit=10" \
| jq '.results[0].slug'
# → "lagoon-400-s2-JP12QW"
# Step 2: Get the Markdown detail
curl -s "https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW.md"
# → Returns Markdown with YAML frontmatter + structured sectionsTypeScript
// search-and-evaluate.ts
const API_BASE = "https://api.owning.pro";
async function searchAndEvaluate() {
// Step 1: Search for boats under 200k
const searchUrl = new URL(`${API_BASE}/api/listings`);
searchUrl.searchParams.set("category", "boats");
searchUrl.searchParams.set("max_price", "200000");
searchUrl.searchParams.set("limit", "10");
const searchRes = await fetch(searchUrl);
const searchData = await searchRes.json();
console.log(`Found ${searchData.pagination.total} listings`);
if (searchData.results.length === 0) {
console.log("No results found");
return;
}
// Step 2: Pick the first result
const topListing = searchData.results[0];
console.log(`Top result: ${topListing.title} — ${topListing.price.amount} ${topListing.price.currency}`);
// Step 3: Get the Markdown detail
const mdRes = await fetch(`${API_BASE}/api/listings/${topListing.slug}.md`);
const markdown = await mdRes.text();
// Step 4: Evaluate
console.log("\n--- Listing Detail (Markdown) ---\n");
console.log(markdown);
// Parse key info from the Markdown for evaluation
const priceMatch = markdown.match(/price:\s*(\d+)/);
const yearMatch = markdown.match(/Year.*?(\d{4})/);
const lengthMatch = markdown.match(/Length.*?([\d.]+)\s*m/);
const evaluation = {
title: topListing.title,
price: priceMatch ? parseInt(priceMatch[1]) : null,
year: yearMatch ? parseInt(yearMatch[1]) : null,
length: lengthMatch ? parseFloat(lengthMatch[1]) : null,
pricePerMeter: null as number | null,
};
if (evaluation.price && evaluation.length) {
evaluation.pricePerMeter = Math.round(evaluation.price / evaluation.length);
}
console.log("\n--- Evaluation ---");
console.log(JSON.stringify(evaluation, null, 2));
}
searchAndEvaluate().catch(console.error);Python
# search_and_evaluate.py
import requests
import re
import json
API_BASE = "https://api.owning.pro"
def search_and_evaluate():
# Step 1: Search for boats under 200k
params = {
"category": "boats",
"max_price": "200000",
"limit": "10",
}
resp = requests.get(f"{API_BASE}/api/listings", params=params)
data = resp.json()
print(f"Found {data['pagination']['total']} listings")
if not data["results"]:
print("No results found")
return
# Step 2: Pick the first result
top = data["results"][0]
print(f"Top result: {top['title']} — {top['price']['amount']} {top['price']['currency']}")
# Step 3: Get the Markdown detail
md_resp = requests.get(f"{API_BASE}/api/listings/{top['slug']}.md")
markdown = md_resp.text
# Step 4: Evaluate
print("\n--- Listing Detail (Markdown) ---\n")
print(markdown)
# Parse key info
price_match = re.search(r"price:\s*(\d+)", markdown)
year_match = re.search(r"Year.*?(\d{4})", markdown)
length_match = re.search(r"Length.*?([\d.]+)\s*m", markdown)
evaluation = {
"title": top["title"],
"price": int(price_match.group(1)) if price_match else None,
"year": int(year_match.group(1)) if year_match else None,
"length": float(length_match.group(1)) if length_match else None,
"price_per_meter": None,
}
if evaluation["price"] and evaluation["length"]:
evaluation["price_per_meter"] = round(evaluation["price"] / evaluation["length"])
print("\n--- Evaluation ---")
print(json.dumps(evaluation, indent=2))
if __name__ == "__main__":
search_and_evaluate()Beispiel 2: Ein Inserat veröffentlichen
Szenario: Ein Agent registriert ein Konto, meldet sich an, erstellt einen API-Schlüssel mit Schreibberechtigungen, lädt ein Bild hoch, erstellt ein Inserat und veröffentlicht es. Dies ist der vollständige End-to-End-Schreibablauf.
Dieses Beispiel erfordert ein JWT-Token (aus Registrierung/Login), um einen API-Schlüssel zu erstellen, und anschließend den API-Schlüssel für nachfolgende Schreibvorgänge. Ratenbegrenzungen: 10 Inserate/Tag, 50 Bild-Uploads/Tag.
Ablauf
1. Register an account → POST /api/auth/register
2. Login (get JWT) → POST /api/auth/login
3. Create an API key → POST /api/api-keys (JWT auth)
4. Upload an image → POST /api/upload (API key auth)
5. Create a listing → POST /api/listings (API key auth)
6. Publish the listing → POST /api/listings/:id/publish (API key auth)curl
# Step 1: Register
curl -s -X POST https://api.owning.pro/api/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"agent@example.com","password":"SecurePass123!","name":"AI Agent"}'
# Step 2: Login (get JWT)
JWT=$(curl -s -X POST https://api.owning.pro/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"agent@example.com","password":"SecurePass123!"}' \
| jq -r '.token')
# Step 3: Create an API key with write permission
API_KEY=$(curl -s -X POST https://api.owning.pro/api/api-keys \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $JWT" \
-d '{"name":"agent-key","permissions":["read","write"]}' \
| jq -r '.key')
echo "API Key: $API_KEY"
# Step 4: Upload an image
IMAGE_URL=$(curl -s -X POST https://api.owning.pro/api/upload \
-H "Authorization: Bearer $API_KEY" \
-F "file=@photo.jpg" \
| jq -r '.url')
# Step 5: Create a listing
LISTING_ID=$(curl -s -X POST https://api.owning.pro/api/listings \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d "{
\"title\": \"Apple iPhone 13 128GB Blue\",
\"description\": \"Apple iPhone 13 in blue, 128GB. Good condition, battery health 89%.\",
\"category_id\": \"electronics\",
\"condition\": \"good\",
\"price\": { \"amount\": 399, \"currency\": \"EUR\", \"negotiable\": true },
\"location\": { \"country\": \"ES\", \"city\": \"Madrid\", \"shipping\": true },
\"images\": [{ \"url\": \"$IMAGE_URL\" }],
\"tags\": [\"iphone\", \"apple\", \"smartphone\"]
}" \
| jq -r '.id')
echo "Listing ID: $LISTING_ID"
# Step 6: Publish the listing
curl -s -X POST "https://api.owning.pro/api/listings/$LISTING_ID/publish" \
-H "Authorization: Bearer $API_KEY"
# → { "id": "...", "status": "active", ... }TypeScript
// publish-listing.ts
const API_BASE = "https://api.owning.pro";
async function publishListing() {
// Step 1: Register
const regRes = await fetch(`${API_BASE}/api/auth/register`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: "agent@example.com",
password: "SecurePass123!",
name: "AI Agent",
}),
});
// Ignore 409 (already registered) — proceed to login
// Step 2: Login
const loginRes = await fetch(`${API_BASE}/api/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email: "agent@example.com",
password: "SecurePass123!",
}),
});
const { token: jwt } = await loginRes.json();
// Step 3: Create an API key
const keyRes = await fetch(`${API_BASE}/api/api-keys`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${jwt}`,
},
body: JSON.stringify({
name: "agent-key",
permissions: ["read", "write"],
}),
});
const { key: apiKey } = await keyRes.json();
console.log("API Key:", apiKey);
// Step 4: Upload an image
const formData = new FormData();
const imageBuffer = await fetch("https://example.com/photo.jpg").then((r) => r.blob());
formData.append("file", imageBuffer, "photo.jpg");
const uploadRes = await fetch(`${API_BASE}/api/upload`, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
body: formData,
});
const { url: imageUrl } = await uploadRes.json();
console.log("Image URL:", imageUrl);
// Step 5: Create a listing
const createRes = await fetch(`${API_BASE}/api/listings`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
title: "Apple iPhone 13 128GB Blue",
description: "Apple iPhone 13 in blue, 128GB. Good condition, battery health 89%.",
category_id: "electronics",
condition: "good",
price: { amount: 399, currency: "EUR", negotiable: true },
location: { country: "ES", city: "Madrid", shipping: true },
images: [{ url: imageUrl }],
tags: ["iphone", "apple", "smartphone"],
}),
});
const listing = await createRes.json();
console.log("Listing ID:", listing.id);
// Step 6: Publish
const pubRes = await fetch(`${API_BASE}/api/listings/${listing.id}/publish`, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
});
const published = await pubRes.json();
console.log("Published:", published.status); // → "active"
}
publishListing().catch(console.error);Python
# publish_listing.py
import requests
API_BASE = "https://api.owning.pro"
def publish_listing():
# Step 1: Register
requests.post(f"{API_BASE}/api/auth/register", json={
"email": "agent@example.com",
"password": "SecurePass123!",
"name": "AI Agent",
})
# Ignore 409 (already registered)
# Step 2: Login
login_resp = requests.post(f"{API_BASE}/api/auth/login", json={
"email": "agent@example.com",
"password": "SecurePass123!",
})
jwt = login_resp.json()["token"]
# Step 3: Create an API key
key_resp = requests.post(f"{API_BASE}/api/api-keys", json={
"name": "agent-key",
"permissions": ["read", "write"],
}, headers={"Authorization": f"Bearer {jwt}"})
api_key = key_resp.json()["key"]
print(f"API Key: {api_key}")
auth_headers = {"Authorization": f"Bearer {api_key}"}
# Step 4: Upload an image
with open("photo.jpg", "rb") as f:
upload_resp = requests.post(
f"{API_BASE}/api/upload",
headers=auth_headers,
files={"file": ("photo.jpg", f, "image/jpeg")},
)
image_url = upload_resp.json()["url"]
print(f"Image URL: {image_url}")
# Step 5: Create a listing
create_resp = requests.post(f"{API_BASE}/api/listings", json={
"title": "Apple iPhone 13 128GB Blue",
"description": "Apple iPhone 13 in blue, 128GB. Good condition, battery health 89%.",
"category_id": "electronics",
"condition": "good",
"price": {"amount": 399, "currency": "EUR", "negotiable": True},
"location": {"country": "ES", "city": "Madrid", "shipping": True},
"images": [{"url": image_url}],
"tags": ["iphone", "apple", "smartphone"],
}, headers={**auth_headers, "Content-Type": "application/json"})
listing = create_resp.json()
print(f"Listing ID: {listing['id']}")
# Step 6: Publish
pub_resp = requests.post(
f"{API_BASE}/api/listings/{listing['id']}/publish",
headers=auth_headers,
)
published = pub_resp.json()
print(f"Published: {published['status']}") # → "active"
if __name__ == "__main__":
publish_listing()Beispiel 3: MCP-Server verwenden
Szenario: Ein Agent verbindet sich mit dem Owning MCP-Server, listet verfügbare Tools auf, sucht nach Inseraten, ruft eines im Markdown-Format ab, erstellt ein neues Inserat und veröffentlicht es — alles über MCP-Tool-Aufrufe anstelle von direkten HTTP-Anfragen.
Dieses Beispiel erreicht dieselben Ziele wie Beispiel 1 und 2, jedoch über die Tool-Schnittstelle des MCP-Servers. Der MCP-Server übernimmt Auth-Header, URL-Konstruktion und Antwort-Parsing intern.
Ablauf
1. Initialize MCP session → method: "initialize"
2. List available tools → method: "tools/list"
3. Search listings → tool: "search_listings"
4. Get Markdown detail → tool: "get_listing_markdown"
5. Create a listing → tool: "create_listing"
6. Publish the listing → tool: "publish_listing"curl
MCP_URL="https://mcp.owning.pro/mcp"
API_KEY="own_aBcD123eFgH456iJkL789mNoP012qRsT345uVwX678yZ"
# Step 1: Initialize
curl -s -X POST "$MCP_URL" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}'
# Step 2: List tools
curl -s -X POST "$MCP_URL" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
# Step 3: Search listings
curl -s -X POST "$MCP_URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"jsonrpc":"2.0","id":3,"method":"tools/call",
"params":{"name":"search_listings","arguments":{
"query":"lagoon","category":"boats","limit":5
}}
}'
# Step 4: Get Markdown detail
curl -s -X POST "$MCP_URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"jsonrpc":"2.0","id":4,"method":"tools/call",
"params":{"name":"get_listing_markdown","arguments":{
"id_or_slug":"lagoon-400-s2-JP12QW"
}}
}'
# Step 5: Create a listing
curl -s -X POST "$MCP_URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"jsonrpc":"2.0","id":5,"method":"tools/call",
"params":{"name":"create_listing","arguments":{
"title":"Apple iPhone 13 128GB Blue",
"description":"Apple iPhone 13 in blue, 128GB. Good condition, battery health 89%.",
"category_id":"electronics",
"condition":"good",
"price":{"amount":399,"currency":"EUR","negotiable":true},
"location":{"country":"ES","city":"Madrid","shipping":true},
"tags":["iphone","apple","smartphone"]
}}
}'
# Step 6: Publish (use the listing ID from step 5)
curl -s -X POST "$MCP_URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $API_KEY" \
-d '{
"jsonrpc":"2.0","id":6,"method":"tools/call",
"params":{"name":"publish_listing","arguments":{
"id_or_slug":"apple-iphone-13-128gb-blue"
}}
}'TypeScript
// use-mcp-server.ts
const MCP_URL = "https://mcp.owning.pro/mcp";
const API_KEY = process.env.OWNING_API_KEY; // own_... format
let nextId = 1;
async function callMCP(method: string, params: Record<string, unknown> = {}) {
const res = await fetch(MCP_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
...(API_KEY ? { Authorization: `Bearer ${API_KEY}` } : {}),
},
body: JSON.stringify({
jsonrpc: "2.0",
id: nextId++,
method,
params,
}),
});
const data = await res.json();
if (data.error) throw new Error(data.error.message);
return data.result;
}
async function callTool(name: string, args: Record<string, unknown>) {
const result = await callMCP("tools/call", { name, arguments: args });
if (result.isError) {
throw new Error(result.content[0].text);
}
// Parse the text content as JSON if possible
const text = result.content[0].text;
try {
return JSON.parse(text);
} catch {
return text;
}
}
async function main() {
// Step 1: Initialize
const init = await callMCP("initialize", {});
console.log(`Connected to ${init.serverInfo.name} v${init.serverInfo.version}`);
// Step 2: List tools
const toolsList = await callMCP("tools/list", {});
console.log(`Available tools: ${toolsList.tools.map((t: any) => t.name).join(", ")}`);
// Step 3: Search listings
const searchResult = await callTool("search_listings", {
query: "lagoon",
category: "boats",
limit: 5,
});
console.log(`Found ${searchResult.results.length} listings`);
const topListing = searchResult.results[0];
console.log(`Top: ${topListing.title}`);
// Step 4: Get Markdown detail
const markdown = await callTool("get_listing_markdown", {
id_or_slug: topListing.slug,
});
console.log("\n--- Markdown ---");
console.log(typeof markdown === "string" ? markdown : JSON.stringify(markdown));
// Step 5: Create a listing (requires write API key)
if (!API_KEY) {
console.log("\nSkipping write operations — no API key set");
return;
}
const newListing = await callTool("create_listing", {
title: "Apple iPhone 13 128GB Blue",
description: "Apple iPhone 13 in blue, 128GB. Good condition, battery health 89%.",
category_id: "electronics",
condition: "good",
price: { amount: 399, currency: "EUR", negotiable: true },
location: { country: "ES", city: "Madrid", shipping: true },
tags: ["iphone", "apple", "smartphone"],
});
console.log(`Created listing: ${newListing.id}`);
// Step 6: Publish
const published = await callTool("publish_listing", {
id_or_slug: newListing.slug,
});
console.log(`Published: ${published.status}`);
}
main().catch(console.error);Python
# use_mcp_server.py
import requests
import json
import os
MCP_URL = "https://mcp.owning.pro/mcp"
API_KEY = os.environ.get("OWNING_API_KEY") # own_... format
_next_id = 1
def call_mcp(method, params=None):
global _next_id
headers = {"Content-Type": "application/json"}
if API_KEY:
headers["Authorization"] = f"Bearer {API_KEY}"
resp = requests.post(MCP_URL, headers=headers, json={
"jsonrpc": "2.0",
"id": _next_id,
"method": method,
"params": params or {},
})
_next_id += 1
data = resp.json()
if "error" in data:
raise Exception(data["error"]["message"])
return data["result"]
def call_tool(name, args):
result = call_mcp("tools/call", {"name": name, "arguments": args})
if result.get("isError"):
raise Exception(result["content"][0]["text"])
text = result["content"][0]["text"]
try:
return json.loads(text)
except json.JSONDecodeError:
return text
def main():
# Step 1: Initialize
init = call_mcp("initialize", {})
info = init["serverInfo"]
print(f"Connected to {info['name']} v{info['version']}")
# Step 2: List tools
tools_list = call_mcp("tools/list", {})
tool_names = [t["name"] for t in tools_list["tools"]]
print(f"Available tools: {', '.join(tool_names)}")
# Step 3: Search listings
search_result = call_tool("search_listings", {
"query": "lagoon",
"category": "boats",
"limit": 5,
})
print(f"Found {len(search_result['results'])} listings")
top = search_result["results"][0]
print(f"Top: {top['title']}")
# Step 4: Get Markdown detail
markdown = call_tool("get_listing_markdown", {
"id_or_slug": top["slug"],
})
print("\n--- Markdown ---")
if isinstance(markdown, str):
print(markdown)
else:
print(json.dumps(markdown, indent=2))
# Step 5: Create a listing (requires write API key)
if not API_KEY:
print("\nSkipping write operations — no API key set")
return
new_listing = call_tool("create_listing", {
"title": "Apple iPhone 13 128GB Blue",
"description": "Apple iPhone 13 in blue, 128GB. Good condition, battery health 89%.",
"category_id": "electronics",
"condition": "good",
"price": {"amount": 399, "currency": "EUR", "negotiable": True},
"location": {"country": "ES", "city": "Madrid", "shipping": True},
"tags": ["iphone", "apple", "smartphone"],
})
print(f"Created listing: {new_listing['id']}")
# Step 6: Publish
published = call_tool("publish_listing", {
"id_or_slug": new_listing["slug"],
})
print(f"Published: {published['status']}")
if __name__ == "__main__":
main()Beispiel 4: Analytics-Ereignisse verfolgen
Szenario: Eine Frontend-Anwendung sendet Analytics-Ereignisse an Owning, um Seitenaufrufe, Suchen, Anmeldungen und Inserat-Aufrufe zu verfolgen. Dieser Endpoint ist öffentlich und Fire-and-Forget — er gibt immer 202 Accepted zurück.
Client-IPs werden vor der Speicherung gehasht (SHA-256 + Salt). Rohe IPs werden niemals gespeichert.
Gültige Ereignistypen
page_view— includepath,referrersignup— includeuser_idlisting_created— includeuser_id,listing_idsearch— includequery,results_countlisting_view— includelisting_id
curl
# Track a page view
curl -s -X POST https://api.owning.pro/api/track -H "Content-Type: application/json" -d '{"event":"page_view","path":"/listings/bavaria-s36-coupe","referrer":"https://google.com"}'
# → 202 {"ok":true}
# Track a search
curl -s -X POST https://api.owning.pro/api/track -H "Content-Type: application/json" -d '{"event":"search","query":"yacht","results_count":142,"path":"/listings?q=yacht"}'
# → 202 {"ok":true}TypeScript
// Fire-and-forget analytics tracking
function track(event: string, data: Record<string, unknown> = {}) {
fetch("https://api.owning.pro/api/track", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ event, ...data }),
}).catch(() => {}); // silently fail — best-effort
}
track("page_view", { path: window.location.pathname, referrer: document.referrer });
track("search", { query: "yacht", results_count: 142 });Python
import requests
def track(event: str, **data):
"""Send analytics event (fire-and-forget)."""
try:
requests.post("https://api.owning.pro/api/track",
json={"event": event, **data}, timeout=5)
except Exception:
pass # best-effort
track("page_view", path="/listings", referrer="https://google.com")
track("search", query="yacht", results_count=142)Beispiel 5: Ein Inserat bewerben (Premium)
Szenario: Ein Verkäufer bewirbt sein Inserat mit einer Premium-Hervorhebung über Stripe-Checkout und prüft anschließend den Premium-Status.
Das DB-Schema für Premium-Inserate (premium_type, premium_until) ist live. Stripe-Checkout- und webhook-Endpoints werden derzeit eingerichtet. Die Beispiele zeigen die geplante API-Oberfläche.
Premium-Typen
featured— top of search results and category pagesurgent— urgent badge and priority placement
curl (geplant)
# Step 1: Initiate premium checkout (JWT — listing owner only)
curl -s -X POST https://api.owning.pro/api/listings/lst_01J.../premium -H "Authorization: Bearer <jwt>" -H "Content-Type: application/json" -d '{"premium_type":"featured","duration_days":7}'
# → 200 {"checkout_url":"https://checkout.stripe.com/c/pay/cs_...","session_id":"cs_...","premium_type":"featured","amount":499,"currency":"eur","duration_days":7}
# Step 2: Check premium status (public)
curl -s https://api.owning.pro/api/listings/lst_01J.../premium-status
# → 200 {"listing_id":"lst_01J...","premium_type":"featured","premium_until":"2026-07-18T18:00:00.000Z","is_active":true}TypeScript (geplant)
async function promoteListing(listingId: string, jwt: string, premiumType: "featured" | "urgent", durationDays = 7) {
const res = await fetch(`https://api.owning.pro/api/listings/${listingId}/premium`, {
method: "POST",
headers: { "Authorization": `Bearer ${jwt}`, "Content-Type": "application/json" },
body: JSON.stringify({ premium_type: premiumType, duration_days: durationDays }),
});
const data = await res.json();
window.location.href = data.checkout_url; // redirect to Stripe
}
async function getPremiumStatus(listingId: string) {
const res = await fetch(`https://api.owning.pro/api/listings/${listingId}/premium-status`);
return res.json();
}Python (geplant)
import requests
def promote_listing(listing_id: str, jwt: str, premium_type: str, duration_days: int = 7):
res = requests.post(f"https://api.owning.pro/api/listings/{listing_id}/premium",
headers={"Authorization": f"Bearer {jwt}", "Content-Type": "application/json"},
json={"premium_type": premium_type, "duration_days": duration_days})
return res.json()
def get_premium_status(listing_id: str):
res = requests.get(f"https://api.owning.pro/api/listings/{listing_id}/premium-status")
return res.json()Tipps & Best Practices
Wahl zwischen REST API und MCP-Server
| REST API verwenden, wenn… | MCP-Server verwenden, wenn… |
|---|---|
| Sie benötigen feingranulare Kontrolle über HTTP (benutzerdefinierte Header, Streaming, Caching) | Sie entwickeln einen KI-Agenten, der ein LLM zur Entscheidungsfindung nutzt |
| Sie integrieren in eine bestehende HTTP-basierte Codebasis | Sie möchten Tool-Beschreibungen und typisierte Parameter für das LLM |
| Sie benötigen Endpoints, die nicht von MCP-Tools abgedeckt sind (z. B. Authentifizierung, Schema) | Sie möchten einfacheren Code ohne HTTP-Boilerplate |
| Sie verarbeiten die Markdown/JSON-Feeds direkt | Sie verwenden einen Client, der MCP nativ unterstützt (Claude Desktop, Cursor, etc.) |
Ratenbegrenzungs-Verwaltung
- Suchergebnisse zwischenspeichern — wenn Sie wiederholt suchen, speichern Sie die Ergebnisse lokal zwischen, um Ratenbegrenzungen zu vermeiden.
- API-Schlüssel für höhere Limits verwenden — authentifizierte Anfragen erhalten 300 Anfragen/Min. gegenüber 100 Anfragen/Min. ohne Authentifizierung.
- Bild-Uploads bündeln — Sie erhalten 50 Uploads/Tag. Laden Sie alle Bilder für ein Inserat hoch, bevor Sie es erstellen.
- 429-Antworten behandeln — prüfen Sie den Retry-After-Header und machen Sie entsprechend eine Pause.
Sicherheit
- API-Schlüssel niemals fest im Code eintragen — verwenden Sie Umgebungsvariablen oder einen Secrets-Manager.
- Minimale Berechtigungen verwenden — wenn Sie nur lesen müssen, erstellen Sie einen schreibgeschützten Schlüssel.
- Schlüssel regelmäßig rotieren — löschen Sie alte Schlüssel und erstellen Sie periodisch neue.
- Ausschließlich HTTPS verwenden — alle Owning-Endpoints sind HTTPS. Verwenden Sie niemals HTTP.
Vollständige Endpoint-Dokumentation: API-Referenz
MCP-Tool-Details: MCP-Server-Schnellstart