Skip to content

Instantly share code, notes, and snippets.

@jaeseokan94
Last active May 14, 2026 03:55
Show Gist options
  • Select an option

  • Save jaeseokan94/b36181a1bbf692f91e4f53b41ed02b16 to your computer and use it in GitHub Desktop.

Select an option

Save jaeseokan94/b36181a1bbf692f91e4f53b41ed02b16 to your computer and use it in GitHub Desktop.
Airbtics - Airbnb API - V3.0.3 OpenAPI.yaml
openapi: 3.1.0
info:
title: Airbtics Public API
description: |
HTTP surface for the Public API behind API Gateway. All operations require a valid API key.
**Authoritative behavior** is implemented in the Python handlers under `apps/public_api/`
(AWS Lambda Powertools `APIGatewayRestResolver`). This document is for clients and tooling;
when in doubt, refer to the handler and helper modules and markdown readmes in this folder.
**Market resolution** (metrics and future pacing): supply `market_id`, or both `search_latitude`
and `search_longitude` (floats). Invalid or missing combinations return 400 where noted.
**Agent/tooling note**: call concrete paths (for example `/markets/search`) rather than
the API root. The API root often returns `403` because no root route is exposed and
all operations require `x-api-key`.
**Quick example**:
`curl -X GET "https://crap0y5bx5.execute-api.us-east-2.amazonaws.com/prod/markets/search?query=london" -H "x-api-key: <your_api_key>" -H "Accept: application/json"`
**Market metrics filters**: **Preferred** — `POST /markets/summary` and
`POST /markets/metrics/{proxy}` with JSON body field `filters` (object).
**Deprecated** — same paths as `GET` with `filters` as a **URL-encoded JSON** query
param; see `apps/public_api/markets/readme.md` for allowed keys.
version: 1.0.0
servers:
- url: https://crap0y5bx5.execute-api.us-east-2.amazonaws.com/prod
description: Production API Gateway base URL.
security:
- ApiKeyAuth: []
tags:
- name: Markets
description: Market search, lookup, metadata, and metrics
- name: Listings
description: Listing search and listing-level metrics
- name: Reports
description: Create and fetch reports
- name: Search
description: Search ranking utilities
- name: Live
description: Live listing snapshot
paths:
/markets/search:
get:
tags: [Markets]
operationId: getMarketsSearch
summary: Autocomplete market search
description: Substring search; up to 50 results. Optional `country_code` for tie-breaking.
parameters:
- name: query
in: query
required: true
schema:
type: string
- name: country_code
in: query
required: false
schema:
type: string
description: ISO-style country code hint (e.g. GB)
responses:
"200":
description: Search results
content:
application/json:
schema:
$ref: "#/components/schemas/MarketSearchResponse"
"400":
description: Missing or invalid query
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/markets/country:
get:
tags: [Markets]
operationId: getMarketsCountry
summary: List markets by country (paginated)
description: >
Returns up to 10 markets per page for a given ISO-style country code, using the same
cached S3 list as `/markets/search`. Stable sort by market id then name.
parameters:
- name: country_code
in: query
required: true
schema:
type: string
minLength: 2
maxLength: 2
pattern: "^[A-Za-z]{2}$"
description: Two-letter country code (e.g. GB, US)
- name: page
in: query
required: false
schema:
type: integer
minimum: 1
default: 1
responses:
"200":
description: Paginated markets for the country
content:
application/json:
schema:
$ref: "#/components/schemas/MarketsByCountryResponse"
"400":
description: Missing or invalid country_code or page
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/markets/lookup:
get:
tags: [Markets]
operationId: getMarketsLookup
summary: Resolve nearest market from coordinates
description: Returns the nearest market among verified tiers T1–T3, or 404 if none.
parameters:
- name: latitude
in: query
required: true
schema:
type: string
description: Latitude (string accepted by handler; numeric)
- name: longitude
in: query
required: true
schema:
type: string
responses:
"200":
description: Nearest market
content:
application/json:
schema:
$ref: "#/components/schemas/MessageObjectResponse"
"400":
description: Missing coordinates
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"404":
description: No eligible market
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/markets/metadata:
get:
tags: [Markets]
operationId: getMarketsMetadata
summary: Market metadata and public submarkets
description: >
Parent market row plus submarkets. Parent and each submarket must be `visibility=public`
and pass hidden-market rules; otherwise 404.
parameters:
- name: market_id
in: query
required: true
schema:
type: integer
minimum: 1
responses:
"200":
description: Metadata payload
content:
application/json:
schema:
$ref: "#/components/schemas/MarketMetadataResponse"
"400":
description: Missing or invalid market_id
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"404":
description: Market not found or not eligible
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/markets/summary:
post:
tags: [Markets]
operationId: postMarketsSummary
summary: Market summary KPIs (preferred)
description: >
Same response as GET. JSON body: `market_id` or `search_latitude` +
`search_longitude`, optional `filters` object. Do not send top-level
`bedrooms` — use `filters.bedrooms`. Filter surcharge uses non-empty
`filters` in the body (see `authorizePublicApiUser`).
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/MarketMetricsPostRequest"
responses:
"200":
description: Summary, metadata, optional market_info when resolved by coordinates
content:
application/json:
schema:
$ref: "#/components/schemas/MarketMetricsEnvelope"
"400":
description: Missing market, invalid body, invalid filters, etc.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
get:
tags: [Markets]
operationId: getMarketsSummaryDeprecated
summary: Market summary KPIs (deprecated — use POST)
deprecated: true
parameters:
- $ref: "#/components/parameters/MarketIdQuery"
- $ref: "#/components/parameters/SearchLatitudeQuery"
- $ref: "#/components/parameters/SearchLongitudeQuery"
- name: bedrooms
in: query
required: false
schema:
type: string
description: Legacy single-select; superseded by `filters` when present.
- $ref: "#/components/parameters/FiltersQueryJson"
responses:
"200":
description: Summary, metadata, optional market_info when resolved by coordinates
content:
application/json:
schema:
$ref: "#/components/schemas/MarketMetricsEnvelope"
"400":
description: Missing market, invalid filters, etc.
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/markets/metrics/{proxy}:
post:
tags: [Markets]
operationId: postMarketsMetricsByProxy
summary: Market time-series metrics (preferred)
description: >
Same response as GET. JSON body includes `proxy` path segment implicitly;
body fields `market_id` or coordinates, optional `number_of_months` (1–36,
default 12), optional `filters` object. Do not send top-level `bedrooms`.
Filter surcharge uses non-empty `filters` in the body.
parameters:
- name: proxy
in: path
required: true
schema:
type: string
enum:
- all
- occupancy
- average-daily-rate
- revenue
- active-listings
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/MarketMetricsPostRequest"
responses:
"200":
description: Metrics payload
content:
application/json:
schema:
$ref: "#/components/schemas/MarketMetricsEnvelope"
"400":
description: Invalid metric, months, filters, or missing market/coordinates
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
get:
tags: [Markets]
operationId: getMarketsMetricsByProxyDeprecated
summary: Market time-series metrics (deprecated — use POST)
deprecated: true
description: Monthly metrics for the requested slice (`proxy`).
parameters:
- name: proxy
in: path
required: true
schema:
type: string
enum:
- all
- occupancy
- average-daily-rate
- revenue
- active-listings
- $ref: "#/components/parameters/MarketIdQuery"
- $ref: "#/components/parameters/SearchLatitudeQuery"
- $ref: "#/components/parameters/SearchLongitudeQuery"
- name: number_of_months
in: query
required: false
schema:
type: integer
minimum: 1
maximum: 36
default: 12
- name: bedrooms
in: query
required: false
schema:
type: string
- $ref: "#/components/parameters/FiltersQueryJson"
responses:
"200":
description: Metrics payload
content:
application/json:
schema:
$ref: "#/components/schemas/MarketMetricsEnvelope"
"400":
description: Invalid metric, months, filters, or missing market/coordinates
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/markets/metrics/future/pacing:
get:
tags: [Markets]
operationId: getMarketsMetricsFuturePacing
summary: Future pacing metrics for a market
parameters:
- $ref: "#/components/parameters/MarketIdQuery"
- $ref: "#/components/parameters/SearchLatitudeQuery"
- $ref: "#/components/parameters/SearchLongitudeQuery"
responses:
"200":
description: Future pacing data; may include `market_info` when resolved by coordinates
content:
application/json:
schema:
$ref: "#/components/schemas/MessageObjectResponse"
"400":
description: Missing market_id and coordinate pair
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"404":
description: Market not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorBodyErrorKey"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/listings/search/{proxy}:
post:
tags: [Listings]
operationId: postListingsSearchByProxy
summary: Search listings by bounds, market, or radius
description: |
Page size is fixed at 50. `page` defaults to 1.
**Legacy response**: `message.listings` is a JSON **string** whose value is itself a
JSON object `{"message":[...]}` (double-encoded for backward compatibility).
parameters:
- name: proxy
in: path
required: true
schema:
type: string
enum: [bounds, market, radius]
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ListingSearchRequest"
responses:
"200":
description: Listings payload and total count
content:
application/json:
schema:
$ref: "#/components/schemas/ListingSearchResponse"
"400":
description: Invalid proxy, body, bounds, market_id, radius inputs, page, or filters
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/listings/metrics/{proxy}:
get:
tags: [Listings]
operationId: getListingsMetricsByProxy
summary: Metrics for a single listing
parameters:
- name: proxy
in: path
required: true
schema:
type: string
enum: [all]
- name: listing_id
in: query
required: true
schema:
type: string
- name: granularity
in: query
required: false
schema:
type: string
enum: [monthly, yearly]
default: yearly
responses:
"200":
description: Listing metrics
content:
application/json:
schema:
$ref: "#/components/schemas/MessageObjectResponse"
"400":
description: Missing listing_id, invalid metric or granularity
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/report/{proxy}:
post:
tags: [Reports]
operationId: postReportByProxy
summary: Create a report job
parameters:
- name: proxy
in: path
required: true
schema:
type: string
enum: [all, summary]
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateReportRequest"
responses:
"200":
description: Report created
content:
application/json:
schema:
$ref: "#/components/schemas/CreateReportResponse"
"400":
description: Invalid report type or body
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/report:
get:
tags: [Reports]
operationId: getReport
summary: Fetch a report by id
parameters:
- name: id
in: query
required: true
schema:
type: string
description: Report identifier returned from create.
responses:
"200":
description: Formatted report (shape depends on report type)
content:
application/json:
schema:
$ref: "#/components/schemas/MessageObjectResponse"
"400":
description: Missing id
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/search/ranking:
post:
tags: [Search]
operationId: postSearchRanking
summary: Rank listings inside a polygon (Airbnb / Vrbo)
description: >
Forwards to the search-ranking worker. Requires valid `bounds`. Optional `site` defaults
in downstream logic; optional `filters` and `country_code` are passed through.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SearchRankingRequest"
responses:
"200":
description: Ranked listing ids
content:
application/json:
schema:
$ref: "#/components/schemas/SearchRankingResponse"
"400":
description: Missing or invalid bounds
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
/live/listing/search:
get:
tags: [Live]
operationId: getLiveListingSearch
summary: Live snapshot for a listing
parameters:
- name: listing_id
in: query
required: true
schema:
type: string
responses:
"200":
description: Listing payload
content:
application/json:
schema:
$ref: "#/components/schemas/MessageObjectResponse"
"400":
description: Missing listing_id
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
"404":
description: Listing not found
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorBodyErrorKey"
"500":
description: Server error
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorMessageBody"
components:
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: x-api-key
description: Public API key (see `authorizePublicApiUser`).
parameters:
MarketIdQuery:
name: market_id
in: query
required: false
schema:
type: integer
description: Numeric market ID. Use this **or** search_latitude + search_longitude.
SearchLatitudeQuery:
name: search_latitude
in: query
required: false
schema:
type: number
format: float
SearchLongitudeQuery:
name: search_longitude
in: query
required: false
schema:
type: number
format: float
FiltersQueryJson:
name: filters
in: query
required: false
schema:
type: string
description: >
URL-encoded JSON object of listing filters for market metrics/summary.
Invalid JSON or validation errors return 400 with a string `message`. See
`apps/public_api/markets/readme.md` for allowed keys.
**Billing (GET, deprecated)**: non-empty `filters` query or legacy `bedrooms`
query doubles the price of `/markets/summary` and `/markets/metrics/<proxy>`
(same amounts as POST body `filters`). **POST** uses a non-empty JSON
`filters` object in the request body for surcharge (see
`authorizePublicApiUser`). `/markets/metrics/future/pacing` is not surcharged.
schemas:
MarketMetricsPostRequest:
type: object
description: >
Request body for `POST /markets/summary` and `POST /markets/metrics/{proxy}`.
Do not send top-level `bedrooms` — nest under `filters` (e.g.
`{"filters":{"bedrooms":["1"]}}`).
properties:
market_id:
type: integer
description: Numeric market ID, or omit and use search_latitude + search_longitude.
search_latitude:
type: number
format: float
search_longitude:
type: number
format: float
number_of_months:
type: integer
minimum: 1
maximum: 36
default: 12
description: Only used by metrics POST; ignored on summary POST.
filters:
type: object
additionalProperties: true
description: >
Optional. Same keys as GET `filters` (see `apps/public_api/markets/readme.md`).
When present and non-empty, uses the filtered metrics path and triggers surcharge.
additionalProperties: true
ErrorMessageBody:
type: object
properties:
message:
description: Human-readable error or structured error payload
oneOf:
- type: string
- type: object
additionalProperties: true
hint:
type: string
description: >
Optional. Present on some 400 responses when `filters` use keys from the
wrong public API surface (e.g. `adr` on market metrics, or `prof_mgmt` on
listings search). See `packages/utility/python/public_api_handler_failure.py`.
ErrorBodyErrorKey:
type: object
properties:
error:
type: string
MessageObjectResponse:
type: object
required: [message]
properties:
message:
type: object
additionalProperties: true
description: Arbitrary JSON object; see handler helpers for field lists.
MarketSearchResponse:
type: object
required: [message]
properties:
message:
type: array
items:
$ref: "#/components/schemas/MarketSearchResultItem"
MarketsByCountryResponse:
type: object
required: [message]
properties:
message:
type: object
required: [markets, page, page_size, total_count, has_more]
properties:
markets:
type: array
items:
$ref: "#/components/schemas/MarketSearchResultItem"
page:
type: integer
minimum: 1
page_size:
type: integer
enum: [10]
total_count:
type: integer
minimum: 0
has_more:
type: boolean
MarketSearchResultItem:
type: object
description: Same shape as `/markets/search` result rows.
properties:
id:
type: integer
name:
type: string
region:
type: string
country:
type: string
country_code:
type: string
verified:
type: boolean
MarketMetadataResponse:
type: object
required: [message]
properties:
message:
type: object
required: [district, country_code, verified_tier, submarkets]
properties:
district:
type: string
country_code:
type: string
verified_tier:
type: string
submarkets:
type: array
items:
type: object
required: [market_id, district, country_code]
properties:
market_id:
type: integer
district:
type: string
country_code:
type: string
MarketMetricsEnvelope:
type: object
description: >
Typical shape includes `message`, `metadata`, and optionally `market_info`.
Exact KPI keys vary by endpoint; see `marketMetrics/helper.py` and markets readme.
properties:
message:
type: object
additionalProperties: true
metadata:
type: object
additionalProperties: true
market_info:
type: object
additionalProperties: true
BoundsBox:
type: object
required: [ne_lat, ne_lng, sw_lat, sw_lng]
properties:
ne_lat:
type: number
ne_lng:
type: number
sw_lat:
type: number
sw_lng:
type: number
ListingFilters:
type: object
properties:
property_type:
type: array
items:
type: string
description: Optional property-type filters.
bedrooms:
type: array
items:
oneOf:
- type: integer
- type: string
description: Optional bedroom buckets (for example `2`, `3`, `6+`).
bathrooms:
type: array
items:
oneOf:
- type: integer
- type: string
description: Optional bathroom buckets (for example `1`, `2`, `6+`).
sleeps:
type: object
properties:
min:
type: number
max:
type: number
additionalProperties: false
minstay:
type: object
properties:
min:
type: number
max:
type: number
additionalProperties: false
rating:
type: object
properties:
min:
type: number
max:
type: number
additionalProperties: false
reviews:
type: string
description: Optional review-count threshold string such as `10+`.
adr:
type: object
properties:
min:
type: number
max:
type: number
additionalProperties: false
revenue:
type: object
properties:
min:
type: number
max:
type: number
additionalProperties: false
additionalProperties: true
description: >
Optional filters object. Supported keys and semantics are documented in
`apps/public_api/listings/listingSearch/readme.md`. Unknown keys are ignored by the handler.
ListingSearchRequest:
type: object
description: >
Fields used depend on `proxy` path segment. `filters` must be an object if present.
properties:
page:
type: integer
minimum: 1
default: 1
bounds:
$ref: "#/components/schemas/BoundsBox"
market_id:
type: integer
minimum: 1
center_lat:
type: number
center_lng:
type: number
distance_in_meters:
type: integer
minimum: 1
sort_by:
type:
- string
- "null"
description: Optional sort key; passed through when supported.
filters:
$ref: "#/components/schemas/ListingFilters"
ListingSearchResponse:
type: object
required: [message]
properties:
message:
type: object
required: [listings, total_count]
properties:
listings:
type: string
description: >
JSON string containing an object `{"message":[...]}` where the array is listing rows.
total_count:
type: integer
minimum: 0
CreateReportRequest:
type: object
required: [latitude, longitude, bedrooms, bathrooms, accommodates]
properties:
latitude:
type: number
longitude:
type: number
bedrooms:
type: integer
bathrooms:
type: integer
accommodates:
type: integer
CreateReportResponse:
type: object
required: [message]
properties:
message:
type: object
required: [report_id]
properties:
report_id:
type: string
SearchRankingRequest:
type: object
required: [bounds]
properties:
bounds:
$ref: "#/components/schemas/BoundsBox"
site:
type: string
enum: [airbnb, vrbo]
description: Optional; downstream defaults if omitted.
filters:
type: object
additionalProperties: true
description: Optional filter object validated by the worker.
country_code:
type: string
description: Optional proxy / session hint.
SearchRankingResponse:
type: object
required: [message]
properties:
message:
type: array
items:
type: object
additionalProperties: true
description: Array of ranked rows (e.g. listing_id, rank, page).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment