Referencia de la API
La API de Owning.pro es una API RESTful para buscar, navegar, crear y gestionar anuncios clasificados. Todos los endpoints devuelven JSON; los endpoints de anuncios también soportan respuestas en Markdown. La API está disponible en https://api.owning.pro y proxy en https://owning.pro/api.
Un explorador de API interactivo (con Scalar) está disponible en https://api.owning.pro/api/docs. La especificación OpenAPI 3.1 está en /api/openapi.json.
Resumen de endpoints
| Método | Endpoint | Descripción | Auth |
|---|---|---|---|
| GET | /api/listings | Search listings with filters | — |
| POST | /api/listings | Create a listing (draft) | * |
| GET | /api/listings/{id} | Get listing detail (JSON) | — |
| GET | /api/listings/{id}.md | Get listing detail (Markdown) | — |
| PUT | /api/listings/{id} | Update listing (owner) | * |
| DELETE | /api/listings/{id} | Delete listing (owner) | * |
| PATCH | /api/listings/{id} | Change listing status | * |
| POST | /api/listings/{id}/publish | Publish a draft listing | * |
| POST | /api/listings/{id}/contact | Contact the seller | — |
| POST | /api/listings/{id}/images | Upload images to listing | * |
| GET | /api/listings.md | Listings catalog (Markdown) | — |
| GET | /api/categories | Category tree with counts | — |
| GET | /api/categories/{id} | Category detail | — |
| GET | /api/asset-types | List all asset type templates | — |
| GET | /api/asset-types/{type} | Get asset type template | — |
| POST | /api/auth/register | Register a new account | — |
| POST | /api/auth/login | Login (get JWT) | — |
| GET | /api/me | Get current user profile | * |
| POST | /api/api-keys | Create an API key | * |
| GET | /api/api-keys | List API keys | * |
| DELETE | /api/api-keys/{id} | Revoke an API key | * |
| POST | /api/upload | Upload a standalone image | * |
| GET | /api/schema/listing | Listing JSON Schema | — |
| GET | /api/schema/listing.md | Listing schema (Markdown) | — |
| GET | /.well-known/ai.json | Agent discovery document | — |
* = autenticación requerida (JWT o API key)
Autenticación
Los endpoints públicos de lectura (GET) no requieren autenticación. Las operaciones de escritura (POST, PUT, DELETE, PATCH) requieren autenticación.
docsApi.authDesc2
- Tokens de sesión JWT — se obtienen vía POST /api/auth/register o POST /api/auth/login. Válidos durante 7 días.
- API keys — claves de larga duración en formato own_..., creadas vía POST /api/api-keys. Soportan permisos read, write y admin.
La API detecta automáticamente el tipo de token: los tokens que empiezan por own_ se tratan como API keys; el resto como sesiones JWT.
Crear una API key
# 1. Register (or login if you have an account)
curl -X POST https://api.owning.pro/api/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"securepassword123","name":"Jane Doe"}'
# Response: { "user": {...}, "token": "eyJhbG...", "token_type": "bearer", "expires_in": 604800 }
# 2. Create an API key using the JWT
curl -X POST https://api.owning.pro/api/api-keys \
-H "Authorization: Bearer eyJhbG..." \
-H "Content-Type: application/json" \
-d '{"name":"My Integration Key","permissions":"write"}'
# Response: { "id":"apk_...", "key":"own_aBcD123eFgH...", "key_prefix":"own_aBcD1", ... }
# The plaintext key is shown ONCE - store it securely.
# 3. Use the API key for all subsequent requests
curl https://api.owning.pro/api/listings \
-H "Authorization: Bearer own_aBcD123eFgH..."Límites de uso
| Estado de auth | Límite | Ámbito |
|---|---|---|
| Unauthenticated | 100 req/min | per IP |
| Authenticated (JWT or API key) | 300 req/min | per user/key |
| Contact seller | 5 req/hour | per IP |
| Listing creation | 10 req/day | per user |
| Image upload | 50 req/day | per user |
| AI listing generation | 5 req/hour | per user |
Las respuestas limitadas devuelven 429 Too Many Requests con un error.code de rate_limited.
Formatos de respuesta
Los endpoints de anuncios soportan dos formatos de respuesta:
- JSON (por defecto) — application/json. Devuelve objetos de anuncio estructurados.
- Markdown — text/markdown. Devuelve un documento Markdown legible por máquinas con frontmatter YAML. Se solicita añadiendo .md a la URL o enviando Accept: text/markdown.
# JSON (default)
curl https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW
# Markdown - via .md suffix
curl https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW.md
# Markdown - via Accept header
curl https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW \
-H "Accept: text/markdown"Formato de errores
Todos los errores devuelven una estructura JSON consistente:
{
"error": {
"code": "not_found",
"message": "Listing not found",
"details": { "id": "lst_abc123" }
}
}| Estado HTTP | Código de error | Significado |
|---|---|---|
| 400 | bad_request | Malformed request |
| 400 | validation_error | Field validation failed |
| 400 | moderation_rejected | Content rejected by moderation |
| 401 | unauthorized | Missing or invalid auth |
| 403 | forbidden | Not the resource owner |
| 404 | not_found | Resource not found |
| 409 | conflict / duplicate_listing | Conflict (e.g. duplicate) |
| 429 | rate_limited | Rate limit exceeded |
| 500 | internal_error | Server error |
Anuncios
GET /api/listings
Busca y filtra anuncios con paginación, ordenación y filtros de atributos dinámicos. Público — sin auth requerida.
Parámetros de consulta
| Parámetro | Tipo | Por defecto | Descripción |
|---|---|---|---|
q | string | — | Full-text search (title, description, tags) |
category | string | — | Category ID or slug (hierarchical — includes descendants) |
seller_id | string | — | Filter by seller's user ID |
min_price | number | — | Minimum price (inclusive) |
max_price | number | — | Maximum price (inclusive) |
include_unpriced | boolean | false | Include unpriced listings when price filter is active |
condition | enum | — | new, like_new, good, fair, poor, refurbished |
country | string | — | ISO 3166-1 alpha-2 country code (e.g. ES) |
city | string | — | City name (case-insensitive) |
shipping | boolean | — | Filter by shipping availability |
type | enum | — | sale or wanted |
status | enum | active | active, paused, sold, expired, flagged, all |
page | integer | 1 | Page number (1-indexed) |
limit | integer | 20 | Results per page (max 100) |
sort | enum | relevance | newest, price_asc, price_desc, relevance |
attr[{key}] | string | — | Dynamic attribute filter (select type). See asset type template for available keys. |
attr[min_{key}] | number | — | Range filter minimum (numeric attributes) |
attr[max_{key}] | number | — | Range filter maximum (numeric attributes) |
Ejemplo de respuesta
{
"results": [
{
"id": "lst_01KX8G9BM7JKT48N9JZKJP12QW",
"slug": "lagoon-400-s2-JP12QW",
"title": "Lagoon 400 S2",
"description": "Gebrauchtboot; Baujahr 2016",
"category": { "id": "boats", "name": "Boats", "path": ["vehicles", "boats"] },
"condition": "good",
"type": "sale",
"price": { "amount": 295000, "currency": "EUR", "negotiable": true },
"location": { "country": "HR", "city": "Split", "shipping": false },
"images": [{ "url": "https://static.owning.pro/images/...", "alt": "..." }],
"attributes": { "beam": 7.25, "year": 2016, "brand": "lagoon", "length": 11.97 },
"seller": { "id": "usr_...", "name": "Owning Marketplace", "type": "agent", "verified": false, "member_since": "2026-07-10" },
"status": "active",
"created_at": "2026-07-11T11:51:29.151Z",
"updated_at": "2026-07-11T12:03:55.771Z",
"expires_at": "2026-08-10T11:51:22.151Z",
"views": 34,
"tags": ["lagoon", "2016"],
"meta": { "source": "scraped", "language": "en" }
}
],
"pagination": { "page": 1, "limit": 20, "total": 7668, "pages": 384 }
}Ejemplo: Búsqueda con filtros
# Search for boats between 50,000 and 150,000 EUR, sorted by price
curl "https://api.owning.pro/api/listings?category=boats&min_price=50000&max_price=150000&sort=price_asc&limit=10"
# Full-text search for "bavaria" in the boats category
curl "https://api.owning.pro/api/listings?q=bavaria&category=boats"
# Filter by brand attribute and length range
curl "https://api.owning.pro/api/listings?category=boats&attr[brand]=bavaria&attr[min_length]=10&attr[max_length]=15"
# Get listings in Spain with shipping available
curl "https://api.owning.pro/api/listings?country=ES&shipping=true"Códigos de estado
| Código | Significado |
|---|---|
| 200 | Success — paginated listings |
| 400 | Invalid query parameters |
| 429 | Rate limit exceeded |
POST /api/listings
docsApi.createListingDesc
Cuerpo de la petición
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
title | string | Yes | 5–120 characters |
description | string | Yes | 20–5000 characters |
category_id | string | Yes | Category ID (e.g. boats) |
condition | enum | Yes | new, like_new, good, fair, poor, refurbished |
type | enum | No | sale (default) or wanted |
price | object | Yes | { amount, currency, negotiable } |
location | object | Yes | { country, city, postal_code?, lat?, lng?, shipping } |
images | array | No | Up to 10 images: [{ url, alt? }]}. Use POST /api/upload first. |
attributes | object | No | Category-specific attributes (see asset type template) |
tags | array | No | Up to 10 tags, each 1–50 characters |
Ejemplo
curl -X POST https://api.owning.pro/api/listings \
-H "Authorization: Bearer own_aBcD123eFgH..." \
-H "Content-Type: application/json" \
-d '{
"title": "Apple iPhone 13 128GB Blue",
"description": "Apple iPhone 13 in blue, 128GB storage. Good condition with minor wear. Battery health 89%.",
"category_id": "electronics",
"condition": "good",
"type": "sale",
"price": { "amount": 399, "currency": "EUR", "negotiable": true },
"location": { "country": "ES", "city": "Madrid", "shipping": true },
"images": [{ "url": "https://static.owning.pro/images/tmp/iphone13-1.webp", "alt": "iPhone 13 front" }],
"attributes": { "brand": "Apple", "model": "iPhone 13", "storage": "128GB" },
"tags": ["iphone", "apple", "smartphone", "128gb"]
}'Códigos de estado
| Código | Significado |
|---|---|
| 201 | Listing created (in draft status) |
| 400 | Validation error or moderation rejection |
| 401 | Authentication required |
| 409 | Duplicate listing (same seller, same title + description) |
| 429 | Daily creation limit exceeded (10/day) |
GET /api/listings/{id}
Obtiene un anuncio por ID o slug. Público. La respuesta de detalle incluye long_description (si está disponible), que no está en las respuestas de lista.
Ejemplo
curl https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW
# Returns full listing JSON with long_description, all attributes, all images| Código | Significado |
|---|---|
| 200 | Listing detail |
| 404 | Listing not found |
GET /api/listings/{id}.md
Obtiene un anuncio como Markdown legible por máquinas. Público. El Markdown incluye frontmatter YAML con metadatos clave, seguido de secciones estructuradas.
Ejemplo de respuesta
---
id: "lst_01KX8G9BM7JKT48N9JZKJP12QW"
slug: "lagoon-400-s2-JP12QW"
title: "Lagoon 400 S2"
price: 295000
currency: "EUR"
negotiable: true
condition: "good"
type: "sale"
status: "active"
category:
id: "boats"
name: "Boats"
path: ["vehicles", "boats"]
location:
country: "HR"
city: "Split"
shipping: false
seller:
id: "usr_01KX6SQGZYHSBDCC3D6GSBNFV8"
name: "Owning Marketplace"
type: "agent"
verified: false
member_since: "2026-07-10"
images:
- url: "https://static.owning.pro/images/..."
alt: "Lagoon 400 S2"
tags: ["lagoon", "2016"]
---
## Description
Gebrauchtboot; Baujahr 2016
## Specifications
- **Brand**: lagoon
- **Year**: 2016
- **Length**: 11.97 m
- **Beam**: 7.25 m
- **Boat type**: Katamaran
- **Engine**: 2 x 40 PS / 29 kWPUT /api/listings/{id}
Actualiza un anuncio. Actualización parcial — solo se cambian los campos proporcionados. Auth requerida — solo el propietario.
curl -X PUT https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW \
-H "Authorization: Bearer own_aBcD123eFgH..." \
-H "Content-Type: application/json" \
-d '{ "price": { "amount": 280000, "currency": "EUR", "negotiable": false } }'| Código | Significado |
|---|---|
| 200 | Updated listing |
| 403 | Not the listing owner |
| 404 | Listing not found |
DELETE /api/listings/{id}
Borrado lógico de un anuncio. Auth requerida — solo el propietario.
curl -X DELETE https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW \
-H "Authorization: Bearer own_aBcD123eFgH..."
# Response: { "deleted": true, "id": "lst_01KX..." }PATCH /api/listings/{id}
Cambia el estado de un anuncio. Auth requerida — solo el propietario. Transiciones permitidas: active, paused, sold.
curl -X PATCH https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW \
-H "Authorization: Bearer own_aBcD123eFgH..." \
-H "Content-Type: application/json" \
-d '{ "status": "paused" }'POST /api/listings/{id}/publish
Publica un anuncio borrador — lo mueve de draft a active. Auth requerida — solo el propietario.
curl -X POST https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW/publish \
-H "Authorization: Bearer own_aBcD123eFgH..."| Código | Significado |
|---|---|
| 200 | Listing published (now active) |
| 403 | Not the listing owner |
| 404 | Listing not found |
| 409 | Listing is not in draft status |
POST /api/listings/{id}/contact
Envía un mensaje al propietario del anuncio por email. Público — sin auth requerida. El email del vendedor nunca se expone. Límite: 5 por hora por IP.
Cuerpo de la petición
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | Yes | 1–100 characters |
email | string | Yes | Reply-to email (max 200) |
message | string | Yes | 1–2000 characters |
curl -X POST https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW/contact \
-H "Content-Type: application/json" \
-d '{
"name": "Jane Doe",
"email": "jane@example.com",
"message": "Is this still available? Can you send more photos?"
}'
# Response: { "message": "Your message has been sent to the listing owner." }POST /api/listings/{id}/images
Sube imágenes a un anuncio existente (solo propietario). Auth requerida. Datos multipart, hasta 10 imágenes por petición. Límite: 50 imágenes por día.
curl -X POST https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW/images \
-H "Authorization: Bearer own_aBcD123eFgH..." \
-F "files=@photo1.jpg" \
-F "files=@photo2.jpg"
# Response: { "listing_id": "...", "slug": "...", "images": [...], "uploaded": 2 }GET /api/listings.md
Obtiene el catálogo completo de anuncios en Markdown. Público. Soporta los mismos parámetros de consulta que GET /api/listings. Útil para agentes que quieren navegar el catálogo en una sola petición.
curl "https://api.owning.pro/api/listings.md?category=boats&limit=5"Categorías
GET /api/categories
Obtiene el árbol completo de categorías con conteos de anuncios. Público. Jerárquico con count (anuncios activos) y children en cada nodo.
Parámetros de consulta
| Parámetro | Tipo | Descripción |
|---|---|---|
hide_empty | boolean | Hide categories with zero listings (parents with non-empty children are kept) |
curl https://api.owning.pro/api/categories
# Response:
{
"categories": [
{
"id": "vehicles",
"name": "Vehicles",
"slug": "vehicles",
"count": 7668,
"children": [
{ "id": "boats", "name": "Boats", "slug": "boats", "count": 7668, "children": [] },
{ "id": "cars", "name": "Cars", "slug": "cars", "count": 0, "children": [] }
]
}
]
}GET /api/categories/{id}
Obtiene una categoría por ID o slug, con sus hijos directos y conteos. Público.
curl https://api.owning.pro/api/categories/boatsTipos de asset
Los tipos de asset definen plantillas de atributos estructurados por categoría. Indican qué campos están disponibles para los anuncios en una categoría dada y cuáles son filtrables en la búsqueda.
GET /api/asset-types
Lista todas las plantillas de tipos de asset disponibles. Público.
GET /api/asset-types/{type}
Obtiene una definición de tipo de asset con todos sus atributos. Público. La respuesta incluye qué atributos son filtrables y sus tipos de filtro (range, select, boolean).
curl https://api.owning.pro/api/asset-types/boats
# Response:
{
"id": "boats",
"label": "Boats",
"description": "Watercraft - sailboats, motorboats, catamarans, yachts",
"categoryIds": ["boats"],
"attributes": [
{ "key": "brand", "label": "Brand", "type": "string", "filterable": true, "filterType": "select" },
{ "key": "year", "label": "Year", "type": "number", "filterable": true, "filterType": "range" },
{ "key": "length", "label": "Length", "type": "number", "filterable": true, "filterType": "range", "unit": "m" },
{ "key": "boat_type", "label": "Boat Type", "type": "string", "filterable": true, "filterType": "select" }
]
}Usa los atributos filtrables para construir parámetros de consulta attr[...] para GET /api/listings.
Auth y API Keys
POST /api/auth/register
Regístrate con email y contraseña. Público. Devuelve un token de sesión JWT inmediatamente.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
email | string | Yes | Valid email address |
password | string | Yes | 8–128 characters |
name | string | No | Display name (1–100 chars) |
curl -X POST https://api.owning.pro/api/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"securepassword123","name":"Jane Doe"}'
# Response (201):
{
"user": { "id": "usr_...", "email": "user@example.com", "name": "Jane Doe", "type": "human", "verified": false, "created_at": "..." },
"token": "eyJhbGciOiJIUzI1NiJ9...",
"token_type": "bearer",
"expires_in": 604800
}| Código | Significado |
|---|---|
| 201 | Account created, session token returned |
| 409 | Email already registered |
POST /api/auth/login
Inicia sesión con email y contraseña. Público. Devuelve un JWT válido durante 7 días.
curl -X POST https://api.owning.pro/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"securepassword123"}'| Código | Significado |
|---|---|
| 200 | Login successful |
| 401 | Invalid email or password |
GET /api/me
Obtiene el perfil del usuario autenticado. Auth requerida.
curl https://api.owning.pro/api/me \
-H "Authorization: Bearer own_aBcD123eFgH..."
# Response: { "user": { "id": "usr_...", "email": "...", "name": "...", "type": "human", ... } }POST /api/api-keys
Genera una nueva API key. Auth requerida (sesión JWT). La clave en texto plano se devuelve una sola vez — guárdala de forma segura.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | Yes | Label for the key (1–100 chars) |
permissions | enum | No | read, write (default), admin |
curl -X POST https://api.owning.pro/api/api-keys \
-H "Authorization: Bearer eyJhbG..." \
-H "Content-Type: application/json" \
-d '{"name":"My Integration Key","permissions":"write"}'
# Response (201):
{
"id": "apk_01JX...",
"key": "own_aBcD123eFgH456iJkL789mNoP012qRsT345uVwX678yZ",
"key_prefix": "own_aBcD1",
"label": "My Integration Key",
"permissions": "write",
"created_at": "2026-07-10T12:00:00.000Z",
"message": "Store this API key securely. It will not be shown again."
}GET /api/api-keys
Lista las API keys del usuario autenticado (sin texto plano). Auth requerida.
curl https://api.owning.pro/api/api-keys \
-H "Authorization: Bearer eyJhbG..."
# Response: { "keys": [{ "id": "apk_...", "key_prefix": "own_aBcD1", "label": "...", "permissions": "write", ... }] }DELETE /api/api-keys/{id}
Revoca (borrado lógico) una API key. Auth requerida — solo el propietario de la clave puede revocarla.
curl -X DELETE https://api.owning.pro/api/api-keys/apk_01JX... \
-H "Authorization: Bearer eyJhbG..."
# Response: { "id": "apk_...", "revoked": true, "revoked_at": "..." }Subida de imágenes
POST /api/upload
Sube una imagen independiente (sin anuncio requerido). Auth requerida. La imagen se convierte a webp. Límite: 50 imágenes por día. Usa esto para obtener URLs de imágenes antes de crear un anuncio.
curl -X POST https://api.owning.pro/api/upload \
-H "Authorization: Bearer own_aBcD123eFgH..." \
-F "file=@photo.jpg"
# Response (201):
{ "url": "https://static.owning.pro/images/tmp-usr01-abc/1.webp", "alt": "photo" }| Código | Significado |
|---|---|
| 201 | Image uploaded |
| 400 | Invalid file type or size (max 10MB; jpg, png, webp, heic, heif, avif) |
| 401 | Authentication required |
| 429 | Daily upload limit exceeded (50/day) |
Schema
GET /api/schema/listing
Obtiene el JSON Schema (draft 2020-12) para el tipo de dato Listing. Público. Úsalo para validar datos de anuncios antes de enviarlos vía POST /api/listings.
curl https://api.owning.pro/api/schema/listing
# Returns a JSON Schema document:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://owning.pro/api/schema/listing",
"title": "Owning Listing",
"type": "object",
"required": ["id", "slug", "title", "description", "category", ...],
"properties": { ... }
}GET /api/schema/listing.md
Obtiene el schema del anuncio en formato Markdown para agentes de IA. Público.
curl https://api.owning.pro/api/schema/listing.mdDescubrimiento de agentes
GET /.well-known/ai.json
Descripción legible por máquinas de la superficie de la API para auto-descubrimiento de agentes. Público. Devuelve rutas de endpoints, esquema de auth, límites de uso, formatos de respuesta y referencias a schemas. También detectable vía la directiva AI-Discovery en /robots.txt.
curl https://owning.pro/.well-known/ai.json
# Response:
{
"name": "Owning",
"description": "Classifieds portal - buy and sell new and second-hand items",
"api": {
"base_url": "https://owning.pro/api",
"version": "v1",
"endpoints": {
"listings": "/api/listings",
"categories": "/api/categories",
"schema": "/api/schema/listing",
"auth": "/api/auth",
"interactive_docs": "/api/docs",
"openapi_spec": "/api/openapi.json"
},
"auth": {
"type": "api_key",
"header": "Authorization",
"prefix": "Bearer",
"docs": "/api/schema/listing.md"
}
},
"formats": ["json", "markdown"]
}docsApi.calloutTipDesc