API changelog
This page is the API’s own log. The site’s release notes link here for API changes.
Versioning
Section titled “Versioning”The version is in the path: /api/v1. GET /api/<version>/ reports the version asked for as api_version. Each
version has its own OpenAPI document at /api/<version>/openapi.json; its info.version is
that document’s revision (1.0 for v1), whose minor part goes up when the version gains
something.
Inside v1, a change only adds:
- a new endpoint
- a new member in an answer
- a new optional parameter
- a new value in a list of allowed values
Clients must ignore members they do not know, and must accept values they do not know in
any list of allowed values: a new listing status, warnings code, field error code or
webhook event can arrive inside v1. Treat an unknown error code by its status.
These need a new version, v2:
- removing or renaming a member, endpoint or parameter
- changing a member’s type or meaning
- changing a status code, or the
codeof an existing error - making an optional parameter required
- changing how authentication works
Core’s tests compare the v1 OpenAPI document with a saved copy and fail when an endpoint, member, enum value or success status is removed, a type or auth rule changes, or a request member becomes required.
If v2 ships, it runs beside v1 for at least 12 months. A core endpoint that does not change
serves both; one that does keeps its v1 form under /api/v1. These stay on v1 until they
opt in:
- plugin endpoints that do not list
versions - webhook bodies
osc_api_url(), unless given a version
A member is deprecated inside a version by marking it deprecated: true in the OpenAPI
document and listing it below. It keeps its type and meaning until the version is retired.
Deprecation policy
Section titled “Deprecation policy”-
Announced. The endpoint or member is marked deprecated in a release and listed below with its end date.
-
Warned. Every answer from a deprecated endpoint carries these headers:
Header Value Deprecation@<unix time>: when it was deprecated (RFC 9745)SunsetAn HTTP date: when it stops answering (RFC 8594) Link<…/docs/developers/api/changelog/>; rel="deprecation"The OpenAPI document marks the operation
deprecated: true. -
Removed. Not before the next major release, and not sooner than six months after the
Sunsetheader first shipped.
A route with a deprecated date but no sunset has no removal date yet.
Plugin endpoints can use the same headers: set deprecated and sunset in the route spec
(Plugin endpoints).
To catch a deprecation early, log any answer that has a Deprecation header.
Current deprecations
Section titled “Current deprecations”None.
Changes
Section titled “Changes”v1: conversations
Section titled “v1: conversations”Added to v1 on 2026-10-23.
GETandPOST /account/conversations,GET /account/conversations/{id},GETandPOST /account/conversations/{id}/messages, andPOST /account/conversations/{id}/read,/block(DELETElifts it) and/report. Scopesconversations:readandconversations:write.GET /admin/conversations,GET /admin/conversations/{id},POST /admin/conversations/{id}/closeand/clear-report,GET /admin/messages,GET /admin/messages/{id},POST /admin/messages/{id}/releaseand/remove. Scopeadmin:conversations, also for moderator keys.- New warning
message_held. - New webhook events
message.createdandconversation.reported; they carry ids only.
v1: capped search count
Section titled “v1: capped search count”Added to v1 on 2026-10-22.
- With
count=true, a listing search’smetahastotal_capped. When true, counting stopped at the site’s search count limit:totalis that limit and more listings match.
v1: pay at the end of posting
Section titled “v1: pay at the end of posting”Added to v1 on 2026-10-19.
- A listing can be
unpaid, inListing.statusand in thestatusfilter ofGET /account/listingsandGET /admin/listings.POST /listingsmay answer201with that status, thelisting_unpaidwarning and apaymentmember. POST /listings/{id}/publishpublishes it with credits, or with{package, gateway}starts an order and answersorderandnext.POST /admin/listings/{id}/publishpublishes without payment.- New error
insufficient_credits(422, withpayment) and new warninglisting_unpaid. listing.createdis sent when a listing that waited for payment goes live.- The site document’s
modeisclassifiedsorshop;bothis gone.
v1: cart quote and orders
Section titled “v1: cart quote and orders”Added to v1.
GET /cart/quote?lines=variant:qty,…: a cart priced on the server with delivery, fees, totals and the payment methods on offer. Needs no sign-in.POST /account/orders,GET /account/orders,GET /account/orders/{id},POST /account/orders/{id}/payandPOST /account/orders/{id}/cancel, with the scopesorders:readandorders:write.GET /account/cartandPUT /account/cart: the signed-in shopper’s saved cart, with the scopescart:readandcart:write.- New field error codes
out_of_stock,not_buyable,unavailableandtoo_many_open.
v1: pages, menus, SEO and products
Section titled “v1: pages, menus, SEO and products”Added to v1.
GET /pages,GET /pages/{id|name}andGET /menus: linked pages, one page with its text, and the site menus (main,footer). A plugin adds or edits menus with theapi_menusfilter. All takelocale.GET /seo?for=home|listing|category|page|search: canonical URL, title, description, robots, breadcrumbs, hreflang and Open Graph. A hidden or unknown target is a 404.- The site document carries
modeandfeatures.shop. In shop modePOST /listingsanswers403 feature_disabled. - A listing has a
productmember with itsvariants(SKU, price, stock, weight, shipping class) when it is for sale. Only an admin writes them. product.optionslists the option names and values (Colour, Size);variants[].photois the variant’s photo. An admin sends option rows asproduct.variants.- Reviews: a comment carries
rating(1 to 5 ornull), andPOST /listings/{id}/commentstakesrating(a comment may have text, a rating or both; a second rated comment is409 already_reviewed; whilefeatures.ratingsis false a rating is403 feature_disabled).GET /listings/{id}/commentstakessort=newest.include=ratingaddsrating: {average, count}to a listing, withdistributionon one listing.
v1: search extras
Section titled “v1: search extras”Added to v1.
GET /listings/{id}/related: live listings like one listing, best first.limit1-20 (6);same_category(on by default),same_country,same_region,with_photos,premium_only.GET /search/suggest?q=: listings for a search box, as{id, title, url, thumbnail, price, place}, plus anysearchesa search plugin adds, each with a translatedcount_label. Titles only, all words first, then titles with some of them;qneeds a word of three letters or more.limit1-10 (5).GET /search/categories?title=: up to three categories for a new listing’s title, from listings whose titles share the most words with it.- On a keyword search each listing has
snippet: its text with the searched words in<mark>. - A listing search’s
metamay carryfacets.category,suggestionandrelaxedwhen a search plugin sent them.
v1: distance search
Section titled “v1: distance search”Added to v1.
GET /listingsandGET /users/{id}/listingstakelat,lngandradius(km), andsort=distancefor nearest first. With noradius, nearest first keeps to 1000 km.- They answer 422 when distance search is off on the site, and when
radiusorsort=distancecomes without bothlatandlng. - Each listing’s
locationhasdistance_km: whole km fromlat/lng, even on a site that shows miles, or null outside a distance search.
v1: admin and webhooks
Section titled “v1: admin and webhooks”Added to v1.
| Endpoint | Does |
|---|---|
/admin/listings, with bump |
Moderate and edit any listing |
/admin/comments, with approved and blocked on PATCH |
Moderate comments |
/admin/users, with confirmed and blocked on PATCH, and /admin/users/{id}/sessions |
Manage users and end their sign-ins |
GET /account/listings |
The user’s own listings in any status |
/admin/categories, /admin/currencies, /admin/custom-fields |
Manage categories, currencies and custom fields |
/admin/regions, /admin/cities, /admin/areas |
Manage locations |
GET, PATCH /admin/settings, GET /admin/jobs |
Settings from a fixed list, and the job queue |
/admin/keys, with rotate |
Manage API keys |
/admin/webhooks, with rotate-secret, test, deliveries, and GET /admin/webhook-events |
Manage webhook endpoints |
GET /comments/{id}, GET /listings/{id}/photos/{photo}, GET /account/keys/{id}, GET /account/alerts/{id} |
Read one thing back, so every Location can be read |
Also added:
- Webhooks: signed deliveries in the Standard Webhooks layout for eleven events
(
listing.created,listing.updated,listing.deleted,listing.activated,listing.deactivated,listing.spam,listing.reported,comment.created,user.registered,user.updated,user.deleted), with retries, auto-pause and secret rotation. Plugins add events namedext.<slug>.<name>. - Scopes:
admin:comments,admin:settings,admin:keys,admin:webhooks.admin:listings,admin:usersandadmin:taxonomyalso open their admin endpoints. - An
apimember onGET /:registration,personal_keys,photo_urls. - Hooks:
api_webhook_events,api_webhook_payload,api_webhook_delivered. Function:osc_webhook_emit(). - For plugins:
ApiKit::listingContext(),Scopes::PLUGIN_SCOPEand theScopes::AUDIENCE_*constants,RouteSpec::auth(),scope()andplugin(), andosc_user_api_access_url(). - The
api_listing,api_userandapi_categoryfilters get$data, $context; the database row is no longer passed. - Settings: Allow webhook addresses on a private network.
- No new error codes. Admin actions the state refuses answer
409 conflict.
See Admin endpoints and Webhooks.
v1: writes and sign-in
Section titled “v1: writes and sign-in”Added to v1.
| Endpoint | Does |
|---|---|
POST /auth/token |
Sign in with a password, or swap a refresh token for new tokens |
POST /auth/sign-out |
Sign out this sign-in |
GET, PATCH /account, POST /account/password |
The user’s own profile and password |
GET /account/sessions, DELETE /account/sessions/{id} |
The user’s sign-ins |
GET, POST /account/keys, DELETE /account/keys/{id} |
Personal API keys, when the site allows them |
POST /users |
Sign up, when the site allows it |
POST /listings, PATCH /listings/{id}, DELETE /listings/{id} |
Post, edit and delete the user’s listings |
POST /photos, POST /listings/{id}/photos, DELETE /listings/{id}/photos/{photo} |
Upload, add and remove photos |
POST /listings/{id}/comments, DELETE /comments/{id} |
Comment, and delete one’s own comment |
GET, POST /account/alerts, DELETE /account/alerts/{id} |
Saved searches |
Also added:
- Credentials: access tokens (
sca_, 15 minutes), single-use refresh tokens (scr_) with reuse detection, and personal keys (sck_) that expire within a year and stop when the password changes. - Scopes held by users:
listings:write,listings:delete,comments:write,alerts:read,alerts:write,account:read,account:write. - The
ownerview for a user’s own listings and account. Idempotency-Keyon writes, answered again withIdempotency-Replayed: true.PATCHtakesapplication/merge-patch+jsonand answersAccept-Patch.Locationon every201, andwarningsnext todata:listing_pending,photo_skipped,comment_pending,email_confirmation_sent.- Error codes:
token_expired,not_owner,listing_limit,login_blocked,idempotency_key_reused,idempotency_in_flight,conflict,invalid_header, and atPOST /auth/tokenthe OAuth 2 ones:invalid_request,invalid_grant,invalid_scope,unsupported_grant_type. The token endpoint also takes a form. - Settings: personal keys, sign-up, photos by address, and new listings per user an hour.
- Hourly caps for new listings, comments, photo downloads, e-mail changes and sign-ups.
See Writes and Authentication.
v1: first release
Section titled “v1: first release”Read-only endpoints, authenticated with keys.
| Endpoint | Returns |
|---|---|
GET / |
The site, its locales and links |
GET /openapi.json |
The OpenAPI 3.1 document, with the site’s plugin endpoints. No credential needed. |
GET /listings, GET /listings/{id} |
Search, and one listing |
GET /listings/{id}/photos, GET /listings/{id}/comments |
A listing’s photos and approved comments |
GET /categories, GET /categories/{category}, GET /custom-fields |
Categories (flat or tree) and custom fields |
GET /countries, /countries/{code}/regions, /regions/{id}/cities, /cities/{id}/areas |
Locations |
GET /currencies |
Currencies |
GET /users/{id}, GET /users/{id}/listings |
A public profile and the user’s live listings |
Also in the first release:
- Public keys (
scp_) and admin keys (sck_), made on Admin → Settings → API or withapi:key:create. - Cursor paging, sparse fieldsets (
fields),ETagandIf-None-Match. - Rate limits with
RateLimitandX-RateLimit-*headers, and a lockout after failed keys. - CORS for origins the site owner lists.
- Errors as
application/problem+json. - For plugins:
ext/<slug>/routes,ext:<slug>:<verb>scopes, and declared fields underext.