Skip to content
Osyro

API reference

Listings API

A read-only REST API that serves the complete, current set of active property listings as JSON. It is built for scheduled sync: your platform pulls the feed on a timer and keeps its copy identical to Osyro's.

On this page

Overview

Base URL
https://api.osyro.com
Auth
X-API-Key header
Format
JSON over HTTPS
Timestamps
ISO 8601, UTC

Quickstart

Three steps to your first response.

  1. 1

    Get an API key from the Osyro team and store it as an environment variable, for example OSYRO_API_KEY.

  2. 2

    Call GET /listings with the key in the X-API-Key header.

  3. 3

    Read total_pages from the response and request the remaining pages.

curl "https://api.osyro.com/listings?page=1&page_size=50" \
  -H "X-API-Key: $OSYRO_API_KEY"
200 · Response
{
  "page": 1,
  "page_size": 50,
  "total": 120,
  "total_pages": 3,
  "listings": [
    { "referenceNumber": "osy-demo-100231", "...": "full listing object" }
  ]
}

Authentication

Every request must include your API key in the X-API-Key header. Requests without a valid key are rejected.

Keys are issued by the Osyro team and are tied to your organisation's account.

Keep the key on your server. Never ship it in a browser or mobile app, and never commit it to source control. If you think a key has been exposed, contact Osyro to rotate it.

HTTP
GET /listings HTTP/1.1
Host: api.osyro.com
X-API-Key: {YOUR_API_KEY}
Accept: application/json

Endpoints

Two read-only endpoints. Both return listing objects with exactly the same fields.

GET/listings

List listings

Returns active listings, page by page, as full listing objects. Use this endpoint for both full and incremental sync. Pages are numbered from 1; keep requesting until you have read total_pages.

Query parameters

ParameterTypeDefaultDescription
pageinteger1Page number, starting at 1.
page_sizeinteger50Listings per page. Maximum 200. Larger pages mean fewer requests.
updated_sinceISO 8601noneOnly listings changed at or after this time (inclusive). Use the start time of your last successful sync.

Response

The list response wraps listing objects with pagination metadata.

FieldTypeDescription
pageintegerThe page you requested.
page_sizeintegerListings per page used for this response.
totalintegerNumber of listings matching your query, across all pages.
total_pagesintegerNumber of pages. Request pages 1 through total_pages.
listingsobject[]Array of listing objects for this page.
curl "https://api.osyro.com/listings?page=2&page_size=200&updated_since=2026-10-01T00:00:00Z" \
  -H "X-API-Key: $OSYRO_API_KEY"
200 · Response
{
  "page": 1,
  "page_size": 50,
  "total": 120,
  "total_pages": 3,
  "listings": [
    { "referenceNumber": "osy-demo-100231", "...": "full listing object" }
  ]
}
GET/listings/{referenceNumber}

Retrieve a listing

Returns a single listing object by its reference number. Not needed for sync, but useful for spot checks, support cases and debugging a specific listing.

Path parameter

ParameterTypeDescription
referenceNumberstringThe listing's referenceNumber.

The listing object →

curl "https://api.osyro.com/listings/osy-demo-100231" \
  -H "X-API-Key: $OSYRO_API_KEY"

Keeping in sync

The feed contains only active listings. Your job is to keep your copy identical to it: add what is new, update what changed, and unpublish what disappeared.

Full sync

Page through /listings without updated_since. The result is every active listing.

Incremental sync

Pass the start time of your last successful sync as updated_since to fetch only changes.

Detect changes

A new referenceNumber is a new listing. A newer lastUpdated means the listing changed.

Sold, rented or withdrawn listings are dropped from the feed rather than marked. Any referenceNumber missing from a full sync should be unpublished on your side.

Recipe: full sync

Run a full sync on first connection and then periodically (for example nightly) to catch removals.

# Full sync: fetch every page, then unpublish anything not seen.
page=1
while :; do
  body=$(curl -s "https://api.osyro.com/listings?page=$page&page_size=200" \
    -H "X-API-Key: $OSYRO_API_KEY")
  echo "$body" | jq -c '.listings[]' >> listings.ndjson
  total_pages=$(echo "$body" | jq '.total_pages')
  [ "$page" -ge "$total_pages" ] && break
  page=$((page + 1))
done

Recipe: incremental sync

Run incremental syncs frequently (for example every 15 minutes) to pick up new and changed listings quickly.

# Incremental sync: only listings changed since your last successful run.
curl "https://api.osyro.com/listings?page=1&page_size=200&updated_since=$LAST_SYNC_START" \
  -H "X-API-Key: $OSYRO_API_KEY"
  • Save the time your sync started, not when it finished, and use it as the next updated_since. Because updated_since is inclusive, nothing falls through the gap.

  • Upsert by referenceNumber, so re-processing a listing is always safe.

  • Incremental sync does not tell you about removals. Pair it with a regular full sync.

  • Use page_size=200 for syncs to minimise requests.

The listing object

Every listing has the same shape in both endpoints. Field names are case-sensitive; enum values are exact text and matched case-insensitively.

Always presentPresent when applicablePresent when available

Identity

FieldValues and notes
referenceNumberstring
Always present

Unique, stable ID. Never changes and is never reused. Use it as your primary key.

lastUpdatedISO 8601
Always present

Changes whenever any field changes, including price and images.

Classification

FieldValues and notes
categoryenum
Always present

Property category.

  • Residential
  • Commercial
purposeenum
Always present

Whether the listing is for sale or for rent.

  • For Sale
  • For Rent
frequencyenum
Present when applicable

Rentals only. The period that price refers to.

  • Monthly
  • Weekly
  • Daily
completionStatusenum
Present when available

Construction status.

  • Completed
  • Off Plan
unitTypestring
Always present

Type of unit. The list is open-ended; handle unknown values gracefully.

  • Apartment
  • Villa
  • Townhouse
  • Office
  • Shop
  • Warehouse
  • Land
  • …

Content

FieldValues and notes
titlestring
Always present

Listing headline.

descriptionstring
Always present

Full description. May contain HTML: sanitise before rendering.

imagesstring[]
Always present

Ordered HTTPS URLs; the first is the cover. At least 1024 px on the long edge.

Size & layout

FieldValues and notes
unitBuiltupAreanumber
Always present

Built-up area, in unitMeasure.

unitMeasureenum
Present when available

Defaults to sqm when absent.

  • sqm
  • sqft
bedroomsnumber | string
Present when applicable

Residential only. Studios are 0 or "Studio".

bathroomsnumber
Present when available

Bathroom count.

furnishingenum
Present when available

Furnishing level.

  • Fully furnished
  • Semi-furnished
  • Unfurnished
facilitiesstring[]
Present when available

Amenity names, e.g. Balcony, Shared Pool, Security.

Price

FieldValues and notes
pricenumber
Always present

In QAR. Sale price, or rent per frequency.

Location

FieldValues and notes
citystring
Always present

e.g. Doha, Lusail, Al Wakrah. Uses the names accepted by major Qatar portals.

areastring
Always present

Most specific community or tower, e.g. Porto Arabia, West Bay.

latitudenumber
Always present

WGS84 decimal degrees.

longitudenumber
Always present

WGS84 decimal degrees.

Rental terms

FieldValues and notes
rentalPeriodenum
Present when available

Contract length.

  • Less than 1 year
  • Annual contract
  • More than 1 year
depositenum
Present when available

Security deposit.

  • 1 Month
  • 2 Months
  • Negotiable
  • None
commissionenum
Present when available

Agency commission.

  • 1 Month
  • 1/2 Month
  • Negotiable
  • None
  • Percentage
  • Cash
  • Other
  • Not applicable
freeMonthsenum
Present when available

Rent-free months offered.

  • 0
  • 1
  • 2
  • 3
  • 4

Unit features

FieldValues and notes
kitchenenum
Present when available

Kitchen type.

  • Closed
  • Open
  • Fitted
  • Unfitted
  • Kitchenette
  • None
parkingenum
Present when available

Parking type.

  • Covered
  • Uncovered
  • Underground
  • Dedicated - Covered
  • Dedicated - Uncovered
  • Dedicated - Underground
  • On street parking
acTypeenum
Present when available

Air conditioning.

  • Central
  • Split
  • Windows
  • None
kahramaaboolean
Present when available

Electricity and water (Kahramaa) included in rent.

internetboolean
Present when available

Internet included in rent.

Agent

FieldValues and notes
agent.namestring
Always present

Full name.

agent.phonestring
Always present

International format, +974XXXXXXXX.

agent.emailstring
Always present

Email address.

agent.photostring
Present when available

Profile photo URL.

Example listing

A complete residential rental. The data is illustrative.

listing.json
{
  "referenceNumber": "osy-demo-100231",
  "lastUpdated": "2026-09-22T07:47:10Z",
  "category": "Residential",
  "purpose": "For Rent",
  "frequency": "Monthly",
  "rentalPeriod": "Annual contract",
  "deposit": "1 Month",
  "commission": "1 Month",
  "freeMonths": 1,
  "completionStatus": "Completed",
  "unitType": "Apartment",
  "title": "Elegant 1BR Apartment, Porto Arabia",
  "description": "<p>Spacious 1BHK with sea view</p>",
  "unitBuiltupArea": 95,
  "unitMeasure": "sqm",
  "bedrooms": 1,
  "bathrooms": 2,
  "furnishing": "Fully furnished",
  "facilities": ["Balcony", "Shared Pool", "Security"],
  "kitchen": "Fitted",
  "parking": "Covered",
  "acType": "Central",
  "kahramaa": true,
  "internet": true,
  "price": 7500,
  "city": "Doha",
  "area": "Porto Arabia",
  "latitude": 25.3725,
  "longitude": 51.541,
  "images": [
    "https://cdn.example.com/100231/1.jpg",
    "https://cdn.example.com/100231/2.jpg"
  ],
  "agent": {
    "name": "Jane Doe",
    "phone": "+97455500000",
    "email": "jane.doe@example.com",
    "photo": "https://cdn.example.com/agents/1.jpg"
  }
}

Errors

Failed requests return an HTTP error status and a JSON body with an application code and a message. Branch on Code; the message is for humans and may change.

CodeDescriptionWhat to do
201The API key is missing or invalid.Check the X-API-Key header. Don't retry until it is fixed.
202The key is valid but cannot access the feed.Contact Osyro to check your account. Don't retry.
203Temporary server error.Retry with exponential backoff. Contact Osyro if it persists.
4xx / 5xx · Response
{
  "Code": 201,
  "Message": "Invalid access token"
}

Retrying safely

Every endpoint is a read, so retrying is always safe. Retry only server errors, with growing delays.

Log the Code and the time of the request when you contact support. Never include your API key.

# Retry server errors (code 203) with backoff: 1s, 2s, 4s, 8s, 16s
for delay in 1 2 4 8 16; do
  status=$(curl -s -o page.json -w "%{http_code}" \
    "https://api.osyro.com/listings?page=1" -H "X-API-Key: $OSYRO_API_KEY")
  [ "$status" -lt 500 ] && break
  sleep "$delay"
done

Feed guarantees

  • A full sync returns every active listing. Nothing is omitted for performance.

  • referenceNumber is stable and never reused.

  • lastUpdated changes on any edit, including price and images.

  • updated_since is inclusive: at or after the given time.

  • Image URLs are public, stable HTTPS. The first image is the cover.

  • Prices are in QAR. Rentals are quoted per frequency.

  • Agent phone numbers use the +974XXXXXXXX format.

  • All timestamps are UTC.

Need a key or help?

API keys, technical questions and integration support come directly from the Osyro team.

Contact the Osyro team