Coliving Listing API — Partner guide
This page is published at https://api.coliving.com/json/guide.
This guide is enough to implement a host integration against the Listing API. Use it together with the live reference pages linked below. Those pages stay in sync with the API. If a field example and this guide ever disagree, follow the live pages.
Base URL: https://api.coliving.com
Quick start: try the sandbox
Use Postman or any HTTP client to try the create, read, and update flow. The sandbox needs no API key; its temporary data lasts about one hour and does not create a real listing.
- Send
POST /sandbox/listingswith a JSON body. Use the minimal create example and a uniqueoriginal_id. Savedata.idfrom the response. - Send
GET /sandbox/listings/{id}using that listing id. Save the Coliving room type and room ids from the response. - Send
PATCH /sandbox/listings/{id}using those ids. See the minimal daily update for an example body.
Send Accept: application/json and Content-Type: application/json headers. If the listing has multiple room types, include every room type that should remain available when sending room_types. The sandbox currently supports updating nested room types and rooms by Coliving id; use production PATCH to create new nested items. Sandbox validation and responses can differ from production; see Sandbox for its limits.
Reference pages
| Page | What it is for |
|---|---|
| https://api.coliving.com/docs#listings-create | Interactive docs for create and update |
| https://api.coliving.com/json/import | A full example payload |
| https://api.coliving.com/json/docs | Every field, with type and notes |
| https://api.coliving.com/json/import/extra-fees | Extra fee sections, categories, and display names |
| https://api.coliving.com/json/import/listing-amenities | Amenity ids to send |
| https://api.coliving.com/json/import/room-type-amenities | Room amenity ids to send |
| https://api.coliving.com/json/import/parking | Allowed parking values |
| https://api.coliving.com/json/import/cancellation-policies | Allowed cancellation policies |
| https://api.coliving.com/json/import/community-events | Community event ids |
| https://api.coliving.com/json/import/community-types | Community type ids |
Helper pages do not need an API key.
1. What you are building
You keep the property in your system. Coliving keeps a copy that residents can book.
- Create each property once with
POST /v1/listings. - Save the Coliving listing id from the response. Save room-type ids and room ids from
GETor from theroom_typesmap returned by a PATCH. - Every day, update prices and availability with
PATCH /v1/listings/{id}using those Coliving ids. - Do not send
POSTagain for a property that already exists.POSTwill refuse it.
A new listing is not public. Coliving reviews it and publishes it. You cannot set that status through the API.
2. Authentication and limits
Send JSON. Set these headers:
Accept: application/json
Content-Type: application/json
X-API-Key: YOUR_API_KEY
Authorization: Bearer YOUR_API_KEY is also accepted. Prefer X-API-Key.
The key belongs to one host. Every read and write is limited to that host's listings. Another host's listing id returns 404.
| Call | Permission on the key |
|---|---|
GET /v1/listings |
listings.read |
GET /v1/listings/{id} |
A valid key for the host that owns the listing |
POST /v1/listings and PATCH /v1/listings/{id} |
listings.write |
Rate limit: 60 requests per minute per API key. A 429 response includes a Retry-After header. Wait, then retry that request.
Optional header X-Request-Id. If you omit it, the API generates one and returns it on the response. Send it to Coliving support when a call fails.
Optional header Idempotency-Key on POST. Use a unique key for each distinct create operation. If a request times out or needs a retry, reuse the same key and the same body: a stored successful response is returned without creating another listing. Reusing the key with a different body returns 409. Successful responses are retained for a limited period (24 hours by default); after that, check for the listing before attempting another create. X-Request-Id is for tracing, not deduplication.
3. The four calls
| Method and path | Use it for |
|---|---|
GET /v1/listings |
List your listings and collect Coliving ids |
GET /v1/listings/{id} |
Read one full listing |
POST /v1/listings |
Create a listing that does not exist yet |
PATCH /v1/listings/{id} |
Update a listing that already has a Coliving id |
{id} is always the integer Coliving listing id. It is not your original_id.
3.1 List listings
GET /v1/listings
Query parameters:
| Parameter | Rules |
|---|---|
page |
Integer, minimum 1. Default 1. |
per_page |
Integer from 1 to 100. Default 50. |
sort |
id, name, updated_at, or listing_status |
order |
asc or desc |
listing_status |
draft, pending, published, or inactive. Use listing_status[] for multiple values; see the example below. |
q |
Search text, maximum 255 characters |
For more than one status, use array parameters, for example ?listing_status[]=draft&listing_status[]=published. URL-encoded brackets are also accepted.
pending includes listings created through this API that are waiting for Coliving to publish them.
Each item is a summary, not the full listing:
id,unique_id,original_id,namelisting_status:draft,pending,published, orinactiveupdated_atin UTC, for example2026-03-16T10:00:00Zroom_types[]withid,original_id,name, androoms[](id,original_id,name)
Store every id. You need them for PATCH.
3.2 Read one listing
GET /v1/listings/{id}
Returns the listing in the same shape you send on create: descriptions, location, amenities, prices, extra fees, room types, rooms, bookings, community, media, instructions, and FAQ.
Differences from the write payload:
- Room types and rooms include Coliving
id. prices.extra_feeson this response includesname,description,mandatory,type,sum, andfrequency. It does not includesection,category, orapplies_to. Keep those in your own system if you need them later.statusis one ofdraft,pending,published,inactive. An API-created listing is stored internally aspending_apiand is returned aspending.
A listing that does not exist, or that belongs to another host, returns 404.
POST returns a compact response. PATCH also returns a compact response; when the request includes room_types, data.room_types contains the Coliving ids and names for the listing's room types and rooms. Call GET /v1/listings/{id} when you need the full stored listing.
3.3 Create a listing
POST /v1/listings
This call only creates. It never updates.
Required:
nameroom_typeswith at least one room type- Each room type needs
name,room_type(privateorshared), andprices.base_price(whole number, monthly rent inprices.currency)
Do not send:
statusroom_types[].idroom_types[].rooms[].id
Send original_id and original_url. original_id is your permanent id for this property. Keep the exact spelling, including capitals. Send the same value on every future check so you can tell a duplicate from a new property.
Recommended on the first create: location.country, descriptions, photos, and original_id on each room type and room. You will match room types later by the Coliving id, so create the rooms you intend to keep.
Success is HTTP 201:
{
"action": "created",
"data": {
"id": 22968,
"original_id": "property-01",
"links": {
"preview": "https://coliving.com/spaces/example?preview=true"
}
},
"meta": {
"request_id": "6dedb326-1421-4dbc-8bad-c513895b8823"
}
}
data.id is the Coliving listing id. Save it. The preview link is not a public listing page until Coliving publishes the listing.
If this host already has a listing with the same original_id, original_url, address, or name, the response is 409 and nothing is written. The body includes the existing listing_id. Read the listing and confirm it is the same property before updating it with PATCH. The same original_id on a different host does not conflict.
409 for an active listing:
{
"error": {
"type": "conflict_error",
"message": "A listing with this external identity already exists for this host.",
"errors": [
{ "path": "listing_id", "code": "conflict", "message": "22968" }
]
}
}
If that existing listing is inactive, the message ends with "but is inactive." POST still does not change it. Ask Coliving if that listing should be published again. Do not create a second copy.
3.4 Update a listing
PATCH /v1/listings/{id}
{id} is the Coliving listing id from create or from the list call.
Send only the fields that changed. Omitted top-level fields stay as they are. Exception: if you send room_types, include every room type that should remain available. An existing room type omitted from that array is kept, but its available_at is set to one year from today. An empty body ({} or []) returns 422 with the message "At least one field is required for PATCH update."
Success is HTTP 200. action is updated, or unchanged when this payload matches the last saved payload. The data object has the same id, original_id, and preview link as create.
PATCH does not change listing status. If you send status, it is ignored.
Room types and rooms on PATCH use their Coliving id when they already exist:
- Include
room_types[].idto update an existing room type. Omitidto create a new room type. A new room type requiresname,room_type, andprices.base_price. - Include
rooms[].idto update an existing room. Omitidto create a new room. A new room requiresname. - To add rooms to an existing room type, send its Coliving
idand include the new room objects withoutidin itsroomsarray. Existing rooms you include for updates must have their Coliving ids. - Do not send
original_idon room types or rooms in aPATCH. Identity for existing objects is the Coliving id; new objects receive a Coliving id in the response. - When sending
room_types, include every existing room type you want to keep available. An existing room type omitted from that array is kept, but itsavailable_atis set to one year from today. It is not deleted. - To hide one rented room type, send that room type with
available_atset to a future date. There is no room-type status field.
When a PATCH includes room_types, the response includes the Coliving id map for all room types and rooms on that listing:
{
"action": "updated",
"data": {
"id": 22968,
"room_types": [
{
"id": 50123,
"name": "Private queen room",
"room_type": "private",
"rooms": [
{ "id": 90001, "name": "Room 104" },
{ "id": 90002, "name": "Room 105" }
]
}
]
}
}
Save these ids and use them on later updates. The response includes the complete id map even when the PATCH action is unchanged.
A listing id that is not yours returns 404.
4. Suggested sync
Daily update, after the listing exists:
GET /v1/listings/{id}if you do not already have room-type and room ids.PATCHprices,available_ator iCal, and any extra fees that changed.- On 429, wait for
Retry-Afterand retry the same request. - On 422, fix the field named in
error.errors[].pathand retry. Do not retry a 422 unchanged.
First import of a property:
POSTwithoriginal_id.- On 201, save
data.id. - On
409 conflict_error, find the error withpathequal tolisting_id. Read that listing and confirm it is the same property before saving its id. If it is inactive, contact Coliving before proceeding. - On
409 idempotency_key_mismatch, correct the key/body mismatch; this error has no listing id. GET /v1/listings/{id}and save room-type ids and room ids beforePATCH.
5. Listing fields
Omit a field you do not have. Do not send N/A, empty strings, or fake coordinates.
prices.currency is a code such as usd or eur. It defaults to usd.
prices.base_price on a room type is the monthly rent before long-stay discounts. discount_90, discount_180, and discount_360 are percentages off that rent for stays of 3 months, 6 months, and 12 months. They are not prices. price_jan through price_dec are optional monthly overrides.
prices.security_deposit_type is % or fixed. security_deposit is a whole number.
Text fields such as descriptions and the house manual can contain line breaks. A JSON serializer encodes these as \n; when writing JSON by hand, use \n inside the string, not a raw newline.
Photos and iCal URLs must be public. The API downloads photos and stores its own copies. Send url and an optional description.
room_type is only private or shared. bed_type is single, full, queen, king, twin, or bunk. gender is mixed, male, or female. bathroom_type is private or shared.
For a room type, send available_at (YYYY-MM-DD) or an iCal URL on each room (rooms[].availability.ical), not both.
Amenity, parking, cancellation policy, and community ids must come from the helper pages. Do not invent ids.
Do not send url. Use original_url.
6. Extra fees
Send fees on prices.extra_fees. The array is optional. Send only the fees that property charges. The six sample fees in the docs are examples, not a required list.
Each fee object needs:
| Field | Rule |
|---|---|
name |
Required. Maximum 255 characters. No URL, email, or phone number. |
sum |
Required number, at least 1. For type percent, this is the percentage and must be at most 100. |
frequency |
Required. once, monthly, or total. |
type |
fixed or percent. Defaults to fixed, except a catalog tax with no type, which is stored as percent. |
mandatory |
0 or 1. |
description |
Optional, maximum 1000 characters. No URL, email, or phone number. |
section |
common, service, other, or tax. |
category |
A category that belongs to that section. See the extra-fee page. |
applies_to |
Required for a percent tax: rent, rent_plus_mandatory, or full_subtotal. |
section and category decide which standard fee this is. The API does not look up the catalog from name.
When name is stored
| What you send | What is stored in name |
|---|---|
section and category match a catalog row |
The catalog display name. Your name is not stored. other + utilities is stored as Utilities fee even if you send utilities. |
section is other and category is omitted |
Your name, with the first letter capitalized. |
Both section and category are omitted |
Your name, with the first letter capitalized. section and category stay empty. |
name is still required in every case. A missing name is 422.
These pairs are rejected with 422 and nothing is written:
sectioniscommon,service, ortax, andcategoryis missingcategoryis sent withoutsectioncategorydoes not belong tosection(common+utilitiesis invalid becauseutilitiesbelongs toother)- A percent tax without
applies_to - A percent
sumabove 100 sumbelow 1
For a catalog row, some values you send are replaced before save:
- The stored name is the catalog name.
- Service fees are stored with
mandatory0. - Other and Tax catalog fees are stored with
mandatory1. - Common fees other than
move_out_cleaningare stored withmandatory0.move_out_cleaningkeeps the0or1you send. - These categories are stored with
frequencyonce:move_out_cleaning,exit_fee,check_in_checkout_fee,early_late_checkin,membership,refundable_key_deposit,pet_deposit,pets_cleaning. applies_tois kept only for a percent tax. A missingapplies_toon a percent tax is 422.
Booking price calculation uses the stored type, sum, frequency, and mandatory. It does not read section or category. mandatory 1 is included in the booking. mandatory 0 is included only when the resident selects it.
How an update finds the existing fee
POST on a new listing creates each fee. PATCH updates a fee it can match, and creates one it cannot.
- Catalog fee (
sectionandcategoryare a valid pair): update the row with that same pair. If none exists, update an older row that has nosectionor nocategoryand the same normalized name. Otherwise create a row. - Custom Other fee (
sectionother, nocategory): update the row with the same name. Otherwise create a row withsectionotherand an emptycategory. - No
sectionand nocategory: update the row with the same name. Otherwise create a row and leave both columns empty.
Name comparison ignores letter case and extra spaces. city admin levy and City admin levy are the same custom fee. Changing the custom name does not find the old row, so a new row is created.
Fees you leave out of a PATCH are kept. "extra_fees": [] does not delete fees. There is no delete call. To stop charging a fee, ask Coliving to remove it, or set the amount only if you still want the fee to exist.
Example for a utilities fee of 200 per month and a one-time service fee of 250:
{
"prices": {
"extra_fees": [
{
"section": "other",
"category": "utilities",
"name": "Utilities fee",
"type": "fixed",
"sum": 200,
"frequency": "monthly",
"mandatory": 1
},
{
"section": "other",
"category": "host_service_package",
"name": "Service Package",
"type": "fixed",
"sum": 250,
"frequency": "once",
"mandatory": 1
}
]
}
}
service fee, service charge, and service package are all the Other category host_service_package. The stored name is Service Package. Map your own labels on https://api.coliving.com/json/import/extra-fees before you choose section and category.
7. Minimal create
{
"original_id": "property-01",
"original_url": "https://host.example.com/listings/property-01",
"name": "300 Mountain Road",
"descriptions": {
"property": "Bright shared home with comfortable, work-ready common areas."
},
"location": {
"address": "300 Mountain Road",
"city": "San Francisco",
"country": "United States"
},
"prices": {
"currency": "usd",
"extra_fees": [
{
"section": "other",
"category": "utilities",
"name": "Utilities fee",
"type": "fixed",
"sum": 200,
"frequency": "monthly",
"mandatory": 1
}
]
},
"room_types": [
{
"original_id": "private-queen-01",
"name": "Private queen room",
"room_type": "private",
"bed_type": "queen",
"prices": {
"base_price": 2898,
"discount_180": 9,
"discount_360": 16
},
"rooms": [
{
"original_id": "room-104",
"name": "Room 104"
}
]
}
]
}
base_price 2898 is the monthly rent. discount_180 9 means a 9 percent discount on that rent for stays of 6 months or longer. The utilities fee stays 200. It is not reduced by the discount.
8. Minimal daily update
Use ids from GET, not your original ids.
{
"prices": {
"currency": "usd"
},
"room_types": [
{
"id": 50123,
"prices": {
"base_price": 2898,
"discount_180": 9,
"discount_360": 16
},
"rooms": [
{ "id": 90001 }
]
}
]
}
If the listing has another room type and you omit it from room_types, that room type stays in Coliving and its available_at becomes one year from today. Include every room type you still offer.
9. Errors
| HTTP | error.type |
Meaning |
|---|---|---|
| 401 | auth_error |
Missing, invalid, or expired API key |
| 403 | forbidden |
The key is missing listings.read or listings.write |
| 404 | not_found |
This host has no listing with that id |
| 409 | conflict_error |
POST found an existing listing; inspect error.errors for listing_id |
| 409 | idempotency_key_mismatch |
Idempotency-Key was reused with a different body; there is no listing id in this error |
| 422 | validation_error |
Fix the fields in error.errors |
| 429 | Slow down. Honor Retry-After. |
|
| 500 | internal_error |
Retry later; for a create retry, reuse the same Idempotency-Key and body. Keep the request id for support. |
Validation error shape:
{
"error": {
"type": "validation_error",
"message": "Validation failed",
"request_id": "7baebd12-c0fc-497f-ba5a-1979a6ad30ad",
"errors": [
{
"path": "prices.extra_fees.0.category",
"code": "invalid",
"message": "Choose a standard category."
}
]
}
}
10. Sandbox
Practice against the sandbox before using a production key. No API key is required. Data is cached for about one hour and is not a real listing.
POST https://api.coliving.com/sandbox/listingsGET https://api.coliving.com/sandbox/listings/{id}PATCH https://api.coliving.com/sandbox/listings/{id}
There is no sandbox list call. Use the id returned by the sandbox POST. A second sandbox POST with the same original_id returns 409 and does not write.
The sandbox simulates the integration flow; it does not create a real listing or reproduce every production validation, permission, or external-service check. For example, it does not verify that helper IDs exist, and its listing matching, fee storage and normalization, and currency-rate checks differ from production. A successful sandbox request does not guarantee the same request will succeed in production. Use the field reference and handle production errors when going live.
11. Checklist before you go live
- The API key has
listings.writeandlistings.read. - Every property has a stable
original_id, copied exactly, including capitals. - You store
data.idfromPOST, or thelisting_idfrom a confirmed duplicate listing conflict. - You store room-type ids and room ids from
GETbefore the firstPATCH. - Daily jobs call
PATCH, notPOST. - You do not send
status. - Extra fees that match the catalog send
sectionandcategory. - A room type sends either
available_ator iCal, not both. - A
PATCHthat sendsroom_typesincludes every room type that should stay available. - Photo and iCal URLs are public.
- 429 retries wait for
Retry-After. 422 retries change the payload first.