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: 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.