Référence de l'API
L'API d'Owning.pro est une API RESTful pour rechercher, parcourir, créer et gérer des annonces classées. Tous les endpoints renvoient du JSON ; les endpoints d'annonces prennent également en charge les réponses Markdown. L'API est disponible à https://api.owning.pro et proxyée à https://owning.pro/api.
Un explorateur d'API interactif (propulsé par Scalar) est disponible à https://api.owning.pro/api/docs. La spécification OpenAPI 3.1 brute se trouve à /api/openapi.json.
Résumé des endpoints
| Méthode | Endpoint | Description | Authentification |
|---|---|---|---|
| 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 | — |
* = authentification requise (JWT ou clé API)
Authentification
Les endpoints de lecture publics (GET) ne nécessitent pas d'authentification. Les opérations d'écriture (POST, PUT, DELETE, PATCH) nécessitent une authentification.
docsApi.authDesc2
- Tokens de session JWT — obtenus via POST /api/auth/register ou POST /api/auth/login. Valides pendant 7 jours.
- Clés API — clés de longue durée au format own_..., créées via POST /api/api-keys. Prennent en charge les permissions read, write et admin.
L'API détecte automatiquement le type de token : les tokens commençant par own_ sont traités comme des clés API ; tous les autres sont traités comme des sessions JWT.
Créer une clé API
# 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..."Limites de débit
| État d'authentification | Limite | Portée |
|---|---|---|
| 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 |
Les réponses limitées en débit renvoient 429 Too Many Requests avec un error.code de rate_limited.
Formats de réponse
Les endpoints d'annonces prennent en charge deux formats de réponse :
- JSON (par défaut) — application/json. Renvoie des objets d'annonce structurés.
- Markdown — text/markdown. Renvoie un document Markdown lisible par machine avec un frontmatter YAML. Demandez-le en ajoutant .md à l'URL ou en envoyant 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"Format d'erreur
Toutes les erreurs renvoient une structure JSON cohérente :
{
"error": {
"code": "not_found",
"message": "Listing not found",
"details": { "id": "lst_abc123" }
}
}| Statut HTTP | Code d'erreur | Signification |
|---|---|---|
| 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 |
Annonces
GET /api/listings
Rechercher et filtrer des annonces avec pagination, tri et filtres d'attributs dynamiques. Public — aucune authentification requise.
Paramètres de requête
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
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) |
Exemple de réponse
{
"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 }
}Exemple : Recherche avec filtres
# 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"Codes de statut
| Code | Signification |
|---|---|
| 200 | Success — paginated listings |
| 400 | Invalid query parameters |
| 429 | Rate limit exceeded |
POST /api/listings
docsApi.createListingDesc
Corps de la requête
| Champ | Type | Requis | Description |
|---|---|---|---|
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 |
Exemple
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"]
}'Codes de statut
| Code | Signification |
|---|---|
| 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}
Obtenir une annonce unique par ID ou slug. Public. La réponse détaillée inclut long_description (si disponible), qui n'est pas présente dans les réponses de liste.
Exemple
curl https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW
# Returns full listing JSON with long_description, all attributes, all images| Code | Signification |
|---|---|
| 200 | Listing detail |
| 404 | Listing not found |
GET /api/listings/{id}.md
Obtenir une annonce au format Markdown lisible par machine. Public. Le Markdown inclut un frontmatter YAML avec les métadonnées clés, suivi de sections structurées.
Exemple de réponse
---
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}
Mettre à jour une annonce. Mise à jour partielle — seuls les champs fournis sont modifiés. Authentification requise — propriétaire uniquement.
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 } }'| Code | Signification |
|---|---|
| 200 | Updated listing |
| 403 | Not the listing owner |
| 404 | Listing not found |
DELETE /api/listings/{id}
Suppression douce d'une annonce. Authentification requise — propriétaire uniquement.
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}
Changer le statut d'une annonce. Authentification requise — propriétaire uniquement. Transitions autorisées : 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
Publier une annonce brouillon — la fait passer de brouillon à actif. Authentification requise — propriétaire uniquement.
curl -X POST https://api.owning.pro/api/listings/lagoon-400-s2-JP12QW/publish \
-H "Authorization: Bearer own_aBcD123eFgH..."| Code | Signification |
|---|---|
| 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
Envoyer un message au propriétaire de l'annonce par email. Public — aucune authentification requise. L'email du vendeur n'est jamais exposé. Limite de débit : 5 par heure et par IP.
Corps de la requête
| Champ | Type | Requis | Description |
|---|---|---|---|
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
Téléverser des images vers une annonce existante (propriétaire uniquement). Authentification requise. Données de formulaire multipart, jusqu'à 10 images par requête. Limite de débit : 50 images par jour.
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
Obtenir le catalogue complet des annonces en Markdown. Public. Prend en charge les mêmes paramètres de requête que GET /api/listings. Utile pour les agents qui souhaitent parcourir le catalogue en une seule requête.
curl "https://api.owning.pro/api/listings.md?category=boats&limit=5"Catégories
GET /api/categories
Obtenir l'arbre complet des catégories avec le nombre d'annonces. Public. Hiérarchique avec count (annonces actives) et children sur chaque nœud.
Paramètres de requête
| Paramètre | Type | Description |
|---|---|---|
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}
Obtenir une catégorie unique par ID ou slug, avec ses enfants directs et les compteurs. Public.
curl https://api.owning.pro/api/categories/boatsTypes d'actifs
Les types d'actifs définissent des modèles d'attributs structurés par catégorie. Ils indiquent quels champs sont disponibles pour les annonces dans une catégorie donnée, et lesquels sont filtrables dans la recherche.
GET /api/asset-types
Lister tous les modèles de types d'actifs disponibles. Public.
GET /api/asset-types/{type}
Obtenir la définition d'un type d'actif spécifique avec tous ses attributs. Public. La réponse indique quels attributs sont filtrables et leurs types de filtre (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" }
]
}Utilisez les attributs filtrables pour construire les paramètres de requête attr[...] pour GET /api/listings.
Authentification et clés API
POST /api/auth/register
S'inscrire avec un email et un mot de passe. Public. Renvoie immédiatement un token de session JWT.
| Champ | Type | Requis | Description |
|---|---|---|---|
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
}| Code | Signification |
|---|---|
| 201 | Account created, session token returned |
| 409 | Email already registered |
POST /api/auth/login
Se connecter avec un email et un mot de passe. Public. Renvoie un JWT valide pendant 7 jours.
curl -X POST https://api.owning.pro/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"securepassword123"}'| Code | Signification |
|---|---|
| 200 | Login successful |
| 401 | Invalid email or password |
GET /api/me
Obtenir le profil de l'utilisateur authentifié. Authentification requise.
curl https://api.owning.pro/api/me \
-H "Authorization: Bearer own_aBcD123eFgH..."
# Response: { "user": { "id": "usr_...", "email": "...", "name": "...", "type": "human", ... } }POST /api/api-keys
Générer une nouvelle clé API. Authentification requise (session JWT). La clé en texte clair n'est renvoyée qu'une seule fois — stockez-la en toute sécurité.
| Champ | Type | Requis | Description |
|---|---|---|---|
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
Lister les clés API de l'utilisateur authentifié (sans texte clair). Authentification requise.
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}
Révoquer (suppression douce) une clé API. Authentification requise — seul le propriétaire de la clé peut la révoquer.
curl -X DELETE https://api.owning.pro/api/api-keys/apk_01JX... \
-H "Authorization: Bearer eyJhbG..."
# Response: { "id": "apk_...", "revoked": true, "revoked_at": "..." }Téléversement d'images
POST /api/upload
Téléverser une image autonome (aucune annonce requise). Authentification requise. L'image est convertie en webp. Limite de débit : 50 images par jour. Utilisez ceci pour obtenir des URL d'images avant de créer une annonce.
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" }| Code | Signification |
|---|---|
| 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) |
Schéma
GET /api/schema/listing
Obtenir le schéma JSON (draft 2020-12) pour le type de données Listing. Public. Utilisez-le pour valider les données de l'annonce avant de les soumettre via 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
Obtenir le schéma des annonces au format Markdown pour les agents IA. Public.
curl https://api.owning.pro/api/schema/listing.mdDécouverte pour agents
GET /.well-known/ai.json
Description lisible par machine de la surface de l'API pour l'auto-découverte par les agents. Public. Renvoie les chemins des endpoints, le schéma d'authentification, les limites de débit, les formats de réponse et les références de schéma. Également découvrable via la directive AI-Discovery dans /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