Moveandstay API for agents
A keyed HTTP API that lets an AI agent search Moveandstay's listings, read one listing, and send an enquiry through the same pipeline a visitor's enquiry goes through.
Moveandstay is a directory of move-in-ready stays and workspaces: serviced apartments, extended-stay accommodation, serviced offices and coworking spaces. Coverage is worldwide — strongest in Asia-Pacific. We take no booking commission — an enquiry is an introduction to the operator, not a reservation.
What it does, and what it does not
- Search published properties by country, city, category, capacity and property name.
- Read one property: its description, amenities, the operator's own FAQ, photos, unit types, and dated statements from the property.
- Send an enquiry — either to a specific property, or to our matching team when no single property is named.
It is not a booking API and there is nothing to reserve. It is not an availability API: we do not hold a live calendar, so no endpoint will tell you whether a property is free on a date. A property may have told us what was free, and that comes back as a dated statement to quote with its date, never as a booking. It cannot create or edit listings.
As at 2026-10-25, the directory holds 2,729 published properties across 157 cities in 40 countries. These are point-in-time figures read from the database when this page was generated; they change with every import, so do not cache them.
Authentication
Every endpoint except the health check needs an API key, sent as a bearer token. There is no anonymous tier: a request without a key is a 401. Ask contact@moveandstay.com for one.
curl -H 'Authorization: Bearer YOUR_KEY' \
'https://moveandstay.com/api/agent/v1/properties?city=hong-kong&limit=5'Each key has its own hourly budget — generous for reads, deliberately tight for enquiries. A spent budget answers 429 with a Retry-After header saying when the next slot frees, rather than a flat hour.
GET /api/agent/v1/properties
| Parameter | Meaning |
|---|---|
| q | Free text. Matches the property name only — not the description, address or amenities. |
| country | Country slug, e.g. thailand. |
| city | City slug, e.g. hong-kong. |
| type | One or more of serviced-apartments, extended-stay, serviced-offices, coworking-spaces, comma-separated. The former slug hotels is still accepted and means extended-stay. |
| min_capacity | Minimum guests (apartments, extended stay). |
| min_workstations | Minimum desks (coworking). |
| has_photos | true to require at least one photo. |
| bedrooms | Apartment size: at least this many bedrooms, 0 a studio. Keeps a property that names such a size, or whose own bedroom figure reaches it; each result then carries size_match. |
| max_rate, currency, period | A budget ceiling, all three together, nothing converted. Without bedrooms it reads the "from" rate and a property with no stated rate never matches. With bedrooms it reads that size's own rate; a size whose price is not published is kept with size_match.within_budget: null. |
| limit | 1–50, default 20. |
| offset | For paging through total, up to 1000. An offset past the last result returns an empty results list with the same total, not an error. |
An unknown city, country or type is an error, not an empty list. city=hong-kongg answers unknown_city, because returning zero results would tell you we have nothing in Hong Kong.
A capacity filter matches on the capacity figures we hold. A result whose capacity figure for the request is null (capacity.guests_max for people, capacity.workstations for desks) has not stated one: never tell a person it fits. Results come back in the same order the website ranks them.
GET /api/agent/v1/properties/{id}
id is the value search returned. The response adds the description, the amenity list, the operator's FAQ, the photos, and three more fields:
units— the property's units and their rates, the same rows as its page's "Units and rates": each unit the property names, and each size or office product (kind:hot_desk,dedicated_desk,private_office,meeting_room,virtual_office,day_pass) only its dated price statements name. Each hasguests,bedrooms(0 is a studio),size_sqm, its ownrate_fromandrate_toornull(price on request), who gave the price (price_source:operator,website, orlistingfor a rate stored on the unit) and the day (price_observed_on), andavailability: the latest statement about it being free, with its day. Quote it with its day, never as a booking.latest_updates— short dated statements about the property: what the property told us (source: "operator") or what we read on its own website (source: "website"). The statement itself is intext, andkindsays what it is about (for exampleavailability,minimum_stay,price). Each has the day it was observed (observed_on) and, for things that go stale, the last day it applies (valid_until). They can cover availability, minimum stay, price, what the price includes, terms, offers, busy and quiet periods, and news. Expired items are left out; for each kind only what the most recent statement said is sent (every news item is kept); at most 10. We check each item before it is published.last_confirmed— the day the page was last confirmed (on) and by whom (by):operatorwhen the property confirmed it,websitewhen it was checked against the property's own website.nullif it never has been.facts— in some cities only (absent elsewhere): the numbers behind what the page shows, per kind.priceandavailabilityare lists, one per unit type (unit_type, ornullfor the property);minimum_stay,depositandincludedare one each, ornull. Every fact has the day it was said (confirmed_on), who said it (source:operatorfor the property,websitefor its own site) and the last day it applies (valid_until). Quote them with their day: "the property said on 2026-11-03 that a one-bedroom is free from 1 December". They are never a calendar and never a booking.freshness— in the same cities:confirmed,partlyornot_confirmed, how recently the property (or, counting less, its own website) confirmed its price, availability and minimum stay. A band, never a score;not_confirmedsays nothing bad about a property.rate_confirmation— in the same cities: whetherrate_fromis a price the property (operator) or its website confirmed, with the day (confirmed_on);not_confirmedwhenever no dated fact backs it.
A latest_updates item is what the property said on that day, not a calendar and not a booking. Say "the property said on 2026-10-01 that …" (or "the property's website said on …" when source is website), never that a unit is available or reserved.
No endpoint returns a property's email address or phone number. The enquiry endpoint is how a property is contacted.
POST /api/agent/v1/enquiries
Send property_id and the enquiry goes to that property. Leave it out and it goes to our matching team, who contact suitable properties on the traveller's behalf. Both land in the same pipeline a website enquiry lands in.
{
"property_id": "3f1a2b4c-5d6e-7f80-9a1b-2c3d4e5f6071",
"contact": {
"name": "Alice Tan",
"email": "alice@example.com",
"phone": "+852 1234 5678"
},
"requirements": {
"move_in_date": "2026-11-01",
"duration_value": 3,
"duration_unit": "month",
"capacity": 2,
"budget_amount": 40000,
"budget_currency": "HKD",
"budget_period": "monthly",
"message": "Quiet flat, close to the MTR."
},
"consent": { "granted": true, "source": "asked in chat" },
"idempotency_key": "your-unique-id-for-this-enquiry"
}consent.grantedmust be literallytrue. You are submitting somebody else's contact details to a company that will email them; this is where you confirm they agreed.idempotency_keyis required. Send the same value if you retry, so a timeout cannot produce a second enquiry. Reusing a key with different content is a409.requirements.capacityis required whenproperty_idis given — the operator is told how many people or desks the enquiry is for, and there is no honest default.- There is no
bedroomson an enquiry: an apartment's size is asked as the number of guests,requirements.capacity. Withoutproperty_id, properties are ranked on the price of the size that sleeps them (2 a bedroom, a studio for 1–2); that size never leaves a property out (a stated capacity below the guests still does). A size named inmessage("a 3-bedroom") is the size asked for: a property with nothing that big is not contacted, and the budget is judged on that size's own price.
Two success shapes, both 200
{ "status": "RECEIVED", "enquiry_id": "…", "route": "property" }
{ "status": "RECEIVED_UNDER_REVIEW", "enquiry_id": "…", "route": "concierge" }RECEIVED means the enquiry entered the pipeline. RECEIVED_UNDER_REVIEW means it has been saved and is being looked at. Tell the person what the status says and nothing more: we do not promise a reply, a response time, or a number of properties that will get in touch.
Errors
Every error is { "error": { "code": …, "message": … } }. Branch on code; show message.
| Code | Status | Means |
|---|---|---|
| unauthorized | 401 | No key, or not one of ours. |
| rate_limited | 429 | This key's budget for the hour is spent. Retry-After says when. |
| invalid_request | 400 | A field is missing, malformed or out of range. |
| consent_required | 400 | consent.granted was not true. |
| unknown_city / unknown_country / unknown_type | 400 | That slug is not one we cover. |
| property_not_found | 404 | No published property has that reference. |
| idempotency_conflict | 409 | That key was already used for a different enquiry. |
| server_error | 500 | Ours. Safe to retry with the same idempotency key. |
What we cannot tell you
Stated plainly, because an agent that knows the edges of a source is more useful than one that guesses at them:
- Availability. We hold no calendar. Nothing here says a property is free on a date.
latest_updatesmay hold a dated statement from the property about what was free: quote it with its date, and never say a unit is available or reserved. - Minimum stay. Only as a dated statement in
latest_updates, when the property told us one. Otherwise it is not stated: ask the operator through an enquiry. - Full pricing. Only a "from" rate:
rate_from, and per unit inunits. Amount, currency and period are exactly as stored, never converted. It may have been given by the property, entered by our team, or found on a public page about the property. It is indicative: the property confirms the real price.rate_from: nullmeans *not stated* — not free, and not "ask us". - Long-stay suitability as a filter. Most properties on the site take stays of a month or more, but we have no stored field for it, so there is no filter to offer.
- Search beyond the name.
qmatches the property name. To narrow by place, usecountry,cityandtype.
Machine-readable
- OpenAPI description — the whole surface, for a connector platform or a code generator.
- MCP server at
/mcp— the same three capabilities as MCP tools. - /llms.txt — what this site is for and when not to use it.
- Every public page also answers to
Accept: text/markdown, or a.mdsuffix.
Questions, or a key: contact@moveandstay.com.