airbnb

package
v0.0.0-...-9f17d65 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: Apache-2.0 Imports: 20 Imported by: 0

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

View Source
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.

View Source
const BaseURL = "https://" + hostname

BaseURL is the root every page, GraphQL, and JSON URL is built from.

View Source
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

View Source
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

func Defaults(c *kit.Config)

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.

func Identity

func Identity() kit.Identity

Identity is the fixed description of the airbnb CLI, shared by the domain and the standalone composition root so help and version read the same everywhere.

func URLFor

func URLFor(kind, id string) string

URLFor builds the canonical Airbnb URL for a (kind, id) pair.

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

func ClientFromConfig(cfg kit.Config) *Client

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 NewClient

func NewClient(cfg Config) *Client

NewClient builds a client from cfg.

func (*Client) Calendar

func (c *Client) Calendar(ctx context.Context, ref string, limit int) ([]*Day, error)

Calendar returns a listing's availability days, up to limit (0 = all fetched).

func (*Client) ClearCache

func (c *Client) ClearCache() error

ClearCache removes the on-disk cache.

func (*Client) Experiences

func (c *Client) Experiences(ctx context.Context, place string, limit int) ([]*Experience, error)

Experiences returns experience cards for a place, paging up to limit.

func (*Client) GetHost

func (c *Client) GetHost(ctx context.Context, ref string) (*Host, error)

GetHost returns a host/user profile by id (or /users/show/ URL).

func (*Client) GetRoom

func (c *Client) GetRoom(ctx context.Context, ref string) (*Room, error)

GetRoom returns one listing by id (or /rooms/ URL).

func (*Client) HostListings

func (c *Client) HostListings(ctx context.Context, ref string, limit int) ([]*Listing, error)

HostListings returns a host's public listings, up to limit (capped by the operation at about ten).

func (*Client) Reviews

func (c *Client) Reviews(ctx context.Context, ref string, limit int) ([]*Review, error)

Reviews returns a listing's reviews, paging up to limit.

func (*Client) Search

func (c *Client) Search(ctx context.Context, place string, limit int) ([]*Listing, error)

Search returns listings for a place, paging the cursor up to limit.

func (*Client) Suggest

func (c *Client) Suggest(ctx context.Context, prefix string, limit int) ([]*Place, error)

Suggest returns location autocomplete terms for a typed prefix.

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

func (Domain) Classify(input string) (uriType, id string, err error)

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.

func (Domain) Locate

func (Domain) Locate(uriType, id string) (string, error)

Locate is the inverse: the live https URL for a (type, id).

func (Domain) Register

func (Domain) Register(app *kit.App)

Register installs the client factory and every operation onto app. A resolver op (Single) names its own record type and answers `ant get`; a List op enumerates a parent resource's members and answers `ant ls`.

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.

func Classify

func Classify(ref string) Ref

Classify reads a reference (a URL, a path, a bare id, or a pasted GraphQL global id) and reports what it points at. Kind is one of room, host, experience, or unknown.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL