API documentation
Read market reports, occupancy and revenue estimates, daily trends, collected listings and revenue projections for the Gulf and Egypt as JSON. Use it from spreadsheets, scripts, your own software, or AI assistants through the MCP server.
Authentication
Sign in, open Data & API, and create a key. Your plan sets how many active keys the account can hold (5 during the free trial). A key starts with ss_, is shown once, and is stored only as a hash, so copy it when you create it. Revoke a key there at any time; requests with it fail immediately.
Send the key in the Authorization header of every request. All responses are JSON over HTTPS.
Base URL: https://staytrend.ai
Authorization: Bearer ss_0123456789abcdef0123456789abcdef01234567Rate limits
Limits are per account, and all of its keys share them. During the free trial the account may make 120 requests per minute and 25,000 per UTC day; each plan sets its own, listed on API pricing. Requests over the limit return 429 with a Retry-After header in seconds. After the trial without a plan, requests return 402 PLAN_REQUIRED until a plan is chosen. The Data & API page shows the account’s limits and each key’s usage today, this minute and when it was last used.
Errors
Errors use standard HTTP status codes and a JSON body with a stable code, a readable message and a requestId (also sent as X-Request-ID).
{
"error": {
"code": "RATE_LIMIT",
"message": "Limit of 120 requests per minute reached."
},
"requestId": "6f1c2a9e-3b0d-4c55-9a51-2f7e0f0f4b11"
}| Status | Codes | Meaning |
|---|---|---|
400 | INVALID_INPUT, INVALID_AREA, INVALID_JSON, INVALID_METRIC, INVALID_SOURCE | A parameter is missing or out of range. The message says which. |
401 | API_KEY_REQUIRED, API_KEY_INVALID | No Authorization header, or the key is wrong or revoked. |
402 | PLAN_REQUIRED | The key is valid, but the account’s free trial has ended without a plan. Choose a plan and the key works again. |
404 | NOT_FOUND | Unknown market or route. |
429 | RATE_LIMIT, DAILY_LIMIT | Per-minute or daily limit reached. Wait for Retry-After seconds. |
500 | INTERNAL_ERROR | Something failed on our side. Quote the requestId if you contact us. |
503 | STORAGE_UNAVAILABLE | Storage is temporarily unavailable. Retry later. |
Pagination
Only List listings is paginated. It returns nextCursor; send it back as cursor until it is null. Pages are ordered by listingKey, so a cursor stays valid while new listings are collected.
Property types
Listings, metrics and estimates use these type ids.
1 Resort, 2 Chalet, 3 Rest house, 4 Farm, 5 Camp, 6 Apartment, 7 Villa, 8 Room, 9 Studio, 11 Serviced apartment
List markets
GET/api/v1/markets
Every destination with at least 20 collected listings, sorted by listing count. tracker is null until a destination has tracked calendars; its occupancy, ADR and revenue are meaningful once maturity is "estimating".
No parameters.
Example request
curl -H "Authorization: Bearer $JOKERPRICE_API_KEY" \
"https://staytrend.ai/api/v1/markets"Example response
{
"markets": [
{
"slug": "riyadh",
"name": "Riyadh",
"latitude": 24.7136,
"longitude": 46.6753,
"listings": 9412,
"sources": {
"gathern": 7650,
"airbnb": 1762
},
"nightly": {
"median": 420,
"p25": 300,
"p75": 650
},
"tracker": {
"maturity": "estimating",
"trackingDays": 41,
"tracked": 812,
"occupancy": 0.47,
"adr": 512.4,
"revpar": 240.8,
"monthlyRevenuePerListing": 7225,
"onTheBooks": 0.18
}
},
{
"slug": "jeddah",
"name": "Jeddah",
"latitude": 21.4858,
"longitude": 39.1925,
"listings": 6120,
"sources": {
"gathern": 5011,
"airbnb": 1109
},
"nightly": {
"median": 450,
"p25": 320,
"p75": 700
},
"tracker": null
}
]
}Get a market report
GET/api/v1/markets/{slug}
The full report behind a public /market-data page: supply by source, asking-rate quartiles, median asking rate by bedroom count (groups of 5 or more), property mix, average rating (0–10) and, when tracked, 90-day occupancy, ADR, RevPAR, revenue per listing and weekday, monthly and lead-time breakdowns.
Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
slug | path | Yes | Lowercase market slug from List markets, e.g. riyadh, dubai, cairo. |
Example request
curl -H "Authorization: Bearer $JOKERPRICE_API_KEY" \
"https://staytrend.ai/api/v1/markets/riyadh"Example response
{
"market": {
"slug": "riyadh",
"name": "Riyadh",
"latitude": 24.7136,
"longitude": 46.6753,
"listings": 9412,
"sources": {
"gathern": 7650,
"airbnb": 1762
},
"nightly": {
"median": 420,
"p25": 300,
"p75": 650
},
"bedrooms": [
{
"bedrooms": 1,
"listings": 2210,
"median": 300
},
{
"bedrooms": 2,
"listings": 3105,
"median": 420
},
{
"bedrooms": 3,
"listings": 1980,
"median": 600
}
],
"types": [
{
"type": "Apartment",
"count": 5120
},
{
"type": "Chalet",
"count": 1870
},
{
"type": "Villa",
"count": 990
}
],
"rating": 8.9,
"tracker": {
"maturity": "estimating",
"trackingDays": 41,
"tracked": 812,
"occupancy": 0.47,
"adr": 512.4,
"revpar": 240.8,
"monthlyRevenuePerListing": 7225,
"onTheBooks": 0.18,
"observedNights": 30122,
"byMonth": [
{
"month": "2026-09",
"period": "observed",
"bookedNights": 11920,
"nights": 24800,
"occupancy": 0.48,
"adr": 505
}
],
"byWeekday": [
{
"weekday": 4,
"bookedNights": 2410,
"nights": 3900,
"occupancy": 0.62,
"adr": 560
}
],
"leadTimes": [
{
"label": "0–1 days",
"nights": 3120
},
{
"label": "2–7 days",
"nights": 4800
}
]
}
}
}Get performance metrics
GET/api/v1/metrics
Occupancy, ADR and revenue from daily calendar tracking for a city or a map rectangle. past covers the nights before today; forward is the share of upcoming nights already booked. series has one row per night. maturity stays "collecting" until there are 7 days of history and 100 observed listing-nights.
Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
city | query | No | Market slug or name. Pass city, or all four of north, south, east and west. |
north, south, east, west | query | No | Rectangle in decimal degrees, up to 5 degrees across. |
past_days | query | No | Nights to look back, 1–365. Default 30. |
forward_days | query | No | Nights to look ahead, 1–365. Default 60. |
Example request
curl -H "Authorization: Bearer $JOKERPRICE_API_KEY" \
"https://staytrend.ai/api/v1/metrics?city=riyadh&past_days=30&forward_days=60"
# Or a map rectangle
curl -H "Authorization: Bearer $JOKERPRICE_API_KEY" \
"https://staytrend.ai/api/v1/metrics?north=24.85&south=24.70&east=46.80&west=46.60"Example response
{
"metrics": {
"source": "all",
"today": "2026-10-28",
"from": "2026-09-28",
"to": "2026-12-27",
"trackingDays": 41,
"maturity": "estimating",
"listings": {
"tracked": 812,
"checked": 806,
"medianAskingNightly": 430,
"bedrooms": [
{
"value": "2",
"count": 301
},
{
"value": "1",
"count": 244
}
],
"types": [
{
"value": "6",
"count": 520
},
{
"value": "2",
"count": 160
}
]
},
"past": {
"nights": 24180,
"bookedNights": 11365,
"occupancy": 0.47,
"adr": 512.4,
"revpar": 240.8,
"monthlyRevenuePerListing": 7225
},
"forward": {
"nights": 48360,
"bookedNights": 8705,
"onTheBooks": 0.18
},
"byWeekday": [
{
"weekday": 0,
"bookedNights": 1450,
"nights": 3460,
"occupancy": 0.42,
"adr": 480
},
{
"weekday": 4,
"bookedNights": 2410,
"nights": 3460,
"occupancy": 0.7,
"adr": 560
}
],
"byMonth": [
{
"month": "2026-10",
"period": "mixed",
"bookedNights": 10220,
"nights": 21900,
"occupancy": 0.47,
"adr": 510
}
],
"leadTimes": [
{
"label": "0–1 days",
"nights": 3120
},
{
"label": "2–7 days",
"nights": 4800
},
{
"label": "8–14 days",
"nights": 1900
}
],
"series": [
{
"night": "2026-10-27",
"past": true,
"booked": 401,
"eligible": 806,
"occupancy": 0.4975,
"adr": 498.2
}
]
}
}Get daily trends
GET/api/v1/trends
One stored daily snapshot per destination, as a time series for one metric. Occupancy, ADR and RevPAR in each snapshot cover the 30 nights before that day; on_the_books covers the 30 nights after it. changes compares the latest value with 7, 30 and 90 days earlier once that much history exists; yearOverYear stays null until a full year is stored.
Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
city | query | Yes | Market slug or name. |
metric | query | No | occupancy (default), adr, revpar, asking, listings, tracked or on_the_books. |
source | query | No | all (default), gathern or airbnb. |
days | query | No | Days of history to return, 7–1100. Default 365. |
Example request
curl -H "Authorization: Bearer $JOKERPRICE_API_KEY" \
"https://staytrend.ai/api/v1/trends?city=riyadh&metric=occupancy&days=90"Example response
{
"trends": {
"metric": "occupancy",
"label": "Occupancy",
"unit": "share",
"source": "all",
"days": 365,
"windowNights": 30,
"today": "2026-10-28",
"destination": {
"slug": "riyadh",
"name": "Riyadh"
},
"sources": [
"all",
"gathern",
"airbnb"
],
"points": [
{
"day": "2026-10-27",
"value": 0.46,
"origin": "daily",
"observedNights": 23950
},
{
"day": "2026-10-28",
"value": 0.47,
"origin": "daily",
"observedNights": 24180
}
],
"changes": [],
"yearOverYear": null,
"supply": {
"latest": {
"day": "2026-10-28",
"value": 9412,
"tracked": 812
},
"first": {
"day": "2026-10-27",
"value": 9390,
"tracked": 806
},
"changes": [],
"points": [
{
"day": "2026-10-28",
"value": 9412,
"tracked": 812
}
]
},
"history": {
"days": 2,
"firstDay": "2026-10-27",
"dailySince": "2026-10-27",
"backfilledDays": 0
},
"notice": "Only 2 days of history so far (since 2026-10-27). JokerPrice stores one snapshot a day, so trends firm up over the coming weeks; year-over-year appears after a full year."
}
}Get market pace
GET/api/v1/pace
Booking pace from daily snapshots. For each stay date in the next 30 days: onTheBooks is the booked share of tracked nights at the latest snapshot, and pickup7 and pickup14 are how much that share rose over the previous 7 and 14 days. curve averages the booked share by days before arrival. status stays "collecting" until 7 days of snapshots exist.
Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
city | query | Yes | Market slug or name. |
Example request
curl -H "Authorization: Bearer $JOKERPRICE_API_KEY" \
"https://staytrend.ai/api/v1/pace?city=dubai"Example response
{
"pace": {
"destination": "dubai",
"today": "2026-11-20",
"snapshotDays": 21,
"minSnapshotDays": 7,
"basis": "tracked public calendars (estimate)",
"status": "ready",
"latestSnapshot": "2026-11-20",
"stayDates": [
{
"stayDate": "2026-11-27",
"onTheBooks": 0.41,
"eligible": 1180,
"pickup7": 0.09,
"pickup14": 0.15
}
],
"curve": [
{
"lead": 0,
"onTheBooks": 0.52,
"stayDates": 14
},
{
"lead": 7,
"onTheBooks": 0.4,
"stayDates": 14
}
]
}
}List events and holidays
GET/api/v1/events
The sourced calendar of local holidays and events used by event pricing rules. Each event has English and Arabic names, a kind, the cities it covers (all-sa for all of Saudi Arabia), inclusive start and end dates, a certainty and its source. "expected" dates (for example Hijri dates before the moon-sighting announcement) can move.
Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
city | query | No | Market slug or name; omit for every event. |
from, to | query | No | Date range, YYYY-MM-DD. Events overlapping the range are returned. |
kind | query | No | Comma-separated kinds: religious, national, school, season, sports, culture, business. |
Example request
curl -H "Authorization: Bearer $JOKERPRICE_API_KEY" \
"https://staytrend.ai/api/v1/events?city=riyadh&from=2026-11-01&to=2027-03-31"Example response
{
"version": "2026-10-28.2",
"lastReviewed": "2026-10-28",
"coverage": {
"start": "2026-01-01",
"end": "2027-12-31"
},
"certainty": {
"official": "Published by the authority or organiser itself on its own site.",
"expected": "Computed (for example from the Umm al-Qura calendar), not fully announced, or reported only in the news; may move."
},
"count": 1,
"events": [
{
"id": "saudi-national-day-2026",
"name": {
"en": "Saudi National Day",
"ar": "اليوم الوطني السعودي"
},
"kind": "national",
"cities": [
"all-sa"
],
"start": "2026-09-23",
"end": "2026-09-23",
"certainty": "official",
"sourceUrl": "https://www.my.gov.sa/",
"verifiedAt": "2026-10-28"
}
]
}List listings
GET/api/v1/listings
Collected listings for a destination, ordered by listingKey. source is gathern, airbnb or agoda. nightly is the last asking rate seen in SAR before fees; rating is out of 10 (Airbnb ratings are doubled). Keep requesting with cursor=nextCursor until nextCursor is null.
Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
city | query | Yes | Market slug or name. |
limit | query | No | Listings per page, 1–500. Default 100. |
cursor | query | No | nextCursor from the previous page. |
Example request
curl -H "Authorization: Bearer $JOKERPRICE_API_KEY" \
"https://staytrend.ai/api/v1/listings?city=riyadh&limit=100"Example response
{
"listings": [
{
"id": "48213",
"source": "gathern",
"sourceListingId": "48213",
"listingKey": "gathern:48213",
"title": "Modern 2BR in Al Malqa",
"city": "Riyadh",
"district": "Al Malqa",
"typeId": "6",
"latitude": 24.8094,
"longitude": 46.6112,
"bedrooms": 2,
"area": 120,
"nightly": 420,
"rating": 9.4,
"reviews": 87,
"url": "https://gathern.co/view/1234/unit/48213",
"updatedAt": "2026-10-27T21:14:03.000Z"
}
],
"nextCursor": "gathern:48213"
}Estimate revenue
POST/api/v1/estimate
The revenue calculator. Comparables are stored listings within the radius that match bedrooms and type. ADR uses booked-night prices from tracked calendars nearby (or within 20 km) once mature, otherwise the comparables' median asking rate. Without measured occupancy, projection is null and scenarios holds 40%, 55% and 70% occupancy cases. Add an investment object to get cash flow, cap rate and payback for the same projection.
Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
latitude, longitude | body | Yes | Property location in decimal degrees. |
radiusKm | body | No | Comparable radius, 0.5–20 km. Default 3. |
bedrooms | body | No | Exact bedroom count 1–6, or 0 for any. Default 0. |
typeId | body | No | Property type id (see below) or "all". Default "all". |
investment | body | No | Optional. purchasePrice, downPaymentPct (default 25), loanRatePct (6), loanTermYears (25), setupCost, monthlyFixedCosts, platformFeePct (15), cleaningPerStay, avgStayNights (2), managementFeePct. Percentages are whole numbers (6 = 6%). |
comps | body | No | Optional. 3–50 key values from candidates. The estimate then uses only those listings: asking prices, and booked-night ADR and occupancy from their own tracked calendars once they reach 7 days and 100 observed nights, with no wider fallback. Keys that are no longer among the candidates are left out and counted in selection.ignored. |
Property types
1 Resort, 2 Chalet, 3 Rest house, 4 Farm, 5 Camp, 6 Apartment, 7 Villa, 8 Room, 9 Studio, 11 Serviced apartment
Example request
curl -X POST -H "Authorization: Bearer $JOKERPRICE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"latitude":24.7743,"longitude":46.7386,"radiusKm":3,"bedrooms":3,"typeId":"7"}' \
"https://staytrend.ai/api/v1/estimate"Example response
{
"estimate": {
"input": {
"latitude": 24.7743,
"longitude": 46.7386,
"radiusKm": 3,
"bedrooms": 3,
"typeId": "7"
},
"asking": {
"count": 64,
"median": 900,
"p25": 700,
"p75": 1200
},
"adr": {
"value": 860,
"basis": "booked nights nearby"
},
"occupancy": {
"value": 0.44,
"basis": "tracked calendars nearby",
"trackingDays": 41,
"trackedNearby": 58
},
"projection": {
"occupancy": 0.44,
"adr": 860,
"monthly": 11352,
"annual": 138116
},
"range": {
"low": {
"occupancy": 0.44,
"adr": 700,
"monthly": 9240,
"annual": 112420
},
"high": {
"occupancy": 0.44,
"adr": 1200,
"monthly": 15840,
"annual": 192720
}
},
"scenarios": [],
"seasonality": [
{
"month": "2026-09",
"period": "observed",
"occupancy": 0.41,
"adr": 840,
"revenue": 10332
}
],
"comparables": [
{
"id": "50991",
"source": "gathern",
"title": "Villa with pool",
"nightly": 950,
"bedrooms": 3,
"typeId": "7",
"rating": 9.1,
"url": "https://gathern.co/view/2211/unit/50991",
"latitude": 24.771,
"longitude": 46.741,
"distanceKm": 0.43
}
],
"selection": {
"mode": "all",
"status": "ok",
"inRadius": 64,
"candidates": 50,
"kept": 50,
"requested": 0,
"ignored": 0,
"minimum": 3,
"maximum": 50
},
"candidates": [
{
"key": "gathern:50991",
"ref": "3f9c2a7e01b4",
"kept": true,
"source": "gathern",
"title": "Villa with pool",
"url": "https://gathern.co/view/2211/unit/50991",
"nightly": 950,
"checkIn": "2026-10-29",
"bedrooms": 3,
"typeId": "7",
"rating": {
"value": 9.1,
"scale": 10
},
"reviews": 42,
"distanceKm": 0.43,
"position": "within",
"tracked": {
"since": "2026-09-14T06:00:00.000Z",
"observedNights": 44,
"bookedNights": 19,
"minNights": 30,
"occupancy": 0.4318,
"adr": 905
}
}
]
}
}With investment inputs
{
"latitude": 24.7743,
"longitude": 46.7386,
"bedrooms": 3,
"typeId": "7",
"investment": {
"purchasePrice": 1500000,
"downPaymentPct": 30,
"loanRatePct": 6,
"loanTermYears": 25,
"monthlyFixedCosts": 900,
"platformFeePct": 15,
"cleaningPerStay": 120,
"avgStayNights": 2
}
}The response gains estimate.investment:
{
"investment": {
"inputs": {
"purchasePrice": 1500000,
"downPaymentPct": 30,
"loanRatePct": 6,
"loanTermYears": 25,
"monthlyFixedCosts": 900,
"platformFeePct": 15,
"cleaningPerStay": 120,
"avgStayNights": 2,
"setupCost": 0,
"managementFeePct": 0
},
"basis": "projection",
"projection": {
"occupancy": 0.44,
"adr": 860,
"revenue": 138116,
"expenses": {
"platformFees": 20717,
"management": 0,
"cleaning": 9636,
"fixed": 10800
},
"operatingExpenses": 41153,
"netOperatingIncome": 96963,
"capRate": 0.0646,
"loanAmount": 1050000,
"annualDebtService": 81181,
"cashInvested": 450000,
"annualCashFlow": 15782,
"monthlyCashFlow": 1315,
"cashOnCash": 0.0351,
"paybackYears": 28.5,
"breakEvenOccupancy": 0.3756
},
"scenarios": []
}
}MCP endpoint
AI assistants can read the same data through the hosted MCP server at https://staytrend.ai/mcp: JSON-RPC 2.0 over HTTP POST (the MCP Streamable HTTP transport, with JSON responses), authenticated with the same key. Each tool call counts as one request against the limits above; initialize, tools/list and ping do not. See MCP setup and the tools.
POST https://staytrend.ai/mcp
Authorization: Bearer ss_0123456789abcdef0123456789abcdef01234567
Content-Type: application/json
Accept: application/json, text/event-stream
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_market","arguments":{"slug":"riyadh"}}}About the data
- Listings come from public searches on Airbnb, Gathern and Agoda. Amounts are SAR;
nightlyis the asking rate before cleaning and service fees. - Occupancy is estimated from public availability calendars checked daily: a night that closes between two checks counts as booked; long or far-ahead closures count as owner blocks. ADR is the asking price of booked nights, not the achieved rate.
- Revenue per listing per month is ADR × occupancy × 30; RevPAR is ADR × occupancy.
- Market reports are cached for up to 30 minutes.
- JokerPrice is independent and not affiliated with Airbnb, Gathern or Agoda. Figures are estimates, not guarantees.