Endpoints
Every route, from the OpenAPI document the server is checked against.
- Method
- POST
- Path
- /api/v1/listings
- Does
- Enter a listing once
- Scope
listings:write
- Method
- GET
- Does
- Read a listing
- Scope
listings:read or feed:read
- Method
- PATCH
- Does
- Change a listing
- Scope
listings:write
- Method
- GET
- Does
- Where a listing is live
- Scope
listings:read
- Method
- POST
- Does
- Start a photo upload
- Scope
listings:write
- Method
- GET
- Does
- A listing's photos
- Scope
listings:read
- Method
- POST
- Does
- Finish a photo upload
- Scope
listings:write
- Method
- PATCH
- Does
- Reorder photos
- Scope
listings:write
- Method
- DELETE
- Does
- Remove a photo
- Scope
listings:write
- Method
- GET
- Path
- /api/v1/export
- Does
- Export everything your brokerage owns
- Scope
listings:read
- Method
- GET
- Does
- Same address as another brokerage
- Scope
listings:read
- Method
- POST
- Path
- /api/v1/webhooks
- Does
- Subscribe to listing changes
- Scope
webhooks:manage
- Method
- GET
- Path
- /api/v1/webhooks
- Does
- Your subscriptions
- Scope
webhooks:manage
- Method
- DELETE
- Does
- Unsubscribe
- Scope
webhooks:manage
- Method
- POST
- Does
- Rotate this key
- Scope
listings:write or listings:read or feed:read or webhooks:manage
- Method
- GET
- Path
- /api/reso
- Does
- Service document
- Scope
feed:read
- Method
- GET
- Does
- Data model
- Scope
feed:read
- Method
- GET
- Path
- /api/reso/Lookup
- Does
- Lookup values
- Scope
feed:read
- Method
- POST
- Does
- Enter a listing in RESO fields
- Scope
listings:write
- Method
- GET
- Does
- Search the feed
- Scope
feed:read
- Method
- PATCH
- Does
- Change a listing in RESO fields
- Scope
listings:write
- Method
- GET
- Does
- One listing from the feed
- Scope
feed:read
- Method
- GET
- Path
- /api/reso/Media
- Does
- Photos in the feed
- Scope
feed:read
POST/api/v1/listings
Member brokerages only. The caller's organization becomes the listing brokerage and owner. The listing is planned to every destination in its market under the seller's visibility choice and each MLS's current rules, and the history starts with three fingerprinted lines. A listing at the address of another brokerage's active listing is entered and flagged: both owners get a history line (which never names the other brokerage or listing) and listing.conflict, and the pair shows under /api/v1/conflicts until the platform operator decides it.
Auth: a Bearer key with listings:write.
Idempotency-Keystring in header- ^[\x21-\x7E]{1,200}$
- Optional. Repeats with the same key and body replay the first response for 24 hours; the same key with a different body is refused.
streetstringRequired- 1 to 120 chars
- Street address. Not blank, no < or >.
citystringRequired- Austin · Charlotte · Phoenix · Tampa
- Pilot market.
statestringRequired- TX · NC · AZ · FL
- Two letter state; must match the city.
zipstringRequired- ^\d{5}$
- 5 digit ZIP.
priceintegerRequired- 1 to 1,000,000,000
- List price in whole dollars.
bedsintegerRequired- 0 to 50
- Bedrooms.
bathsnumberRequired- 0 to 50, steps of 0.5
- Bathrooms, in halves.
sqftintegerRequired- 0 to 100,000
- Living area in square feet.
lotAcresnumber- 0 to 100,000, steps of 0.01
- Lot size in acres.
yearBuiltinteger- 1700 to 2030
- Year built.
propertyTypestringRequired- Single Family · Townhouse · Condominium · Multi Family
- Property sub type.
remarksstringRequired- 1 to 2000 chars
- Public remarks. Not blank, no < or >.
agentstringRequired- 1 to 80 chars
- Listing agent's name. Not blank, no < or >.
sellerstringRequired- 1 to 120 chars
- Seller's name. Seen only by the owner. Not blank, no < or >.
visibilitystringRequired- public · coming_soon · members_only
- The seller's choice: public (the usual one), coming_soon or members_only.
goLiveDatestring- YYYY-MM-DD
- Required for coming_soon. Not in the past.
listingAgreementAtstringRequired- YYYY-MM-DD
- The day the listing agreement was signed. Within the last year, not after tomorrow (UTC).
curl -X POST "https://listonce.io/api/v1/listings" \
-H "Authorization: Bearer $LISTONCE_KEY" \
-H "Content-Type: application/json" \
-d '{
"street": "88 Cedar Bluff Dr",
"city": "Austin",
"state": "TX",
"zip": "78704",
"price": 625000,
"beds": 3,
"baths": 2.5,
"sqft": 1960,
"lotAcres": 0.18,
"yearBuilt": 2004,
"propertyType": "Single Family",
"remarks": "Quiet street, open kitchen and a covered patio facing the greenbelt.",
"agent": "Rowan Ellis",
"seller": "A. and T. Monroe",
"visibility": "members_only",
"listingAgreementAt": "2026-10-26"
}'{
"street": "88 Cedar Bluff Dr",
"city": "Austin",
"state": "TX",
"zip": "78704",
"price": 625000,
"beds": 3,
"baths": 2.5,
"sqft": 1960,
"lotAcres": 0.18,
"yearBuilt": 2004,
"propertyType": "Single Family",
"remarks": "Quiet street, open kitchen and a covered patio facing the greenbelt.",
"agent": "Rowan Ellis",
"seller": "A. and T. Monroe",
"visibility": "members_only",
"listingAgreementAt": "2026-10-26",
"id": "OL-RCGZQ4",
"addressKey": "88|CEDAR BLUFF DR||AUSTIN|TX|78704",
"brokerage": "Harbor & Pine Realty",
"source": "api",
"status": "Active",
"returns": {},
"photos": [],
"sellerConsentAt": "2026-10-26T15:04:12.318Z",
"createdAt": "2026-10-26T15:04:12.318Z",
"updatedAt": "2026-10-26T15:04:12.318Z",
"tint": 0,
"deliveries": [
{
"destId": "mls-austin",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "portal-a",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "portal-b",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "portal-c",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "net-members",
"state": "delivered",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "data-appraisal",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "data-records",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
}
],
"history": [
{
"at": "2026-10-26T15:04:12.318Z",
"who": "Rowan Ellis",
"what": "Seller chose members only",
"hash": "0350a77c66e2d3"
},
{
"at": "2026-10-26T15:04:12.318Z",
"who": "Rowan Ellis",
"what": "Listing entered once",
"hash": "1cde064c84ab14"
},
{
"at": "2026-10-26T15:04:12.318Z",
"who": "ListOnce",
"what": "Sent to 1 of 7 destinations",
"hash": "1d54774e9f1ec6"
}
],
"historyIntact": true,
"lateDays": 0,
"remarkFlags": []
}- Status
- 201
- When
- Created. The owner's view of the listing.
- Status
- 400
- When
- A field failed validation, a field is unknown, or the body is not a JSON object.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks listings:write, or its organization is not a member brokerage.
- Status
- 409
- When
- The brokerage already has an active listing (Coming Soon, Active or Pending) at this address: existingKey names it. Update that listing, or withdraw it before listing again. Or a request with this Idempotency-Key is still running.
- Status
- 413
- When
- Body over 16,384 bytes.
- Status
- 422
- When
- This Idempotency-Key was used for a different body.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/v1/listings/{key}
The owner (with listings:read) gets the full listing with its deliveries and history. Another member brokerage with listings:read gets a members only listing as its own marked copy (an alias for the key and photo URLs issued to it), and every read is logged. Any other key with feed:read gets the RESO record, and only while the listing may be publicly marketed: members only and coming soon listings answer 404.
Auth: a Bearer key with listings:read or feed:read.
keystring in pathRequired- ^OL-[A-Z0-9]{6}$
- The listing key. A key of the wrong shape answers 404, like a missing one.
curl "https://listonce.io/api/v1/listings/OL-4K7Q2M" \ -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
{
"@odata.context": "https://listonce.io/api/reso/$metadata#Property/$entity",
"ListingKey": "OL-4K7Q2M",
"OriginatingSystemName": "ListOnce",
"StandardStatus": "Active",
"ListPrice": 589000,
"ClosePrice": null,
"UnparsedAddress": "1418 Larkspur Way, Austin, TX 78745",
"City": "Austin",
"StateOrProvince": "TX",
"PostalCode": "78745",
"PropertyType": "Residential",
"PropertySubType": "Single Family Residence",
"BedroomsTotal": 3,
"BathroomsTotalInteger": 2,
"BathroomsFull": 2,
"BathroomsHalf": 0,
"LivingArea": 1840,
"LivingAreaUnits": "Square Feet",
"LotSizeAcres": 0.21,
"YearBuilt": 1998,
"PublicRemarks": "Shaded corner lot, updated kitchen, two living areas and a deep backyard with mature oaks.",
"ListAgentFullName": "Dana Whitfield",
"ListOfficeName": "Harbor & Pine Realty",
"ListingContractDate": "2026-10-24",
"OnMarketDate": "2026-10-24",
"ModificationTimestamp": "2026-10-24T21:03:00.000Z",
"LO_SellerVisibilityChoice": "public",
"LO_SellerChoiceRecordedAt": "2026-10-24T10:20:00.000Z"
}- Status
- 200
- When
- The owner's view, or the RESO record for a feed reader.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- No such listing, or not one this key may read.
- Status
- 429
- When
- Over this key's requests per minute.
PATCH/api/v1/listings/{key}
Owner only. Send just the fields that change; null clears lotAcres or yearBuilt. A new visibility is a new seller choice: the choice time resets, and switching to or from coming soon moves the status between Coming Soon and Active. soldPrice is allowed only on a Closed listing; leaving Closed drops it. resend true clears MLS send backs and resends to every destination. Every destination is re-planned; one keeps its time unless its outcome changed.
Auth: a Bearer key with listings:write.
keystring in pathRequired- ^OL-[A-Z0-9]{6}$
- The listing key. A key of the wrong shape answers 404, like a missing one.
streetstring- 1 to 120 chars
- Street address. Not blank, no < or >.
citystring- Austin · Charlotte · Phoenix · Tampa
- Pilot market.
statestring- TX · NC · AZ · FL
- Two letter state; must match the city.
zipstring- ^\d{5}$
- 5 digit ZIP.
priceinteger- 1 to 1,000,000,000
- List price in whole dollars.
bedsinteger- 0 to 50
- Bedrooms.
bathsnumber- 0 to 50, steps of 0.5
- Bathrooms, in halves.
sqftinteger- 0 to 100,000
- Living area in square feet.
lotAcresnumber or null- 0 to 100,000, steps of 0.01
- Lot size in acres. null clears it.
yearBuiltinteger or null- 1700 to 2030
- Year built. null clears it.
propertyTypestring- Single Family · Townhouse · Condominium · Multi Family
- Property sub type.
remarksstring- 1 to 2000 chars
- Public remarks. Not blank, no < or >.
statusstring- Coming Soon · Active · Pending · Closed · Withdrawn
- New status.
visibilitystring- public · coming_soon · members_only
- A new seller choice.
goLiveDatestring- YYYY-MM-DD
- Go live date for coming soon. Not in the past.
soldPriceinteger- 1 to 1,000,000,000
- Sold price; only when the status is Closed.
resendboolean- Clear send backs and resend everywhere.
curl -X PATCH "https://listonce.io/api/v1/listings/OL-4K7Q2M" \
-H "Authorization: Bearer $LISTONCE_KEY" \
-H "Content-Type: application/json" \
-d '{
"price": 615000,
"status": "Pending"
}'{
"street": "88 Cedar Bluff Dr",
"city": "Austin",
"state": "TX",
"zip": "78704",
"price": 615000,
"beds": 3,
"baths": 2.5,
"sqft": 1960,
"lotAcres": 0.18,
"yearBuilt": 2004,
"propertyType": "Single Family",
"remarks": "Quiet street, open kitchen and a covered patio facing the greenbelt.",
"agent": "Rowan Ellis",
"seller": "A. and T. Monroe",
"visibility": "members_only",
"listingAgreementAt": "2026-10-26",
"id": "OL-RCGZQ4",
"addressKey": "88|CEDAR BLUFF DR||AUSTIN|TX|78704",
"brokerage": "Harbor & Pine Realty",
"source": "api",
"status": "Pending",
"returns": {},
"photos": [],
"sellerConsentAt": "2026-10-26T15:04:12.318Z",
"createdAt": "2026-10-26T15:04:12.318Z",
"updatedAt": "2026-10-26T16:40:05.902Z",
"tint": 0,
"deliveries": [
{
"destId": "mls-austin",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "portal-a",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "portal-b",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "portal-c",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "net-members",
"state": "delivered",
"at": "2026-10-26T16:40:05.902Z",
"note": "Shown as pending"
},
{
"destId": "data-appraisal",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "data-records",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
}
],
"history": [
{
"at": "2026-10-26T15:04:12.318Z",
"who": "Rowan Ellis",
"what": "Seller chose members only",
"hash": "0350a77c66e2d3"
},
{
"at": "2026-10-26T15:04:12.318Z",
"who": "Rowan Ellis",
"what": "Listing entered once",
"hash": "1cde064c84ab14"
},
{
"at": "2026-10-26T15:04:12.318Z",
"who": "ListOnce",
"what": "Sent to 1 of 7 destinations",
"hash": "1d54774e9f1ec6"
},
{
"at": "2026-10-26T16:40:05.902Z",
"who": "Harbor & Pine Realty",
"what": "Price changed to $615,000; Status changed to Pending",
"hash": "13d47d05eda81d"
},
{
"at": "2026-10-26T16:40:05.902Z",
"who": "ListOnce",
"what": "Sent to 1 of 7 destinations",
"hash": "1b9856cf24d71e"
}
],
"historyIntact": true,
"lateDays": 0,
"remarkFlags": []
}- Status
- 200
- When
- The owner's view after the change.
- Status
- 400
- When
- A field failed validation, a cross field rule failed, the body was empty, or nothing would change.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- No such listing owned by this key's brokerage.
- Status
- 409
- When
- The brokerage already has an active listing (Coming Soon, Active or Pending) at this address: existingKey names it. Update that listing, or withdraw it before listing again. Or the listing changed while this request ran: read it again and retry. Checked when the address changes or the listing comes back to market.
- Status
- 413
- When
- Body over 16,384 bytes.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/v1/listings/{key}/deliveries
Owner only: one line per destination in the listing's market with its outcome and any note.
Auth: a Bearer key with listings:read.
keystring in pathRequired- ^OL-[A-Z0-9]{6}$
- The listing key. A key of the wrong shape answers 404, like a missing one.
curl "https://listonce.io/api/v1/listings/OL-4K7Q2M/deliveries" \ -H "Authorization: Bearer $LISTONCE_KEY"
{
"listingKey": "OL-4K7Q2M",
"value": [
{
"destId": "mls-austin",
"state": "delivered",
"at": "2026-10-24T11:02:00.000Z",
"destination": "Demo MLS Austin",
"kind": "mls"
},
{
"destId": "net-members",
"state": "delivered",
"at": "2026-10-24T11:02:00.000Z",
"destination": "Member brokerages",
"kind": "network"
},
{
"destId": "portal-a",
"state": "delivered",
"at": "2026-10-24T11:02:00.000Z",
"destination": "Demo Portal A",
"kind": "portal"
}
]
}- Status
- 200
- When
- Deliveries, sorted by destination id.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- No such listing owned by this key's brokerage.
- Status
- 429
- When
- Over this key's requests per minute.
POST/api/v1/listings/{key}/media/uploads
Owner only, no body. Answers a one time URL: PUT the photo there as the raw body with its Content-Type (image/jpeg, image/png or image/webp, at most 8 MB), within two hours, then finish with POST /media and the uploadId. A listing holds at most 25 photos.
Auth: a Bearer key with listings:write.
keystring in pathRequired- ^OL-[A-Z0-9]{6}$
- The listing key. A key of the wrong shape answers 404, like a missing one.
curl -X POST "https://listonce.io/api/v1/listings/OL-4K7Q2M/media/uploads" \ -H "Authorization: Bearer $LISTONCE_KEY"
{
"uploadId": "5b0c1f9e-2d4a-4c1e-9a77-1f3e2b8d6c40",
"uploadUrl": "https://storage.example.com/object/upload/sign/listing-photos/uploads/OL-4K7Q2M.5b0c1f9e-2d4a-4c1e-9a77-1f3e2b8d6c40?token=eyJhbGciOi",
"method": "PUT",
"expiresAt": "2026-10-26T17:20:41.000Z",
"maxBytes": 8388608,
"contentTypes": [
"image/jpeg",
"image/png",
"image/webp"
]
}- Status
- 201
- When
- Where to PUT the photo.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- No such listing owned by this key's brokerage.
- Status
- 409
- When
- The listing already has 25 photos.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/v1/listings/{key}/media
Owner only. The photos in order; the first is the preferred one. MediaURL is a signed link on this host that lasts about 12 hours: store the photo, not the link.
Auth: a Bearer key with listings:read.
keystring in pathRequired- ^OL-[A-Z0-9]{6}$
- The listing key. A key of the wrong shape answers 404, like a missing one.
curl "https://listonce.io/api/v1/listings/OL-4K7Q2M/media" \ -H "Authorization: Bearer $LISTONCE_KEY"
{
"listingKey": "OL-4K7Q2M",
"value": [
{
"MediaKey": "OLP-7RQ2KD9XWM",
"ResourceRecordKey": "OL-4K7Q2M",
"ResourceName": "Property",
"MediaCategory": "Photo",
"MediaURL": "https://listonce.io/m/OLP-7RQ2KD9XWM.jpg?a=o&e=1793095200&s=Jp2c1v9yQkXo3H7bTn0aLw5rUe8sDf4gZi6mCxVhKqY",
"Order": 0,
"ImageWidth": 2560,
"ImageHeight": 1707,
"PreferredPhotoYN": true,
"ModificationTimestamp": "2026-10-26T15:20:41.000Z"
},
{
"MediaKey": "OLP-C4HNV8TZ3P",
"ResourceRecordKey": "OL-4K7Q2M",
"ResourceName": "Property",
"MediaCategory": "Photo",
"MediaURL": "https://listonce.io/m/OLP-C4HNV8TZ3P.jpg?a=o&e=1793095200&s=Jp2c1v9yQkXo3H7bTn0aLw5rUe8sDf4gZi6mCxVhKqY",
"Order": 1,
"ImageWidth": 2560,
"ImageHeight": 1707,
"PreferredPhotoYN": false,
"ModificationTimestamp": "2026-10-26T15:20:41.000Z"
}
]
}- Status
- 200
- When
- Photos, in order.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- No such listing owned by this key's brokerage.
- Status
- 429
- When
- Over this key's requests per minute.
POST/api/v1/listings/{key}/media
Owner only. Checks the uploaded file is the JPEG, PNG or WebP its first bytes say, turns it upright, scales it to at most 2560 pixels on the long side and stores it as a JPEG, which drops every metadata block (GPS location included). It becomes the last photo, or the preferred one if it is the first. Adds a history line and sends listing.updated. Finishing the same uploadId again answers the same photo.
Auth: a Bearer key with listings:write.
keystring in pathRequired- ^OL-[A-Z0-9]{6}$
- The listing key. A key of the wrong shape answers 404, like a missing one.
uploadIdstringRequired- From the upload step.
curl -X POST "https://listonce.io/api/v1/listings/OL-4K7Q2M/media" \
-H "Authorization: Bearer $LISTONCE_KEY" \
-H "Content-Type: application/json" \
-d '{
"uploadId": "5b0c1f9e-2d4a-4c1e-9a77-1f3e2b8d6c40"
}'{
"MediaKey": "OLP-C4HNV8TZ3P",
"ResourceRecordKey": "OL-4K7Q2M",
"ResourceName": "Property",
"MediaCategory": "Photo",
"MediaURL": "https://listonce.io/m/OLP-C4HNV8TZ3P.jpg?a=o&e=1793095200&s=Jp2c1v9yQkXo3H7bTn0aLw5rUe8sDf4gZi6mCxVhKqY",
"Order": 1,
"ImageWidth": 2560,
"ImageHeight": 1707,
"PreferredPhotoYN": false,
"ModificationTimestamp": "2026-10-26T15:20:41.000Z"
}- Status
- 201
- When
- The photo.
- Status
- 400
- When
- Unknown field, or uploadId is not an upload id.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- No such listing owned by this key's brokerage, or nothing was PUT to the upload URL.
- Status
- 409
- When
- The listing has 25 photos, or it changed while this request ran.
- Status
- 413
- When
- The body is over 16,384 bytes, or the photo over 8 MB.
- Status
- 415
- When
- The file is not a JPEG, PNG or WebP.
- Status
- 422
- When
- The file is damaged or over 50 megapixels.
- Status
- 429
- When
- Over this key's requests per minute.
PATCH/api/v1/listings/{key}/media
Owner only. order lists every MediaKey of the listing once; the first becomes the preferred photo. Adds a history line and sends listing.updated.
Auth: a Bearer key with listings:write.
keystring in pathRequired- ^OL-[A-Z0-9]{6}$
- The listing key. A key of the wrong shape answers 404, like a missing one.
orderarray of stringRequired- Every MediaKey of the listing once, first is preferred. 1 to 25.
curl -X PATCH "https://listonce.io/api/v1/listings/OL-4K7Q2M/media" \
-H "Authorization: Bearer $LISTONCE_KEY" \
-H "Content-Type: application/json" \
-d '{
"order": [
"OLP-C4HNV8TZ3P",
"OLP-7RQ2KD9XWM"
]
}'{
"listingKey": "OL-4K7Q2M",
"value": [
{
"MediaKey": "OLP-C4HNV8TZ3P",
"ResourceRecordKey": "OL-4K7Q2M",
"ResourceName": "Property",
"MediaCategory": "Photo",
"MediaURL": "https://listonce.io/m/OLP-C4HNV8TZ3P.jpg?a=o&e=1793095200&s=Jp2c1v9yQkXo3H7bTn0aLw5rUe8sDf4gZi6mCxVhKqY",
"Order": 0,
"ImageWidth": 2560,
"ImageHeight": 1707,
"PreferredPhotoYN": true,
"ModificationTimestamp": "2026-10-26T15:20:41.000Z"
},
{
"MediaKey": "OLP-7RQ2KD9XWM",
"ResourceRecordKey": "OL-4K7Q2M",
"ResourceName": "Property",
"MediaCategory": "Photo",
"MediaURL": "https://listonce.io/m/OLP-7RQ2KD9XWM.jpg?a=o&e=1793095200&s=Jp2c1v9yQkXo3H7bTn0aLw5rUe8sDf4gZi6mCxVhKqY",
"Order": 1,
"ImageWidth": 2560,
"ImageHeight": 1707,
"PreferredPhotoYN": false,
"ModificationTimestamp": "2026-10-26T15:20:41.000Z"
}
]
}- Status
- 200
- When
- The photos in their new order.
- Status
- 400
- When
- order is not every MediaKey of the listing once, or nothing changed.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- No such listing owned by this key's brokerage.
- Status
- 409
- When
- The listing changed while this request ran. Retry.
- Status
- 413
- When
- Body over 16,384 bytes.
- Status
- 429
- When
- Over this key's requests per minute.
DELETE/api/v1/listings/{key}/media/{mediaKey}
Owner only. Deletes the stored file; the next photo moves up, and if this was the preferred one, the new first photo is. Adds a history line and sends listing.updated.
Auth: a Bearer key with listings:write.
keystring in pathRequired- ^OL-[A-Z0-9]{6}$
- The listing key. A key of the wrong shape answers 404, like a missing one.
mediaKeystring in pathRequired- ^OLP-[A-HJ-NP-Z2-9]{10}$
- The photo's MediaKey.
curl -X DELETE "https://listonce.io/api/v1/listings/OL-4K7Q2M/media/OLP-7RQ2KD9XWM" \ -H "Authorization: Bearer $LISTONCE_KEY"
Removed.
- Status
- 204
- When
- Removed.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- No such listing owned by this key's brokerage, or no such photo on it.
- Status
- 409
- When
- The listing changed while this request ran. Retry.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/v1/export
Member brokerages only. Every listing the caller's brokerage owns, as the owner sees them: deliveries and whole history. Sorted by key, at most 50 a page; follow nextLink until it is absent. Signed-in admins can download the same as one JSON lines file from Settings.
Auth: a Bearer key with listings:read.
afterstring in query- ^OL-[A-Z0-9]{6}$
- Start after this listing key. nextLink sets it.
curl "https://listonce.io/api/v1/export" \ -H "Authorization: Bearer $LISTONCE_KEY"
{
"organization": "Harbor & Pine Realty",
"exportedAt": "2026-10-26T16:40:05.902Z",
"value": [
{
"street": "88 Cedar Bluff Dr",
"city": "Austin",
"state": "TX",
"zip": "78704",
"price": 625000,
"beds": 3,
"baths": 2.5,
"sqft": 1960,
"lotAcres": 0.18,
"yearBuilt": 2004,
"propertyType": "Single Family",
"remarks": "Quiet street, open kitchen and a covered patio facing the greenbelt.",
"agent": "Rowan Ellis",
"seller": "A. and T. Monroe",
"visibility": "members_only",
"listingAgreementAt": "2026-10-26",
"id": "OL-RCGZQ4",
"addressKey": "88|CEDAR BLUFF DR||AUSTIN|TX|78704",
"brokerage": "Harbor & Pine Realty",
"source": "api",
"status": "Active",
"returns": {},
"photos": [],
"sellerConsentAt": "2026-10-26T15:04:12.318Z",
"createdAt": "2026-10-26T15:04:12.318Z",
"updatedAt": "2026-10-26T15:04:12.318Z",
"tint": 0,
"deliveries": [
{
"destId": "mls-austin",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "portal-a",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "portal-b",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "portal-c",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "net-members",
"state": "delivered",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "data-appraisal",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
},
{
"destId": "data-records",
"state": "held",
"note": "Seller chose members only",
"at": "2026-10-26T15:04:12.318Z"
}
],
"history": [
{
"at": "2026-10-26T15:04:12.318Z",
"who": "Rowan Ellis",
"what": "Seller chose members only",
"hash": "0350a77c66e2d3"
},
{
"at": "2026-10-26T15:04:12.318Z",
"who": "Rowan Ellis",
"what": "Listing entered once",
"hash": "1cde064c84ab14"
},
{
"at": "2026-10-26T15:04:12.318Z",
"who": "ListOnce",
"what": "Sent to 1 of 7 destinations",
"hash": "1d54774e9f1ec6"
}
],
"historyIntact": true,
"lateDays": 0,
"remarkFlags": []
}
],
"nextLink": "https://listonce.io/api/v1/export?after=OL-RCGZQ4"
}- Status
- 200
- When
- One page.
- Status
- 400
- When
- after is not a listing key.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks listings:read, or its organization is not a member brokerage.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/v1/conflicts
Member brokerages only. Each of the caller's listings that was active at the same canonical address as another brokerage's active listing, open ones first. Addresses are compared after normalizing (St and Street, Apt and #, case, spacing). Of the other listing you see only its key, brokerage and status. The platform operator decides each one: a co-listing, a wrong address (naming which listing) or resolved; both owners get a history line and listing.conflict.
Auth: a Bearer key with listings:read.
listingstring in query- ^OL-[A-Z0-9]{6}$
- Only this listing's.
statusstring in query- open · all
- open for undecided ones only; all (the default) for every one.
curl "https://listonce.io/api/v1/conflicts" \ -H "Authorization: Bearer $LISTONCE_KEY"
{
"value": [
{
"id": "4f1c2a9e-5b7d-4e3a-9c21-6d8e0f4b2a17",
"status": "open",
"listingKey": "OL-RCGZQ4",
"other": {
"listingKey": "OL-7MWX2D",
"brokerage": "Bluebird Realty Co",
"status": "Active"
},
"wrongListing": null,
"createdAt": "2026-10-26T16:40:05.902Z",
"decidedAt": null
}
]
}- Status
- 200
- When
- Open first, then newest. At most 200.
- Status
- 400
- When
- An unknown parameter, a listing that is not a key, or a status other than open or all.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks listings:read, or its organization is not a member brokerage.
- Status
- 429
- When
- Over this key's requests per minute.
POST/api/v1/webhooks
The URL must be https on the standard port, carry no credentials, and resolve only to public addresses (checked again at every send). At most 10 per organization. The signing secret is in this response only.
Auth: a Bearer key with webhooks:manage.
Idempotency-Keystring in header- ^[\x21-\x7E]{1,200}$
- Optional. Repeats with the same key and body replay the first response for 24 hours; the same key with a different body is refused.
urlstringRequired- up to 500 chars
- https URL on the standard port.
eventsarray of string- listing.created · listing.updated · listing.conflict
- Defaults to listing.created and listing.updated. listing.conflict is sent only when asked for, and only member brokerages may ask (400 otherwise). Duplicates are dropped.
curl -X POST "https://listonce.io/api/v1/webhooks" \
-H "Authorization: Bearer $LISTONCE_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/hooks/listings",
"events": [
"listing.updated"
]
}'{
"id": "a7a332fd-bef7-4c6a-a362-9e9a3467e2d8",
"url": "https://example.com/hooks/listings",
"events": [
"listing.updated"
],
"active": true,
"createdAt": "2026-10-26T15:10:03.520883+00:00",
"secret": "whsec_4214177e6e99d1059b387b23bd89a068204cbd7b35b21692"
}- Status
- 201
- When
- Created. Store the secret now; it is not shown again.
- Status
- 400
- When
- Unknown field, bad events, or a URL that is not https, not public or does not resolve.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 409
- When
- The organization already has 10 webhooks, or a request with this Idempotency-Key is still running.
- Status
- 413
- When
- Body over 16,384 bytes.
- Status
- 422
- When
- This Idempotency-Key was used for a different body.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/v1/webhooks
Every webhook of the key's organization, oldest first, with its most recent delivery.
Auth: a Bearer key with webhooks:manage.
curl "https://listonce.io/api/v1/webhooks" \ -H "Authorization: Bearer $LISTONCE_KEY"
{
"value": [
{
"id": "a7a332fd-bef7-4c6a-a362-9e9a3467e2d8",
"url": "https://example.com/hooks/listings",
"events": [
"listing.updated"
],
"active": true,
"createdAt": "2026-10-26T15:10:03.520883+00:00",
"lastDelivery": {
"status": "sent",
"code": 200,
"attempts": 1,
"at": "2026-10-26T16:40:06.114771+00:00"
}
}
]
}- Status
- 200
- When
- Webhooks.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 429
- When
- Over this key's requests per minute.
DELETE/api/v1/webhooks/{id}
Deletes the webhook and its delivery records.
Auth: a Bearer key with webhooks:manage.
idstring in pathRequired- The webhook id. A malformed id answers 404.
curl -X DELETE "https://listonce.io/api/v1/webhooks/a7a332fd-bef7-4c6a-a362-9e9a3467e2d8" \ -H "Authorization: Bearer $LISTONCE_KEY"
Deleted.
- Status
- 204
- When
- Deleted.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- No such webhook for this key's organization.
- Status
- 429
- When
- Over this key's requests per minute.
POST/api/v1/keys/rotate
Replaces the key that signs the request. The new key (same label, scopes and rate limit) is returned once and works at once; the old key keeps working for 24 hours, then stops. Rotating a key that is already being replaced answers 409, so store the new key before anything else. No body. The published demo key cannot be rotated.
Auth: a Bearer key with listings:write or listings:read or feed:read or webhooks:manage.
curl -X POST "https://listonce.io/api/v1/keys/rotate" \ -H "Authorization: Bearer $LISTONCE_KEY"
{
"key": "ol_live_3f9c0d2b7a8e4c1f9d6b5a4e3c2b1a0f9e8d7c6b",
"id": "9b1c7e3a-4f2d-4c8b-a6e5-1d2c3b4a5f60",
"prefix": "ol_live_3f9c0d",
"previousKeyExpiresAt": "2026-10-28T15:10:03.520Z"
}- Status
- 201
- When
- The new key. It is not shown again.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The published demo key, which is shared and cannot be rotated.
- Status
- 409
- When
- This key was already rotated; its replacement is the key to use.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/reso
The OData service document: the entity sets this feed publishes.
Auth: a Bearer key with feed:read.
keystring in query- The API key, for a browser link. Prefer the Authorization header: query strings end up in logs.
curl "https://listonce.io/api/reso" \ -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
{
"@odata.context": "https://listonce.io/api/reso/$metadata",
"value": [
{
"name": "Property",
"kind": "EntitySet",
"url": "Property"
},
{
"name": "Media",
"kind": "EntitySet",
"url": "Media"
},
{
"name": "Lookup",
"kind": "EntitySet",
"url": "Lookup"
}
]
}- Status
- 200
- When
- Entity sets.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/reso/$metadata
The OData CSDL XML for Property (with its Media navigation property), Media and Lookup, with RESO lookup annotations. Always XML; $format=json answers 415.
Auth: a Bearer key with feed:read.
keystring in query- The API key, for a browser link. Prefer the Authorization header: query strings end up in logs.
curl "https://listonce.io/api/reso/$metadata" \ -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
application/xml.
- Status
- 200
- When
- application/xml.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/reso/Lookup
Every lookup value the feed can send, as the RESO Lookup resource. Takes the same query options as Property.
Auth: a Bearer key with feed:read.
keystring in query- The API key, for a browser link. Prefer the Authorization header: query strings end up in logs.
curl "https://listonce.io/api/reso/Lookup" \ -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
{
"@odata.context": "https://listonce.io/api/reso/$metadata#Lookup",
"value": [
{
"LookupKey": "StandardStatus:Active",
"LookupName": "StandardStatus",
"LookupValue": "Active",
"StandardLookupValue": "Active",
"LegacyODataValue": "Active",
"ModificationTimestamp": "2026-10-26T00:00:00.000Z"
}
]
}- Status
- 200
- When
- A page of lookup rows.
- Status
- 400
- When
- A query option the server cannot read.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 429
- When
- Over this key's requests per minute.
POST/api/reso/Property
RESO Web API Add/Edit, for systems that already speak RESO. The same listing, rules and owner as POST /api/v1/listings, in Data Dictionary names plus two extensions: LO_SellerVisibilityChoice and LO_SellerName (write only; never in the feed). We assign ListingKey; read only fields (ListingKey, OriginatingSystemName, ListOfficeName, ModificationTimestamp, LO_SellerChoiceRecordedAt) and @odata. annotations are ignored, and any other name is refused. Baths: BathroomsFull with BathroomsHalf, or BathroomsTotalInteger alone. StandardStatus may be sent if it matches the seller's choice (Coming Soon for coming_soon, otherwise Active); OnMarketDate is the go live date for coming_soon. Errors carry target (Create) and details naming each field.
Auth: a Bearer key with listings:write.
Idempotency-Keystring in header- ^[\x21-\x7E]{1,200}$
- Optional. Repeats with the same key and body replay the first response for 24 hours; the same key with a different body is refused.
Preferstring in header- return=representation · return=minimal
- Optional.
return=representationanswers with the record,return=minimalwith no body; echoed in Preference-Applied. Without it a POST answers 201 with the record and a PATCH 204.
UnparsedAddressstringRequired- Street, city, state ZIP. The text before the first comma is stored as the street (1 to 120 characters, no < or >); city, state and ZIP come from their own fields.
CitystringRequired- Austin · Charlotte · Phoenix · Tampa
- Pilot market.
StateOrProvincestringRequired- TX · NC · AZ · FL
- Two letter state; must match the city.
PostalCodestringRequired- ^\d{5}$
- 5 digit ZIP.
ListPriceintegerRequired- 1 to 1,000,000,000
- List price in whole dollars.
BedroomsTotalintegerRequired- 0 to 50
- Bedrooms.
BathroomsFullinteger- 0 to 50
- Full bathrooms. Send with BathroomsHalf.
BathroomsHalfinteger- 0 to 50
- Half bathrooms, each counted as 0.5. Send with BathroomsFull (0 when there is none).
BathroomsTotalIntegerinteger- 0 to 50
- Used only when BathroomsFull and BathroomsHalf are absent.
LivingAreaintegerRequired- 0 to 100,000
- Living area in square feet.
LivingAreaUnitsstring- Square Feet
- Only Square Feet.
LotSizeAcresnumber- 0 to 100,000, steps of 0.01
- Lot size in acres.
YearBuiltinteger- 1700 to 2030
- Year built.
PropertyTypestring- Residential
- Only Residential.
PropertySubTypestringRequired- Single Family Residence · Townhouse · Condominium · Multi Family
- RESO PropertySubType.
PublicRemarksstringRequired- 1 to 2000 chars
- Public remarks. Not blank, no < or >.
ListAgentFullNamestringRequired- 1 to 80 chars
- Listing agent's name. Cannot change after entry. Not blank, no < or >.
ListingContractDatestringRequired- YYYY-MM-DD
- The day the listing agreement was signed. Within the last year, not after tomorrow (UTC). Cannot change after entry.
StandardStatusstring- Active · Coming Soon
- Optional on create; must match the seller's choice.
OnMarketDatestring- YYYY-MM-DD
- Go live date, required for coming_soon. Not in the past. Ignored for other choices.
LO_SellerVisibilityChoicestringRequired- public · coming_soon · members_only
- Extension: the seller's choice: public, coming_soon or members_only.
LO_SellerNamestringRequired- 1 to 120 chars
- Extension: the seller's name. Write only: seen by the owner, never in the feed. Cannot change after entry. Not blank, no < or >.
ListingKeystring or null- We assign the key; on a PATCH, any other key than the record's own is refused. Read only: ignored.
OriginatingSystemNamestring or null- Always this platform. Read only: ignored.
ListOfficeNamestring or null- The caller's brokerage. Read only: ignored.
ModificationTimestampstring or null- Set on every change. Read only: ignored.
LO_SellerChoiceRecordedAtstring or null- Set when the choice changes. Read only: ignored.
curl -X POST "https://listonce.io/api/reso/Property" \
-H "Authorization: Bearer $LISTONCE_KEY" \
-H "Content-Type: application/json" \
-d '{
"UnparsedAddress": "88 Cedar Bluff Dr, Austin, TX 78704",
"City": "Austin",
"StateOrProvince": "TX",
"PostalCode": "78704",
"ListPrice": 625000,
"BedroomsTotal": 3,
"BathroomsFull": 2,
"BathroomsHalf": 1,
"LivingArea": 1960,
"LotSizeAcres": 0.18,
"YearBuilt": 2004,
"PropertyType": "Residential",
"PropertySubType": "Single Family Residence",
"PublicRemarks": "Quiet street, open kitchen and a covered patio facing the greenbelt.",
"ListAgentFullName": "Rowan Ellis",
"ListingContractDate": "2026-10-26",
"LO_SellerVisibilityChoice": "members_only",
"LO_SellerName": "A. and T. Monroe"
}'{
"@odata.context": "https://listonce.io/api/reso/$metadata#Property/$entity",
"ListingKey": "OL-RCGZQ4",
"OriginatingSystemName": "ListOnce",
"StandardStatus": "Active",
"ListPrice": 625000,
"ClosePrice": null,
"UnparsedAddress": "88 Cedar Bluff Dr, Austin, TX 78704",
"City": "Austin",
"StateOrProvince": "TX",
"PostalCode": "78704",
"PropertyType": "Residential",
"PropertySubType": "Single Family Residence",
"BedroomsTotal": 3,
"BathroomsTotalInteger": 3,
"BathroomsFull": 2,
"BathroomsHalf": 1,
"LivingArea": 1960,
"LivingAreaUnits": "Square Feet",
"LotSizeAcres": 0.18,
"YearBuilt": 2004,
"PublicRemarks": "Quiet street, open kitchen and a covered patio facing the greenbelt.",
"ListAgentFullName": "Rowan Ellis",
"ListOfficeName": "Harbor & Pine Realty",
"ListingContractDate": "2026-10-26",
"OnMarketDate": "2026-10-26",
"ModificationTimestamp": "2026-10-26T15:04:12.318Z",
"LO_SellerVisibilityChoice": "members_only",
"LO_SellerChoiceRecordedAt": "2026-10-26T15:04:12.318Z"
}- Status
- 201
- When
- Created. The record as the owner sees it.
- Status
- 204
- When
- Created, with
Prefer: return=minimal.
- Status
- 400
- When
- A field failed validation, is missing or is unknown. details names each field.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks listings:write, or its organization is not a member brokerage.
- Status
- 409
- When
- The brokerage already has an active listing (Coming Soon, Active or Pending) at this address: existingKey names it. Update that listing, or withdraw it before listing again. Or a request with this Idempotency-Key is still running.
- Status
- 413
- When
- Body over 16,384 bytes.
- Status
- 422
- When
- This Idempotency-Key was used for a different body.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/reso/Property
RESO Web API Core style. Newest change first unless $orderby says otherwise, then by key. Members only and coming soon listings are never included. Pages hold at most 50 records; a nextLink follows when the server cut a page short. Property('OL-4K7Q2M') fetches one record too.
Auth: a Bearer key with feed:read.
$topinteger in query- 0 to undefined
- How many records you want. The server sends at most 50 per page.
$skipinteger in query- 0 to 100,000
- Records to skip, up to 100,000.
$filterstring in query- up to 2000 chars
- OData:
eq ne gt ge lt le,and or not, parentheses,nullwith eq and ne. Strings in single quotes, dates as YYYY-MM-DD, timestamps in ISO 8601. Fields: ListingKey, StandardStatus, ListPrice, ClosePrice, City, StateOrProvince, PostalCode, PropertySubType, BedroomsTotal, LivingArea, LotSizeAcres, YearBuilt, ListAgentFullName, ListOfficeName, ListingContractDate, ModificationTimestamp. $selectstring in query- Comma separated fields to return.
$orderbystring in query- Up to 5 filterable fields, each optionally
ascordesc. $countstring in query- true · false
trueadds @odata.count.$expandstring in query- Media
Mediaadds each record's photos, in order, as a Media array. Nothing else expands.keystring in query- The API key, for a browser link. Prefer the Authorization header: query strings end up in logs.
curl "https://listonce.io/api/reso/Property" \ -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
{
"@odata.context": "https://listonce.io/api/reso/$metadata#Property",
"@odata.count": 1,
"value": [
{
"ListingKey": "OL-4K7Q2M",
"OriginatingSystemName": "ListOnce",
"StandardStatus": "Active",
"ListPrice": 589000,
"ClosePrice": null,
"UnparsedAddress": "1418 Larkspur Way, Austin, TX 78745",
"City": "Austin",
"StateOrProvince": "TX",
"PostalCode": "78745",
"PropertyType": "Residential",
"PropertySubType": "Single Family Residence",
"BedroomsTotal": 3,
"BathroomsTotalInteger": 2,
"BathroomsFull": 2,
"BathroomsHalf": 0,
"LivingArea": 1840,
"LivingAreaUnits": "Square Feet",
"LotSizeAcres": 0.21,
"YearBuilt": 1998,
"PublicRemarks": "Shaded corner lot, updated kitchen, two living areas and a deep backyard with mature oaks.",
"ListAgentFullName": "Dana Whitfield",
"ListOfficeName": "Harbor & Pine Realty",
"ListingContractDate": "2026-10-24",
"OnMarketDate": "2026-10-24",
"ModificationTimestamp": "2026-10-24T21:03:00.000Z",
"LO_SellerVisibilityChoice": "public",
"LO_SellerChoiceRecordedAt": "2026-10-24T10:20:00.000Z"
}
]
}- Status
- 200
- When
- A page of RESO records.
- Status
- 400
- When
- A query option the server cannot read: an unknown field, operator or option.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 429
- When
- Over this key's requests per minute.
PATCH/api/reso/Property('{key}')
RESO Web API Add/Edit. Owner only, with the same rules as PATCH /api/v1/listings/{key}: send just the fields that change; null clears LotSizeAcres or YearBuilt. OnMarketDate sets the go live date while the listing is Coming Soon and is ignored otherwise. ListAgentFullName, ListingContractDate and LO_SellerName cannot change. A record read from this API can be sent back whole: read only fields are ignored. Also answers at /api/reso/Property/{key}. Honors Idempotency-Key.
Auth: a Bearer key with listings:write.
keystring in pathRequired- ^OL-[A-Z0-9]{6}$
- The listing key. A key of the wrong shape answers 404, like a missing one.
Idempotency-Keystring in header- ^[\x21-\x7E]{1,200}$
- Optional. Repeats with the same key and body replay the first response for 24 hours; the same key with a different body is refused.
Preferstring in header- return=representation · return=minimal
- Optional.
return=representationanswers with the record,return=minimalwith no body; echoed in Preference-Applied. Without it a POST answers 201 with the record and a PATCH 204.
UnparsedAddressstring- Street, city, state ZIP. The text before the first comma is stored as the street (1 to 120 characters, no < or >); city, state and ZIP come from their own fields.
Citystring- Austin · Charlotte · Phoenix · Tampa
- Pilot market.
StateOrProvincestring- TX · NC · AZ · FL
- Two letter state; must match the city.
PostalCodestring- ^\d{5}$
- 5 digit ZIP.
ListPriceinteger- 1 to 1,000,000,000
- List price in whole dollars.
BedroomsTotalinteger- 0 to 50
- Bedrooms.
BathroomsFullinteger- 0 to 50
- Full bathrooms. Send with BathroomsHalf.
BathroomsHalfinteger- 0 to 50
- Half bathrooms, each counted as 0.5. Send with BathroomsFull (0 when there is none).
BathroomsTotalIntegerinteger- 0 to 50
- Used only when BathroomsFull and BathroomsHalf are absent.
LivingAreainteger- 0 to 100,000
- Living area in square feet.
LivingAreaUnitsstring- Square Feet
- Only Square Feet.
LotSizeAcresnumber or null- 0 to 100,000, steps of 0.01
- Lot size in acres. null clears it.
YearBuiltinteger or null- 1700 to 2030
- Year built. null clears it.
PropertyTypestring- Residential
- Only Residential.
PropertySubTypestring- Single Family Residence · Townhouse · Condominium · Multi Family
- RESO PropertySubType.
PublicRemarksstring- 1 to 2000 chars
- Public remarks. Not blank, no < or >.
ListAgentFullNamestring- 1 to 80 chars
- Listing agent's name. Cannot change after entry. Not blank, no < or >.
ListingContractDatestring- YYYY-MM-DD
- The day the listing agreement was signed. Within the last year, not after tomorrow (UTC). Cannot change after entry.
StandardStatusstring- Coming Soon · Active · Pending · Closed · Withdrawn
- New status.
OnMarketDatestring or null- YYYY-MM-DD
- Go live date while Coming Soon; ignored otherwise.
LO_SellerVisibilityChoicestring- public · coming_soon · members_only
- Extension: the seller's choice: public, coming_soon or members_only.
LO_SellerNamestring- 1 to 120 chars
- Extension: the seller's name. Write only: seen by the owner, never in the feed. Cannot change after entry. Not blank, no < or >.
ListingKeystring or null- We assign the key; on a PATCH, any other key than the record's own is refused. Read only: ignored.
OriginatingSystemNamestring or null- Always this platform. Read only: ignored.
ListOfficeNamestring or null- The caller's brokerage. Read only: ignored.
ModificationTimestampstring or null- Set on every change. Read only: ignored.
LO_SellerChoiceRecordedAtstring or null- Set when the choice changes. Read only: ignored.
ClosePriceinteger or null- 1 to 1,000,000,000
- Sold price; only when the status is Closed. null is ignored.
curl -X PATCH "https://listonce.io/api/reso/Property('OL-4K7Q2M')" \
-H "Authorization: Bearer $LISTONCE_KEY" \
-H "Content-Type: application/json" \
-d '{
"ListPrice": 615000,
"StandardStatus": "Pending"
}'{
"@odata.context": "https://listonce.io/api/reso/$metadata#Property/$entity",
"ListingKey": "OL-RCGZQ4",
"OriginatingSystemName": "ListOnce",
"StandardStatus": "Pending",
"ListPrice": 615000,
"ClosePrice": null,
"UnparsedAddress": "88 Cedar Bluff Dr, Austin, TX 78704",
"City": "Austin",
"StateOrProvince": "TX",
"PostalCode": "78704",
"PropertyType": "Residential",
"PropertySubType": "Single Family Residence",
"BedroomsTotal": 3,
"BathroomsTotalInteger": 3,
"BathroomsFull": 2,
"BathroomsHalf": 1,
"LivingArea": 1960,
"LivingAreaUnits": "Square Feet",
"LotSizeAcres": 0.18,
"YearBuilt": 2004,
"PublicRemarks": "Quiet street, open kitchen and a covered patio facing the greenbelt.",
"ListAgentFullName": "Rowan Ellis",
"ListOfficeName": "Harbor & Pine Realty",
"ListingContractDate": "2026-10-26",
"OnMarketDate": "2026-10-26",
"ModificationTimestamp": "2026-10-26T16:40:05.902Z",
"LO_SellerVisibilityChoice": "members_only",
"LO_SellerChoiceRecordedAt": "2026-10-26T15:04:12.318Z"
}- Status
- 200
- When
- Changed, with
Prefer: return=representation: the record as the owner sees it.
- Status
- 204
- When
- Changed. No body unless
Prefer: return=representation.
- Status
- 400
- When
- A field failed validation or is unknown, a cross field rule failed, or nothing would change. details names the field.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- No such listing owned by this key's brokerage.
- Status
- 409
- When
- The brokerage already has an active listing (Coming Soon, Active or Pending) at this address: existingKey names it. Update that listing, or withdraw it before listing again. Or the listing changed while this request ran, or a request with this Idempotency-Key is still running.
- Status
- 413
- When
- Body over 16,384 bytes.
- Status
- 422
- When
- This Idempotency-Key was used for a different body.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/reso/Property/{key}
The RESO record, while the listing may be publicly marketed.
Auth: a Bearer key with feed:read.
keystring in pathRequired- ^OL-[A-Z0-9]{6}$
- The listing key. A key of the wrong shape answers 404, like a missing one.
$expandstring in query- Media
Mediaadds each record's photos, in order, as a Media array. Nothing else expands.keystring in query- The API key, for a browser link. Prefer the Authorization header: query strings end up in logs.
curl "https://listonce.io/api/reso/Property/OL-4K7Q2M" \ -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
{
"@odata.context": "https://listonce.io/api/reso/$metadata#Property/$entity",
"ListingKey": "OL-4K7Q2M",
"OriginatingSystemName": "ListOnce",
"StandardStatus": "Active",
"ListPrice": 589000,
"ClosePrice": null,
"UnparsedAddress": "1418 Larkspur Way, Austin, TX 78745",
"City": "Austin",
"StateOrProvince": "TX",
"PostalCode": "78745",
"PropertyType": "Residential",
"PropertySubType": "Single Family Residence",
"BedroomsTotal": 3,
"BathroomsTotalInteger": 2,
"BathroomsFull": 2,
"BathroomsHalf": 0,
"LivingArea": 1840,
"LivingAreaUnits": "Square Feet",
"LotSizeAcres": 0.21,
"YearBuilt": 1998,
"PublicRemarks": "Shaded corner lot, updated kitchen, two living areas and a deep backyard with mature oaks.",
"ListAgentFullName": "Dana Whitfield",
"ListOfficeName": "Harbor & Pine Realty",
"ListingContractDate": "2026-10-24",
"OnMarketDate": "2026-10-24",
"ModificationTimestamp": "2026-10-24T21:03:00.000Z",
"LO_SellerVisibilityChoice": "public",
"LO_SellerChoiceRecordedAt": "2026-10-24T10:20:00.000Z"
}- Status
- 200
- When
- The RESO record.
- Status
- 400
- When
- $expand names something other than Media.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 404
- When
- Not in the feed: missing, members only or coming soon.
- Status
- 429
- When
- Over this key's requests per minute.
GET/api/reso/Media
The RESO Media resource: photos of the listings in the feed only, newest change first unless $orderby says otherwise, then by MediaKey. Takes the same query options as Property; pages hold at most 100. Media('OLP-7RQ2KD9XWM') fetches one. A feed link stops working as soon as its listing leaves the feed. MediaURL is a signed link on this host that lasts about 12 hours: store the photo, not the link.
Auth: a Bearer key with feed:read.
$filterstring in query- up to 2000 chars
- As on Property. PreferredPhotoYN takes true or false with eq and ne. Fields: MediaKey, ResourceRecordKey, MediaCategory, Order, ImageWidth, ImageHeight, PreferredPhotoYN, ModificationTimestamp.
keystring in query- The API key, for a browser link. Prefer the Authorization header: query strings end up in logs.
curl "https://listonce.io/api/reso/Media" \ -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
{
"@odata.context": "https://listonce.io/api/reso/$metadata#Media",
"value": [
{
"MediaKey": "OLP-7RQ2KD9XWM",
"ResourceRecordKey": "OL-4K7Q2M",
"ResourceName": "Property",
"MediaCategory": "Photo",
"MediaURL": "https://listonce.io/m/OLP-7RQ2KD9XWM.jpg?a=p&e=1793095200&s=Jp2c1v9yQkXo3H7bTn0aLw5rUe8sDf4gZi6mCxVhKqY",
"Order": 0,
"ImageWidth": 2560,
"ImageHeight": 1707,
"PreferredPhotoYN": true,
"ModificationTimestamp": "2026-10-26T15:20:41.000Z"
},
{
"MediaKey": "OLP-C4HNV8TZ3P",
"ResourceRecordKey": "OL-4K7Q2M",
"ResourceName": "Property",
"MediaCategory": "Photo",
"MediaURL": "https://listonce.io/m/OLP-C4HNV8TZ3P.jpg?a=p&e=1793095200&s=Jp2c1v9yQkXo3H7bTn0aLw5rUe8sDf4gZi6mCxVhKqY",
"Order": 1,
"ImageWidth": 2560,
"ImageHeight": 1707,
"PreferredPhotoYN": false,
"ModificationTimestamp": "2026-10-26T15:20:41.000Z"
}
]
}- Status
- 200
- When
- A page of Media records.
- Status
- 400
- When
- A query option the server cannot read.
- Status
- 401
- When
- No key, a malformed key, or a revoked one.
- Status
- 403
- When
- The key lacks the scope this route needs.
- Status
- 429
- When
- Over this key's requests per minute.
A photo in three calls
The file goes straight to storage, so it can be up to 8 MB. Nothing is a photo until the last call has checked it, removed its location data and stored it.
UPLOAD=$(curl -s -X POST "https://listonce.io/api/v1/listings/OL-4K7Q2M/media/uploads" \
-H "Authorization: Bearer $LISTONCE_KEY")
curl -X PUT "$(echo "$UPLOAD" | jq -r .uploadUrl)" \
-H "Content-Type: image/jpeg" --data-binary @front.jpg
curl -X POST "https://listonce.io/api/v1/listings/OL-4K7Q2M/media" \
-H "Authorization: Bearer $LISTONCE_KEY" -H "Content-Type: application/json" \
-d "{\"uploadId\": \"$(echo "$UPLOAD" | jq -r .uploadId)\"}"Error body
{
"error": {
"code": "400",
"message": "zip must be a 5 digit ZIP"
}
}