Properties
The catalogue, and your own inventory within it. Read endpoints span everything we hold; write endpoints only ever touch records your organisation published.
/v1/api/propertiesproperties:readList properties
Page through the catalogue with filters.
The workhorse read endpoint. Filters combine with AND. Anything omitted is unconstrained, so an unfiltered call returns the whole catalogue in cursor order.
Query parameters
| Name | Type | Description |
|---|---|---|
city | string | City to restrict to, as it appears on a property's address. example: Berlin |
district | string | District within the city. example: Kreuzberg |
postal_code | string | Postal code to restrict to. example: 10999 |
type | enum | Kind of property. apartment | house | studio | villa | penthouse | loft | townhouse | other example: apartment |
transaction_type | enum | Restrict to sales or rentals. sale | rent example: sale |
status | enum | Listing status. Omit for every status rather than only active ones. active | archived | sold | flagged example: active |
external_id | string | Your own identifier for a record, to find what an import created. example: OBJ-10041 |
min_price | number | Inclusive lower bound on the asking price. example: 250000 |
max_price | number | Inclusive upper bound on the asking price. example: 750000 |
min_living_area | number | Inclusive lower bound on living area, in square metres. example: 60 |
min_bedrooms | integer | Inclusive lower bound on bedroom count. example: 2 |
limit | integer | Records per page. default: 50example: 50 |
cursor | string | Opaque pointer from the previous response. Omit for the first page. Cursors are stable across inserts, so paging never skips or repeats a record the way an offset does. example: eyJpZCI6InByb3BfMWI3N2UwYTRmMiJ9 |
sort | string | Field to order by. Prefix with a minus for descending. example: -created_at |
expand | enum | Related records to inline rather than fetch separately. Available: images, price_history, market, translations. images | price_history | market | translations example: images |
language | string | Answer in this language where the property has it, as an ISO 639-1 code. A property that does not hold the language keeps its own text rather than being answered in another, and the response says which language it came back in. Feeds carrying several languages are what fills this. example: es |
Response
{
"data": [
{ "id": "prop_8f2a41c9d0", "title": "Top-floor apartment…", "price": 42900000 },
{ "id": "prop_1b77e0a4f2", "title": "Garden flat…", "price": 38500000 }
],
"meta": {
"has_more": true,
"next_cursor": "eyJpZCI6InByb3BfMWI3N2UwYTRmMiJ9",
"limit": 50
}
}https://api.skautik.com/v1/api/properties
GET /v1/api/properties?city=Berlin&district=Kreuzberg&postal_code=10999 HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…/v1/api/properties/searchproperties:readSearch properties
Semantic and geographic search that is too complex for a query string.
Takes a natural-language query, a drawn polygon, or both. Results come back ranked by relevance rather than by a sortable column, so this endpoint ignores the sort parameter. It is a POST because a polygon does not belong in a URL, not because it changes anything.
Body fields
| Name | Type | Description |
|---|---|---|
query | string | Plain-language description of what is wanted. example: "quiet two bedroom near a park, needs a home office" |
polygon | array | Closed ring of [longitude, latitude] pairs, GeoJSON order. example: [[13.3702, 52.4812], [13.4791, 52.4812], [13.4791, 52.5401], [13.3702, 52.5401], [13.3702, 52.4812]] |
bounds | object | Rectangle with sw_lat, sw_lng, ne_lat, ne_lng. Mutually exclusive with polygon. example: {"north": 52.5401, "south": 52.4812, "east": 13.4791, "west": 13.3702} |
filters | object | Same keys the list endpoint accepts as query parameters. example: {"property_type": "apartment", "price_max": 60000000} |
limit | integer | Results to return, 1 to 200. default: 50example: 50 |
Request body
{
"query": "quiet two bedroom near a park, needs a home office",
"filters": {
"market": "berlin-de",
"transaction_type": "sale",
"price_max": 50000000
},
"limit": 20
}Response
{
"data": [
{
"id": "prop_8f2a41c9d0",
"title": "Top-floor apartment…",
"relevance": 0.87,
"matched_on": ["quiet street", "park within 300m", "study alcove"]
}
],
"meta": { "has_more": false, "limit": 20 }
}https://api.skautik.com/v1/api/properties/search
POST /v1/api/properties/search HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…
Content-Type: application/json
{
"query": "quiet two bedroom near a park, needs a home office",
"filters": {
"market": "berlin-de",
"transaction_type": "sale",
"price_max": 50000000
},
"limit": 20
}/v1/api/properties/{property_id}properties:readRetrieve a property
One property with its full attribute set.
Path parameters
| Name | Type | Description |
|---|---|---|
property_idrequired | string | Identifier returned by any collection endpoint. example: prop_8f2a41c9d0 |
Query parameters
| Name | Type | Description |
|---|---|---|
expand | enum | Related records to inline rather than fetch separately. Available: images, price_history, market, translations. images | price_history | market | translations example: images |
language | string | Answer in this language where the property has it, as an ISO 639-1 code. A property that does not hold the language keeps its own text rather than being answered in another, and the response says which language it came back in. Feeds carrying several languages are what fills this. example: es |
Response
{
"data": {
"id": "prop_8f2a41c9d0",
"title": "Top-floor apartment with a south-facing balcony",
"property_type": "apartment",
"transaction_type": "sale",
"price": 42900000,
"currency": "EUR",
"price_period": null,
"price_per_area": 579700,
"bedrooms": 2,
"bathrooms": 1,
"rooms": 3,
"living_area": 74,
"floor": 4,
"total_floors": 4,
"year_built": 1908,
"energy_label": "C",
"address": {
"street": "Oranienstrasse",
"postal_code": "10999",
"district": "Kreuzberg",
"city": "Berlin",
"country": "DE"
},
"location": {
"latitude": 52.4993,
"longitude": 13.4184,
"precision": "street"
},
"status": "active",
"listed_at": "2026-07-28T09:12:04Z",
"last_verified_at": "2026-08-11T04:00:00Z",
"updated_at": "2026-08-11T04:00:00Z"
}
}Notable responses
- 404
- Unknown identifier, or a record your key cannot see. Treat as absence.
https://api.skautik.com/v1/api/properties/{property_id}
GET /v1/api/properties/prop_8f2a41c9d0?expand=images&language=es HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…/v1/api/properties/{property_id}/price-historyproperties:readPrice history
Every asking price we observed while tracking the listing.
These are advertised prices, not transacted ones. A property may well have sold for something quite different from its final asking price.
Path parameters
| Name | Type | Description |
|---|---|---|
property_idrequired | string | Property identifier. example: prop_8f2a41c9d0 |
Response
{
"data": [
{ "price": 44900000, "currency": "EUR", "observed_at": "2026-07-28T09:12:04Z" },
{ "price": 42900000, "currency": "EUR", "observed_at": "2026-08-09T06:31:11Z" }
]
}https://api.skautik.com/v1/api/properties/{property_id}/price-history
GET /v1/api/properties/prop_8f2a41c9d0/price-history HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…/v1/api/properties/{property_id}/similarGrowth and aboveproperties:readSimilar properties
Comparable records, for context or valuation support.
Similarity blends location, size, type, and condition. It is a convenience, not an appraisal, and comparables thin out quickly in low-supply areas.
Path parameters
| Name | Type | Description |
|---|---|---|
property_idrequired | string | Property to compare against. example: prop_8f2a41c9d0 |
Query parameters
| Name | Type | Description |
|---|---|---|
limit | integer | Comparables to return, 1 to 50. example: 10 |
https://api.skautik.com/v1/api/properties/{property_id}/similar
GET /v1/api/properties/prop_8f2a41c9d0/similar?limit=10 HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…/v1/api/propertiesproperties:writeCreate a property
Publish a record from your own inventory.
Send an Idempotency-Key so a retried request cannot create a duplicate listing. Records created this way belong to your organisation and are the only ones your key may modify.
Body fields
| Name | Type | Description |
|---|---|---|
external_id | string | Your identifier for this property. Optional, but set it if the record might later arrive through an import: without one the import has nothing to match on and creates a duplicate. example: AG-4471-0812 |
titlerequired | string | Headline for the listing. example: Top-floor apartment with a south-facing balcony |
property_typerequired | enum | One of the supported property types. example: apartment |
transaction_typerequired | enum | sale or rent. example: sale |
pricerequired | integer | Amount in minor units. example: 48900000 |
currencyrequired | string | ISO 4217 code. example: EUR |
addressrequired | object | At minimum a city and country; more improves geocoding precision. example: {"street": "Oranienstrasse 12", "postal_code": "10999", "city": "Berlin", "country": "DE"} |
Headers
| Name | Type | Description |
|---|---|---|
Idempotency-Key | string | Unique per logical creation. Replaying the same key returns the original result rather than creating a second record. example: a2f1c7e4-… |
Request body
{
"title": "Top-floor apartment with a south-facing balcony",
"property_type": "apartment",
"transaction_type": "sale",
"price": 42900000,
"currency": "EUR",
"living_area": 74,
"bedrooms": 2,
"address": {
"street": "Oranienstrasse",
"postal_code": "10999",
"city": "Berlin",
"country": "DE"
}
}Notable responses
- 201
- Created. The Location header carries the new resource URL.
- 409
- An Idempotency-Key was reused with a different body.
- 422
- The body parsed but failed validation. See the errors array.
https://api.skautik.com/v1/api/properties
POST /v1/api/properties HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…
Content-Type: application/json
{
"title": "Top-floor apartment with a south-facing balcony",
"property_type": "apartment",
"transaction_type": "sale",
"price": 42900000,
"currency": "EUR",
"living_area": 74,
"bedrooms": 2,
"address": {
"street": "Oranienstrasse",
"postal_code": "10999",
"city": "Berlin",
"country": "DE"
}
}/v1/api/properties/{property_id}properties:writeUpdate a property
Change fields on a record you published.
A partial update: send only what changes. Send If-Match with the ETag from your last read to avoid overwriting a concurrent edit. A record owned by an import source is refused here, because the next run would revert whatever you wrote: change it in the system that feeds the import instead.
Path parameters
| Name | Type | Description |
|---|---|---|
property_idrequired | string | Property to update. example: prop_8f2a41c9d0 |
Headers
| Name | Type | Description |
|---|---|---|
If-Match | string | ETag from your last read. Rejected with 412 if the record moved on. example: "3f8a2c1d9b" |
Request body
{
"title": "Top-floor apartment with a south-facing balcony and new windows",
"listing": {
"price": 419000,
"status": "active"
}
}Notable responses
- 403
- The record exists but your organisation did not publish it.
- 409
- managed_by_import. The record belongs to an import source, which is authoritative for it. The body names the source.
- 412
- If-Match did not match. Re-read and reapply your change.
https://api.skautik.com/v1/api/properties/{property_id}
PATCH /v1/api/properties/prop_8f2a41c9d0 HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…
Content-Type: application/json
{
"title": "Top-floor apartment with a south-facing balcony and new windows",
"listing": {
"price": 419000,
"status": "active"
}
}/v1/api/properties/{property_id}properties:writeWithdraw a property
Take one of your records off the market.
Withdrawal is not deletion. The record moves to withdrawn, disappears from search, and keeps its price history so past analysis stays intelligible. A record owned by an import source is refused, because the next run would bring it straight back: withdraw it at the source instead.
Path parameters
| Name | Type | Description |
|---|---|---|
property_idrequired | string | Property to withdraw. example: prop_8f2a41c9d0 |
Notable responses
- 204
- Withdrawn. No body.
- 409
- managed_by_import. Withdraw it at the source, or delete the source first.
https://api.skautik.com/v1/api/properties/{property_id}
DELETE /v1/api/properties/prop_8f2a41c9d0 HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…/v1/api/properties/{property_id}/imagesproperties:readList images
Image records for a property, in display order.
Path parameters
| Name | Type | Description |
|---|---|---|
property_idrequired | string | Property identifier. example: prop_8f2a41c9d0 |
https://api.skautik.com/v1/api/properties/{property_id}/images
GET /v1/api/properties/prop_8f2a41c9d0/images HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…/v1/api/properties/{property_id}/imagesimages:writeUpload an image
Attach an image to a record you published.
Multipart upload. JPEG, PNG, or WebP up to 12 MB. Upload only images you hold the rights to: property photography is usually licensed to an agent rather than owned outright.
Path parameters
| Name | Type | Description |
|---|---|---|
property_idrequired | string | Property to attach to. example: prop_8f2a41c9d0 |
Query parameters
| Name | Type | Description |
|---|---|---|
room_type | string | What the photograph shows, used to group images and to pick a source for staging. example: living_room |
primary | boolean | Pass true to make this the primary image, which demotes the current one. example: true |
Body fields
| Name | Type | Description |
|---|---|---|
filerequired | binary | Image payload. example: listings.csv |
position | integer | Display order. The image at position 0 is the primary one. example: 1 |
Notable responses
- 413
- File exceeds the size limit.
- 415
- Unsupported image format.
https://api.skautik.com/v1/api/properties/{property_id}/images
POST /v1/api/properties/prop_8f2a41c9d0/images?room_type=living_room&primary=true HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…
Content-Type: application/json
{
"file": "listings.csv",
"position": 1
}/v1/api/properties/{property_id}/images/{image_id}images:writeDelete an image
Remove an image from a record you published.
Path parameters
| Name | Type | Description |
|---|---|---|
property_idrequired | string | Property identifier. example: prop_8f2a41c9d0 |
image_idrequired | string | Image identifier. example: img_2b7d4f1908 |
https://api.skautik.com/v1/api/properties/{property_id}/images/{image_id}
DELETE /v1/api/properties/prop_8f2a41c9d0/images/img_2b7d4f1908 HTTP/1.1
Host: api.skautik.com
Authorization: Bearer sk_live_…