Endpoints

Every route, from the OpenAPI document the server is checked against.

Method
POST
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
Does
Export everything your brokerage owns
Scope
listings:read
Method
GET
Does
Same address as another brokerage
Scope
listings:read
Method
POST
Does
Subscribe to listing changes
Scope
webhooks:manage
Method
GET
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
Does
Service document
Scope
feed:read
Method
GET
Does
Data model
Scope
feed:read
Method
GET
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
Does
Photos in the feed
Scope
feed:read

POST/api/v1/listings

Enter a listing once

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.

Parameters
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.
Body
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).
Request
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"
  }'
Response 201
{
  "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}

Read a listing

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.

Parameters
keystring in pathRequired
^OL-[A-Z0-9]{6}$
The listing key. A key of the wrong shape answers 404, like a missing one.
Request
curl "https://listonce.io/api/v1/listings/OL-4K7Q2M" \
  -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
Response 200
{
  "@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}

Change a listing

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.

Parameters
keystring in pathRequired
^OL-[A-Z0-9]{6}$
The listing key. A key of the wrong shape answers 404, like a missing one.
Body
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.
Request
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"
  }'
Response 200
{
  "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

Where a listing is live

Owner only: one line per destination in the listing's market with its outcome and any note.

Auth: a Bearer key with listings:read.

Parameters
keystring in pathRequired
^OL-[A-Z0-9]{6}$
The listing key. A key of the wrong shape answers 404, like a missing one.
Request
curl "https://listonce.io/api/v1/listings/OL-4K7Q2M/deliveries" \
  -H "Authorization: Bearer $LISTONCE_KEY"
Response 200
{
  "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

Start a photo upload

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.

Parameters
keystring in pathRequired
^OL-[A-Z0-9]{6}$
The listing key. A key of the wrong shape answers 404, like a missing one.
Request
curl -X POST "https://listonce.io/api/v1/listings/OL-4K7Q2M/media/uploads" \
  -H "Authorization: Bearer $LISTONCE_KEY"
Response 201
{
  "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

A listing's photos

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.

Parameters
keystring in pathRequired
^OL-[A-Z0-9]{6}$
The listing key. A key of the wrong shape answers 404, like a missing one.
Request
curl "https://listonce.io/api/v1/listings/OL-4K7Q2M/media" \
  -H "Authorization: Bearer $LISTONCE_KEY"
Response 200
{
  "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

Finish a photo upload

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.

Parameters
keystring in pathRequired
^OL-[A-Z0-9]{6}$
The listing key. A key of the wrong shape answers 404, like a missing one.
Body
uploadIdstringRequired
From the upload step.
Request
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"
  }'
Response 201
{
  "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

Reorder photos

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.

Parameters
keystring in pathRequired
^OL-[A-Z0-9]{6}$
The listing key. A key of the wrong shape answers 404, like a missing one.
Body
orderarray of stringRequired
Every MediaKey of the listing once, first is preferred. 1 to 25.
Request
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"
    ]
  }'
Response 200
{
  "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}

Remove a photo

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.

Parameters
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.
Request
curl -X DELETE "https://listonce.io/api/v1/listings/OL-4K7Q2M/media/OLP-7RQ2KD9XWM" \
  -H "Authorization: Bearer $LISTONCE_KEY"
Response 204
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

Export everything your brokerage owns

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.

Parameters
afterstring in query
^OL-[A-Z0-9]{6}$
Start after this listing key. nextLink sets it.
Request
curl "https://listonce.io/api/v1/export" \
  -H "Authorization: Bearer $LISTONCE_KEY"
Response 200
{
  "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

Same address as another brokerage

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.

Parameters
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.
Request
curl "https://listonce.io/api/v1/conflicts" \
  -H "Authorization: Bearer $LISTONCE_KEY"
Response 200
{
  "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

Subscribe to listing changes

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.

Parameters
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.
Body
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.
Request
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"
    ]
  }'
Response 201
{
  "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

Your subscriptions

Every webhook of the key's organization, oldest first, with its most recent delivery.

Auth: a Bearer key with webhooks:manage.

Request
curl "https://listonce.io/api/v1/webhooks" \
  -H "Authorization: Bearer $LISTONCE_KEY"
Response 200
{
  "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}

Unsubscribe

Deletes the webhook and its delivery records.

Auth: a Bearer key with webhooks:manage.

Parameters
idstring in pathRequired
The webhook id. A malformed id answers 404.
Request
curl -X DELETE "https://listonce.io/api/v1/webhooks/a7a332fd-bef7-4c6a-a362-9e9a3467e2d8" \
  -H "Authorization: Bearer $LISTONCE_KEY"
Response 204
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

Rotate this key

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.

Request
curl -X POST "https://listonce.io/api/v1/keys/rotate" \
  -H "Authorization: Bearer $LISTONCE_KEY"
Response 201
{
  "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

Service document

The OData service document: the entity sets this feed publishes.

Auth: a Bearer key with feed:read.

Parameters
keystring in query
The API key, for a browser link. Prefer the Authorization header: query strings end up in logs.
Request
curl "https://listonce.io/api/reso" \
  -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
Response 200
{
  "@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

Data model

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.

Parameters
keystring in query
The API key, for a browser link. Prefer the Authorization header: query strings end up in logs.
Request
curl "https://listonce.io/api/reso/$metadata" \
  -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
Response 200
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

Lookup values

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.

Parameters
keystring in query
The API key, for a browser link. Prefer the Authorization header: query strings end up in logs.
Request
curl "https://listonce.io/api/reso/Lookup" \
  -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
Response 200
{
  "@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

Enter a listing in RESO fields

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.

Parameters
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=representation answers with the record, return=minimal with no body; echoed in Preference-Applied. Without it a POST answers 201 with the record and a PATCH 204.
Body
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.
Request
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"
  }'
Response 201
{
  "@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

Search the feed

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.

Parameters
$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, null with 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 asc or desc.
$countstring in query
true · false
true adds @odata.count.
$expandstring in query
Media
Media adds 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.
Request
curl "https://listonce.io/api/reso/Property" \
  -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
Response 200
{
  "@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}')

Change a listing in RESO fields

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.

Parameters
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=representation answers with the record, return=minimal with no body; echoed in Preference-Applied. Without it a POST answers 201 with the record and a PATCH 204.
Body
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.
Request
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"
  }'
Response 200
{
  "@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}

One listing from the feed

The RESO record, while the listing may be publicly marketed.

Auth: a Bearer key with feed:read.

Parameters
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
Media adds 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.
Request
curl "https://listonce.io/api/reso/Property/OL-4K7Q2M" \
  -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
Response 200
{
  "@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

Photos in the feed

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.

Parameters
$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.
Request
curl "https://listonce.io/api/reso/Media" \
  -H "Authorization: Bearer ol_demo_7f3a9c2e5b1d4086"
Response 200
{
  "@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.

curl
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"
  }
}