Documentation
¶
Overview ¶
Package airbnb is the library behind the airbnb command line: an HTTP client for Airbnb's public web pages and its internal data plane, and the typed records every command emits.
Airbnb renders a listing page server-side and embeds the whole client state as a JSON island (data-deferred-state-0), so this client GETs the page and reads that island; every other surface (search, reviews, calendar, host, experiences) is the logged-out web client's own GraphQL endpoint (api/v3), addressed by the public web key the site ships and a per-operation persisted-query hash, plus two lighter api/v2 JSON endpoints the search box uses. Airbnb fronts its whole estate with an edge bot manager (Cloudflare and DataDome) that classifies a request on IP reputation and TLS fingerprint before the application sees it, so from a datacenter IP nearly everything comes back as a bodyless 403; from a residential or mobile connection the same requests return the data. There is no official API to fall back to, so a walled read returns ErrBlocked (exit 4) with a message that names the remedy. This client does not forge a TLS fingerprint and does not rent or rotate IPs: it reads what a logged-out browser reads, the way it reads it. Each surface lives in its own file (search.go, room.go, reviews.go, calendar.go, host.go, experiences.go, suggest.go) with its parsing and record mapping; this file holds the shared web client and api.go holds the GraphQL client.
Index ¶
- Constants
- Variables
- func Defaults(c *kit.Config)
- func Identity() kit.Identity
- func URLFor(kind, id string) string
- type Client
- func (c *Client) Calendar(ctx context.Context, ref string, limit int) ([]*Day, error)
- func (c *Client) ClearCache() error
- func (c *Client) Experiences(ctx context.Context, place string, limit int) ([]*Experience, error)
- func (c *Client) GetHost(ctx context.Context, ref string) (*Host, error)
- func (c *Client) GetRoom(ctx context.Context, ref string) (*Room, error)
- func (c *Client) HostListings(ctx context.Context, ref string, limit int) ([]*Listing, error)
- func (c *Client) Reviews(ctx context.Context, ref string, limit int) ([]*Review, error)
- func (c *Client) Search(ctx context.Context, place string, limit int) ([]*Listing, error)
- func (c *Client) Suggest(ctx context.Context, prefix string, limit int) ([]*Place, error)
- type Config
- type Day
- type Domain
- type Experience
- type Host
- type Listing
- type Place
- type Ref
- type Review
- type Room
Constants ¶
const ( // DefaultDelay is the minimum gap between requests. Airbnb's edge is touchy, // so a two-second pace reads steadily without leaning on it. DefaultDelay = 2 * time.Second DefaultRetries = 3 DefaultTimeout = 30 * time.Second DefaultCacheTTL = 24 * time.Hour )
Defaults for the polite client.
const BaseURL = "https://" + hostname
BaseURL is the root every page, GraphQL, and JSON URL is built from.
const DefaultUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " +
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"
DefaultUserAgent is sent with every request. Airbnb serves its public surfaces to a normal browser; a browser User-Agent is what keeps a logged-out reader looking like one. Override it with --user-agent.
Variables ¶
var ( // ErrNotFound is a missing entity: an unknown room, user, or experience. // Airbnb serves these as a 404 or a not-found GraphQL envelope. Exit code 6. ErrNotFound = errors.New("not found") // ErrRateLimited is a sustained HTTP 429 after the client's own retries. Slow // down with --rate. Exit code 5. ErrRateLimited = errors.New("rate limited") // ErrBlocked is Airbnb's edge bot wall: Cloudflare or DataDome classifies the // request on IP reputation and TLS fingerprint and answers with a bodyless // 403, a challenge body, or a connection reset, before the application sees // it. A GraphQL reply whose errors name an auth or rate problem (a stale // persisted-query hash or a rejected key) reads here too. The data is public // and needs no account, so the message names the real remedy: a residential // or mobile connection. This tool does not forge a TLS fingerprint or rent an // IP to get past the edge. Exit code 4. ErrBlocked = errors.New("blocked by Airbnb's edge bot wall (the data is public and needs no " + "account; retry from a residential or mobile connection)") )
The library reports its outcomes as a few sentinel errors. domain.go's mapErr translates each into the kit error kind that carries the matching exit code, so the standalone binary and a host agree on what a wall, a throttle, and a miss mean.
Functions ¶
func Defaults ¶
Defaults seeds the framework baseline with airbnb's own values, so an unset --rate or --timeout uses the airbnb default rather than the generic kit one.
Types ¶
type Client ¶
type Client struct {
HTTP *http.Client
BaseURL string
UserAgent string
Locale string
Currency string
Adults int
Children int
Checkin string
Checkout string
Delay time.Duration
Retries int
// contains filtered or unexported fields
}
Client talks to Airbnb's public web pages and its internal data plane. It paces requests, retries the transient failures, detects the edge bot wall, and caches response bodies on disk keyed by the request.
func ClientFromConfig ¶
ClientFromConfig maps the framework config onto an airbnb.Config and returns a client. There are no credentials to read: the public web key is a constant, overridable only with --api-key, and Airbnb has no opt-in signed backend.
func (*Client) Calendar ¶
Calendar returns a listing's availability days, up to limit (0 = all fetched).
func (*Client) ClearCache ¶
ClearCache removes the on-disk cache.
func (*Client) Experiences ¶
Experiences returns experience cards for a place, paging up to limit.
func (*Client) HostListings ¶
HostListings returns a host's public listings, up to limit (capped by the operation at about ten).
type Config ¶
type Config struct {
UserAgent string
Locale string
Currency string
// APIKey overrides the public web key. Empty uses defaultAPIKey, refreshed
// from the live site when reachable.
APIKey string
// Checkin and Checkout (ISO YYYY-MM-DD) make a date-specific nightly price
// appear on room and scope a search. Empty leaves the price out rather than
// inventing a nightly figure.
Checkin string
Checkout string
Adults int
Children int
// Delay is the minimum gap between requests. Zero means no pacing.
Delay time.Duration
Retries int
Timeout time.Duration
// BaseURL is the site root. Empty uses the public site; tests point it at an
// httptest server.
BaseURL string
// CacheDir is where responses are cached. Empty disables the cache, as does
// NoCache.
CacheDir string
CacheTTL time.Duration
NoCache bool
// Refresh fetches fresh copies and rewrites the cache, ignoring any hit.
Refresh bool
}
Config carries the knobs the client reads. It is built from the kit framework config in ClientFromConfig, so a --rate or --timeout on the command line and the same value resolved by a host both land here.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns the baseline configuration: a browser User-Agent, the en/USD locale and currency, a two-second pace, three retries, a 30s timeout, one adult, and a one-day cache.
type Day ¶
type Day struct {
ID string `json:"id" kit:"id"` // "<roomid>:<date>"
Date string `json:"date"` // YYYY-MM-DD
Available bool `json:"available"`
Bookable bool `json:"bookable,omitempty"`
MinNights int `json:"min_nights,omitempty"`
MaxNights int `json:"max_nights,omitempty"`
Price float64 `json:"price,omitempty"` // nightly price for this date, when published
Currency string `json:"currency,omitempty"`
Room string `json:"room,omitempty" table:"-" kit:"link,kind=airbnb/room"` // edge back to the listing
}
Day is one day on a listing's availability calendar, emitted by calendar. The id is the room id joined to the date, so the store keeps one row per listing-day and a re-fetch upserts cleanly. Available is the day's calendar state; Bookable reports whether a stay may actually start or span it.
type Domain ¶
type Domain struct{}
Domain is the airbnb driver. It carries no state; the per-run client is built by the factory Register hands kit.
func (Domain) Classify ¶
Classify turns any accepted input into the canonical (type, id), so `ant resolve` and `ant url` touch no network.
func (Domain) Info ¶
func (Domain) Info() kit.DomainInfo
Info describes the scheme, the hostnames a pasted link is matched against, and the identity reused for the binary's help and version.
type Experience ¶
type Experience struct {
ID string `json:"id" kit:"id"`
Title string `json:"title,omitempty" table:",truncate"`
Host string `json:"host,omitempty"`
Price float64 `json:"price,omitempty"` // per-guest price
Currency string `json:"currency,omitempty"`
Rating float64 `json:"rating,omitempty"`
Reviews int `json:"reviews,omitempty"`
Location string `json:"location,omitempty"`
Lat float64 `json:"lat,omitempty"`
Lng float64 `json:"lng,omitempty"`
Image string `json:"image,omitempty" table:",truncate"`
URL string `json:"url"`
}
Experience is an experience search result, emitted by experiences.
type Host ¶
type Host struct {
ID string `json:"id" kit:"id"` // user id
Name string `json:"name,omitempty"`
Superhost bool `json:"superhost,omitempty"`
Since string `json:"since,omitempty"` // the "joined in <year>" text
Location string `json:"location,omitempty"` // the host's stated home, e.g. "Lives in Paris"
About string `json:"about,omitempty" table:",truncate" kit:"body"`
ResponseRate string `json:"response_rate,omitempty"`
ResponseTime string `json:"response_time,omitempty"` // "within an hour", ...
Languages []string `json:"languages,omitempty" table:"-"`
Listings int `json:"listings,omitempty"` // public managed-listings count
Reviews int `json:"reviews,omitempty"` // total reviews across the host's listings
Verified bool `json:"verified,omitempty"` // identity verified
Image string `json:"image,omitempty" table:",truncate"`
URL string `json:"url"`
ListingsRef string `json:"listings_ref,omitempty" table:"-" kit:"link,kind=airbnb/listings"` // edge to the host's listings (= id)
}
Host is a host/user public profile, emitted by host show. The listings_ref edge carries the user id so a crawl reaching a host expands to the host's listings.
type Listing ¶
type Listing struct {
ID string `json:"id" kit:"id"` // room id
Name string `json:"name,omitempty" table:",truncate"`
Title string `json:"title,omitempty" table:",truncate"` // the card's title line
RoomType string `json:"room_type,omitempty"` // Entire home, Private room, ...
Price float64 `json:"price,omitempty"` // nightly, for the searched dates
Original float64 `json:"original,omitempty"` // pre-discount nightly, when the card strikes one through
Currency string `json:"currency,omitempty"`
Total float64 `json:"total,omitempty"` // total for the stay, when dates are given
Rating float64 `json:"rating,omitempty"`
Reviews int `json:"reviews,omitempty"`
Lat float64 `json:"lat,omitempty"`
Lng float64 `json:"lng,omitempty"`
Badges []string `json:"badges,omitempty" table:"-"` // Guest favorite, Superhost, ...
Images []string `json:"images,omitempty" table:"-"` // the card's photo carousel
Image string `json:"image,omitempty" table:",truncate"` // the first photo
URL string `json:"url"`
Room string `json:"room,omitempty" table:"-" kit:"link,kind=airbnb/room"` // edge to the full listing (= id)
Host string `json:"host,omitempty" table:"-" kit:"link,kind=airbnb/host"` // edge to the host, when the card carries it
}
Listing is a summary card in a grid, emitted by search and host listings. Its id is the room id, so Room carries the same value as the graph edge to the full listing: a host walks a search result straight through to airbnb://room/<id>.
type Place ¶
type Place struct {
Query string `json:"query"` // the prefix that was queried
Name string `json:"name" kit:"id"` // the suggested place name
PlaceID string `json:"place_id,omitempty"` // the Google place id the search uses
Lat float64 `json:"lat,omitempty"`
Lng float64 `json:"lng,omitempty"`
SearchRef string `json:"search_ref,omitempty" table:"-" kit:"link,kind=airbnb/search"` // edge into a stay search (= name)
ExperiencesRef string `json:"experiences_ref,omitempty" table:"-" kit:"link,kind=airbnb/experiences"` // edge into an experience search (= name)
}
Place is one location autocomplete suggestion, emitted by suggest. PlaceID is the identifier search resolves a free-text place into. SearchRef carries the place name as the edge into a stay search and ExperiencesRef as the edge into an experience search, so a crawl can start from a typed prefix and fan out into both the stays and the experiences a place returns.
type Ref ¶
type Ref struct {
Input string `json:"input"`
Kind string `json:"kind"`
ID string `json:"id"`
URL string `json:"url"`
}
Ref is the result of `airbnb ref id`: the canonical (kind, id) a reference resolves to, plus the live URL, all without touching the network.
type Review ¶
type Review struct {
ID string `json:"id" kit:"id"`
Author string `json:"author,omitempty"`
AuthorID string `json:"author_id,omitempty" table:"-" kit:"link,kind=airbnb/host"` // edge to the reviewer's profile
AuthorImage string `json:"author_image,omitempty" table:"-"`
Location string `json:"location,omitempty"` // the reviewer's stated home, when shown
Date string `json:"date,omitempty"` // the localized review date
Trip string `json:"trip,omitempty"` // the stay descriptor, e.g. "Stayed a few nights"
Rating int `json:"rating,omitempty"` // the reviewer's star rating, when present
Text string `json:"text,omitempty" table:",truncate" kit:"body"`
Response string `json:"response,omitempty" table:",truncate"` // the host's public response, when present
Language string `json:"language,omitempty"`
Room string `json:"room,omitempty" table:"-" kit:"link,kind=airbnb/room"` // edge back to the listing
}
Review is one review of a listing, emitted by reviews. AuthorID, when the payload carries it, is the reviewer's user id, so a crawl can walk a review to the reviewer's public profile.
type Room ¶
type Room struct {
ID string `json:"id" kit:"id"` // room id
Name string `json:"name,omitempty" table:",truncate"`
Description string `json:"description,omitempty" table:",truncate" kit:"body"`
Highlights []string `json:"highlights,omitempty" table:"-"` // the icon highlights, e.g. "Self check-in"
PropertyType string `json:"property_type,omitempty"` // House, Apartment, ...
RoomType string `json:"room_type,omitempty"` // Entire home, Private room, ...
Capacity int `json:"capacity,omitempty"` // person capacity
Bedrooms int `json:"bedrooms,omitempty"`
Beds int `json:"beds,omitempty"`
Bathrooms string `json:"bathrooms,omitempty"` // "1.5 baths"; text, since half-baths are common
Sleeping []string `json:"sleeping,omitempty" table:"-"` // sleeping arrangements, e.g. "Bedroom 1: 1 queen bed"
Amenities []string `json:"amenities,omitempty" table:"-"`
Price float64 `json:"price,omitempty"` // nightly, when dates are given
Currency string `json:"currency,omitempty"`
Rating float64 `json:"rating,omitempty"` // overall guest-satisfaction rating
Accuracy float64 `json:"accuracy,omitempty"` // the six category ratings
CheckinRating float64 `json:"checkin_rating,omitempty"`
Cleanliness float64 `json:"cleanliness,omitempty"`
Communication float64 `json:"communication,omitempty"`
LocationRating float64 `json:"location_rating,omitempty"`
Value float64 `json:"value,omitempty"`
Reviews int `json:"reviews,omitempty"` // visible review count
Location string `json:"location,omitempty"` // the listing's area, e.g. "Tahoe Vista, California"
Lat float64 `json:"lat,omitempty"`
Lng float64 `json:"lng,omitempty"`
CheckIn string `json:"check_in,omitempty"` // the check-in window, when the rules state one
CheckOut string `json:"check_out,omitempty"` // the checkout time, when stated
HouseRules []string `json:"house_rules,omitempty" table:"-"`
Superhost bool `json:"superhost,omitempty"`
HostID string `json:"host_id,omitempty" table:"-" kit:"link,kind=airbnb/host"` // edge to the host
HostName string `json:"host_name,omitempty"`
HostImage string `json:"host_image,omitempty" table:"-"` // the host's public profile photo
Images []string `json:"images,omitempty" table:"-"` // the photo gallery
Image string `json:"image,omitempty" table:",truncate"` // the first photo
URL string `json:"url"`
ReviewsRef string `json:"reviews_ref,omitempty" table:"-" kit:"link,kind=airbnb/reviews"` // edge to this listing's reviews (= id)
CalendarRef string `json:"calendar_ref,omitempty" table:"-" kit:"link,kind=airbnb/calendar"` // edge to this listing's calendar (= id)
}
Room is the full detail for one listing, emitted by room. It is parsed from the PDP JSON island, with its date-specific price filled from StaysPdpSections when check-in and check-out are given. The reviews_ref and calendar_ref edges carry the room id so a crawl reaching a room expands to its reviews and its calendar.