Skip to content

Ejemplos de integración

Tres ejemplos end-to-end que cubren los patrones de integración más comunes con Owning.pro. Cada ejemplo incluye implementaciones en curl, TypeScript y Python.

#EjemploAuthHerramientas / Endpoints
1Search & EvaluateNone (public)GET /api/listings, GET /api/listings/:id.md
2Publish a ListingAPI key (write)POST /api/api-keys, POST /api/upload, POST /api/listings, POST /api/listings/:id/publish
3Use MCP ServerAPI key (optional)search_listings, get_listing_markdown, create_listing, publish_listing
4Track Analytics EventsNone (public)POST /api/track
5Promote a Listing (Premium)JWT (owner)POST /api/listings/:id/premium, GET /api/listings/:id/premium-status *(planned)*

Ejemplo 1: Buscar y evaluar

Escenario: Un agente de IA busca barcos en una categoría específica, filtra por precio, obtiene el detalle en Markdown del primer resultado y evalúa si es una buena oferta.

Sin autenticación requerida

Este ejemplo usa solo endpoints públicos de lectura. No se necesita API key ni JWT. Límite: 100 peticiones/minuto por IP.

Flujo

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 evaluate

curl

# 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 sections

TypeScript

// 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()

Ejemplo 2: Publicar un anuncio

Escenario: Un agente registra una cuenta, inicia sesión, crea una API key con permisos de escritura, sube una imagen, crea un anuncio y lo publica. Este es el flujo de escritura completo end-to-end.

Autenticación requerida

Este ejemplo requiere un token JWT (de register/login) para crear una API key, y luego la API key para las operaciones de escritura. Límites: 10 anuncios/día, 50 subidas de imágenes/día.

Flujo

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()

Ejemplo 3: Usar el servidor MCP

Escenario: Un agente se conecta al servidor MCP de Owning, lista las herramientas disponibles, busca anuncios, obtiene uno en formato Markdown, crea un nuevo anuncio y lo publica — todo a través de llamadas a herramientas MCP en lugar de peticiones HTTP directas.

MCP vs REST

Este ejemplo logra los mismos objetivos que los Ejemplos 1 y 2, pero a través de la interfaz de herramientas del servidor MCP. El servidor MCP gestiona internamente las cabeceras de auth, la construcción de URLs y el análisis de respuestas.

Flujo

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()

Ejemplo 4: Registrar eventos de analítica

Escenario: Una aplicación frontend envía eventos de analítica a Owning para registrar vistas de página, búsquedas, registros y vistas de anuncios. Este endpoint es público y fire-and-forget — siempre devuelve 202 Accepted.

Privacidad

Las IPs de los clientes se hashean (SHA-256 + salt) antes de almacenarse. Las IPs en bruto nunca se persisten.

Tipos de evento válidos

  • page_view — include path, referrer
  • signup — include user_id
  • listing_created — include user_id, listing_id
  • search — include query, results_count
  • listing_view — include listing_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)

Ejemplo 5: Promocionar un anuncio (Premium)

Escenario: Un vendedor promociona su anuncio con una promoción premium featured vía checkout de Stripe, luego verifica el estado premium.

Estado de la función: En desarrollo

El esquema de BD para anuncios premium (premium_type, premium_until) está activo. Los endpoints de checkout de Stripe y webhooks están en desarrollo. Los ejemplos muestran la API planeada.

Tipos de premium

  • featured — top of search results and category pages
  • urgent — urgent badge and priority placement

curl (planeado)

# 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 (planeado)

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 (planeado)

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()

Consejos y buenas prácticas

Elegir entre API REST y servidor MCP

Usa la API REST cuando…Usa el servidor MCP cuando…
Necesitas control granular sobre HTTP (cabeceras personalizadas, streaming, caché)Estás construyendo un agente de IA que usa un LLM para decidir acciones
Estás integrando en un codebase existente basado en HTTPQuieres descripciones de herramientas y parámetros tipados para el LLM
Necesitas endpoints no cubiertos por las herramientas MCP (ej. auth, schema)Quieres código más simple sin el boilerplate de HTTP
Estás consumiendo los feeds de Markdown/JSON directamenteEstás usando un cliente que soporta MCP nativamente (Claude Desktop, Cursor, etc.)

Gestión de límites de uso

  • Cachea los resultados de búsqueda — si buscas repetidamente, cachea los resultados localmente para evitar alcanzar los límites.
  • Usa API keys para límites más altos — las peticiones autenticadas obtienen 300 req/min vs 100 req/min sin autenticar.
  • Sube imágenes en lote — tienes 50 subidas/día. Sube todas las imágenes de un anuncio antes de crearlo.
  • Gestiona las respuestas 429 — revisa la cabecera Retry-After y haz back-off en consecuencia.

Seguridad

  • Nunca hardcodees API keys — usa variables de entorno o un gestor de secretos.
  • Usa permisos mínimos — si solo necesitas leer, crea una key de solo lectura.
  • Rota las keys regularmente — elimina las keys antiguas y crea nuevas periódicamente.
  • Usa solo HTTPS — todos los endpoints de Owning son HTTPS. Nunca uses HTTP.
¿Necesitas más detalles?

Documentación completa de endpoints: Referencia de la API
Detalles de herramientas MCP: Inicio rápido del servidor MCP

Ejemplos de Código — API de Owning.pro | Owning Docs