Authentication
Every call carries an API key. Keys belong to an organization and carry scopes.
Bearer keys
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
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 -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