Authentication

Every call carries an API key. Keys belong to an organization and carry scopes.

Bearer keys

Header
Authorization: Bearer ol_demo_7f3a9c2e5b1d4086

Keys look like ol_live_ followed by 40 characters. The operator issues them per organization and shows each one once; only a hash is stored, so a lost key is revoked and replaced, never recovered. The two RESO feed routes also take ?key= so a link works in a browser; prefer the header, since query strings end up in logs.

Rotating a key

Request
curl -X POST https://listonce.io/api/v1/keys/rotate -H "Authorization: Bearer $LISTONCE_KEY"

A key can replace itself. The answer holds the new key, shown once, and previousKeyExpiresAt: the old key keeps working for 24 hours, so you can deploy the new one without downtime, then it stops. A second rotation of the same old key answers 409, so save the new key before anything else. Admins can also rotate a key from Settings in the console.

Scopes

Scope
listings:write
Lets the key
Enter listings and change its own organization's listings; for an MLS or the operator, run the MLS sync
Given to
Members, MLSs, operator
Scope
listings:read
Lets the key
Read its own organization's listings in full, with deliveries and history; read the MLS sync log
Given to
Members, MLSs, operator
Scope
feed:read
Lets the key
Read the RESO feed: listings that may be publicly marketed, without the seller
Given to
Licensees, members
Scope
webhooks:manage
Lets the key
Add, list and remove its organization's webhooks
Given to
Anyone who listens

A key without the scope a route needs gets 403. A listing the key may not read or change answers 404, never 403, so keys cannot probe for listings.

Rate limits

Each key has a per minute limit: 120 unless agreed, 600 for the shared sandbox key. Every request made with the key counts, refused ones included. Over the limit, the answer is 429 with Retry-After in seconds until the minute turns.

Errors

Every refusal has the same body. The message says what to fix and never carries listing data.

{
  "error": {
    "code": "403",
    "message": "This key needs the listings:write scope"
  }
}
Status
400
Means
A field failed validation, a field is unknown, the body is not a JSON object, or the change changes nothing
Status
401
Means
No key, a malformed key or a revoked one
Status
403
Means
The key lacks the scope, or (entering listings) its organization is not a member brokerage
Status
404
Means
No such record, or not one this key may see
Status
409
Means
A concurrent change: read again and retry. Also a still running Idempotency-Key, or the webhook cap
Status
413
Means
Body over 16,384 bytes
Status
422
Means
An Idempotency-Key reused with a different body
Status
429
Means
Over the key's per minute limit
Status
500
Means
Our fault. Safe to retry a POST that carried an Idempotency-Key

Input rules

Bodies are JSON objects. Unknown fields are refused rather than ignored, so a typo never passes silently. Text is trimmed, must not be blank, and may hold line breaks but no < or > or other control characters. Each field's type and limits are on the Endpoints page.

Idempotency

Send Idempotency-Key (1 to 200 visible characters) on POST. A repeat with the same key and body within 24 hours replays the first answer with Idempotent-Replayed: true; the same key with another body is refused with 422. Keys are per organization.

curl
curl -X POST "https://listonce.io/api/v1/listings" \
  -H "Authorization: Bearer $LISTONCE_KEY" \
  -H "Idempotency-Key: crm-7781-listing" \
  -H "Content-Type: application/json" \
  -d @listing.json