pub struct Client591 { /* private fields */ }Expand description
Client for the 591 rental platform public API.
Uses native HTTP (reqwest) — no browser required.
Implementations§
Source§impl Client591
impl Client591
Sourcepub fn new() -> Result<Self, TailFinError>
pub fn new() -> Result<Self, TailFinError>
Create a new client.
Sourcepub async fn hot(
&self,
region_id: u32,
limit: usize,
) -> Result<Vec<Community>, TailFinError>
pub async fn hot( &self, region_id: u32, limit: usize, ) -> Result<Vec<Community>, TailFinError>
Fetch the hot community list for a given region.
region_id is the 591 region code (1 = Taipei City).
Returns up to limit communities (the API ignores the limit param,
so slicing is done client-side).
Sourcepub async fn community(
&self,
id: u64,
) -> Result<Option<CommunityDetail>, TailFinError>
pub async fn community( &self, id: u64, ) -> Result<Option<CommunityDetail>, TailFinError>
Fetch detailed info for a community by ID.
Sourcepub async fn price_history(
&self,
id: u64,
limit: usize,
) -> Result<Vec<PriceRecord>, TailFinError>
pub async fn price_history( &self, id: u64, limit: usize, ) -> Result<Vec<PriceRecord>, TailFinError>
Fetch transaction price history for a community.
Returns recent actual sale prices recorded in the ROC government registry.
Sourcepub async fn nearby(
&self,
id: u64,
limit: usize,
) -> Result<Vec<NearbyCommunity>, TailFinError>
pub async fn nearby( &self, id: u64, limit: usize, ) -> Result<Vec<NearbyCommunity>, TailFinError>
Fetch nearby communities (geographically close to id).
591 returns up to ~5 communities with sale-side stats (price per ping, min sale price, distance). Useful for “what else is in the same neighbourhood” queries; orthogonal to the rent-hot list (which is region-keyed, not community-keyed).
Sourcepub async fn sale_list(
&self,
region_id: u32,
first_row: usize,
limit: usize,
) -> Result<SaleHousePage, TailFinError>
pub async fn sale_list( &self, region_id: u32, first_row: usize, limit: usize, ) -> Result<SaleHousePage, TailFinError>
Paginated sale listings for a region.
Hits bff-house.591.com.tw/v1/web/sale/list. 591 paginates by
firstRow (0-indexed offset, page size 30). The total in
the returned SaleHousePage is the count of all matching
listings across pages.
region_id is the same scheme as hot() (1 = Taipei).
Sourcepub async fn newhouse_list(
&self,
region_id: u32,
page: u32,
) -> Result<NewhousePage, TailFinError>
pub async fn newhouse_list( &self, region_id: u32, page: u32, ) -> Result<NewhousePage, TailFinError>
Paginated new-construction (newhouse) projects for a region.
Hits bff-newhouse.591.com.tw/v1/list-search. Pagination uses
page (1-indexed); 591 returns 20 per page.
Sourcepub async fn newhouse_base_info(
&self,
hid: u64,
) -> Result<NewhouseHousing, TailFinError>
pub async fn newhouse_base_info( &self, hid: u64, ) -> Result<NewhouseHousing, TailFinError>
Newhouse project detail core fields (build name, address, price, area, room layouts, build type, manager fees, dates, licenses, …).
Hits bff-newhouse.591.com.tw/v1/detail/base-info. Returns the
curated NewhouseHousing subset of the wire data.housing
block (80+ fields → ~25 most useful for project shopping).
hid is the project ID — same as NewhouseProject.hid from
newhouse_list.
Sourcepub async fn newhouse_module_info(
&self,
hid: u64,
) -> Result<NewhouseModules, TailFinError>
pub async fn newhouse_module_info( &self, hid: u64, ) -> Result<NewhouseModules, TailFinError>
Newhouse project module-info: floor plans, market history, listed sales agents.
Hits bff-newhouse.591.com.tw/v1/detail/module-info. Returns
the curated NewhouseModules aggregate of layout,
market, and sales. The news and report wire keys are
dropped (mostly empty across the projects we sampled).
Sourcepub async fn newhouse_photos(
&self,
hid: u64,
) -> Result<Vec<NewhousePhotoCategory>, TailFinError>
pub async fn newhouse_photos( &self, hid: u64, ) -> Result<Vec<NewhousePhotoCategory>, TailFinError>
Newhouse project photo gallery, organized by category.
Hits bff-newhouse.591.com.tw/v1/detail/photos. Returns a
Vec of NewhousePhotoCategory (cover / floor plan / traffic
/ 3D / real-life / environment) — empty Vec if the project has
no photos uploaded.
Sourcepub async fn newhouse_surrounding(
&self,
hid: u64,
) -> Result<NewhouseSurrounding, TailFinError>
pub async fn newhouse_surrounding( &self, hid: u64, ) -> Result<NewhouseSurrounding, TailFinError>
Newhouse project’s surrounding POIs (transit, schools, life amenities) plus building/sales-office geo coordinates.
Hits bff-newhouse.591.com.tw/v1/detail/surrounding.
Sourcepub async fn newhouse_nearby_market(
&self,
hid: u64,
) -> Result<NewhouseNearbyMarket, TailFinError>
pub async fn newhouse_nearby_market( &self, hid: u64, ) -> Result<NewhouseNearbyMarket, TailFinError>
Newhouse project’s nearby resale comps (other communities) and business districts (商圈) with average prices.
Hits bff-newhouse.591.com.tw/v1/detail/nearby-market. Note
the URL param is hid (not id) on this endpoint — separate
query semantics from the id-keyed siblings.
Sourcepub async fn newhouse_price_list(
&self,
hid: u64,
) -> Result<NewhousePriceList, TailFinError>
pub async fn newhouse_price_list( &self, hid: u64, ) -> Result<NewhousePriceList, TailFinError>
Newhouse project price-list (per-unit catalogue + sale-control metadata).
Hits bff-newhouse.591.com.tw/v1/price/list with trans_type
fixed at 1 (sale price; trans_type=2 returns rental-side data
for the same projects, not exercised here). Often less rich
than newhouse_module_info().market for active pre-sale
projects — call both and prefer module-info’s market block
for actual transaction history.
Sourcepub async fn newhouse_detail(&self, hid: u64) -> NewhouseDetail
pub async fn newhouse_detail(&self, hid: u64) -> NewhouseDetail
Fetch the full newhouse-detail bundle in parallel.
Calls all 6 detail sub-endpoints (base-info, module-info,
photos, surrounding, nearby-market, price/list)
concurrently via tokio::join!. Each sub-call’s success or
failure lands independently in the corresponding bundle field
— partial failures don’t fail the whole bundle. Returns once
all 6 calls have settled.
Typical wall-clock: ~350ms (the slowest sub-call dominates) — roughly 4× faster than calling the 6 atomic methods serially.
Sourcepub async fn community_rank(
&self,
region_id: u32,
) -> Result<CommunityRanks, TailFinError>
pub async fn community_rank( &self, region_id: u32, ) -> Result<CommunityRanks, TailFinError>
Two community rankings for a region — by price metric and by
sale-activity metric. Hits
bff.591.com.tw/v1/community/community-rank. Each slot holds
up to ~10 communities; both share the same time snapshot.
Sourcepub async fn rent_search(
&self,
params: &SearchParams,
) -> Result<(u32, Vec<SearchListing>), TailFinError>
pub async fn rent_search( &self, params: &SearchParams, ) -> Result<(u32, Vec<SearchListing>), TailFinError>
Pure-HTTP rental listing search via bff-house.591.com.tw/v3/web/rent/list.
No browser, no CSRF, no cookies required — verified
2026-04-30 against a clean curl from a fresh process. See
docs/superpowers/research/2026-04-30-591-har-discovery.md.
Pagination is by params.first_row (page size 30). The server
echoes firstRow: 0 even on later pages; trust your input
offset, not the response field.
Returns (total_matching, listings) where total_matching is
the global count across all pages (NOT this page’s length).
Sourcepub async fn rent_crawl<F>(
&self,
params: &SearchParams,
opts: &CrawlOptions,
on_page: F,
) -> Result<usize, TailFinError>
pub async fn rent_crawl<F>( &self, params: &SearchParams, opts: &CrawlOptions, on_page: F, ) -> Result<usize, TailFinError>
Crawl all rental listings matching params, paginating
automatically via /v3/web/rent/list.
Calls on_page(page_num, first_row, listings) after each page.
Stops when a page returns fewer than RENT_PAGE_SIZE items
(last page reached) or after opts.max_pages pages if non-zero.
Returns the total number of listings the callback received.
Sourcepub async fn high_value_search(
&self,
params: &HighValueParams,
) -> Result<Vec<HighValueListing>, TailFinError>
pub async fn high_value_search( &self, params: &HighValueParams, ) -> Result<Vec<HighValueListing>, TailFinError>
Curated premium-listing search.
POSTs JSON to bff-house.591.com.tw/v1/high-value/search.
591 returns a small (~6 items) hand-curated pool of premium
sale listings; the request-side kind/type/section_id/
shape/room/price/area filters are “preferred” rather
than strict (591’s curation logic decides the final set).
Two kind values populate distinct curated pools: 9 (the
default, mostly residential) and 10 (a separate bucket with
different streets / post_ids). Verified live 2026-04-30 — see
HighValueParams for the full quirk catalog. Use
HighValueParams::for_region for the typical defaults
(kind=9, type=2).
Sourcepub async fn coordinate_area(
&self,
latitude: f64,
longitude: f64,
region_id: u32,
) -> Result<Option<CoordArea>, TailFinError>
pub async fn coordinate_area( &self, latitude: f64, longitude: f64, region_id: u32, ) -> Result<Option<CoordArea>, TailFinError>
Reverse-geocode GPS coordinates to a 591 region+section.
Hits bff.591.com.tw/v1/coordinate/area. Returns Some(area)
when the coordinates land inside Taiwan, None otherwise (591
responds with status: 0 and the message
"坐标不在台湾范围内").
region_id is sent as a hint but doesn’t constrain the
response — the server resolves the actual region/section from
the lat/lng. Pass any valid region (1 = Taipei is a safe
default).
Status-handling differs from sibling endpoints. Most
Client591 methods collapse non-1 status into either an
error or Ok(None); this method instead matches 1 →
Ok(Some(_)), 0 → Ok(None) (documented miss), anything
else → Err. The intent: a future server-side wire-state
change becomes loud-fail rather than silently swallowed.
Sourcepub async fn rent_detail(
&self,
post_id: u64,
) -> Result<RentDetail, TailFinError>
pub async fn rent_detail( &self, post_id: u64, ) -> Result<RentDetail, TailFinError>
Single rent listing detail.
Hits bff-house.591.com.tw/v2/web/rent/detail. Returns the
curated RentDetail subset of the wire data block — full
address with lat/lng, deposit + cost breakdown, structured
houseInfo / preference / service / surround sections, plus
the listing’s primary contact (RentLinkInfo).
post_id is the rent listing ID (SearchListing.post_id from
rent_search). Pure HTTP — no browser, no auth.
Sourcepub async fn rent_photos(
&self,
post_id: u64,
) -> Result<Vec<RentPhotoGroup>, TailFinError>
pub async fn rent_photos( &self, post_id: u64, ) -> Result<Vec<RentPhotoGroup>, TailFinError>
Categorized photo gallery for a rent listing.
Hits bff-house.591.com.tw/v1/ware/photos. Returns a Vec of
RentPhotoGroup (photo buckets like "picture", "floor",
"environment"); empty Vec if the listing has no photos.
type=1 is hardcoded on the wire — it’s the rent-side photo
query mode (verified live 2026-04-30).
Sourcepub async fn sale_detail(
&self,
post_id: u64,
) -> Result<SaleDetail, TailFinError>
pub async fn sale_detail( &self, post_id: u64, ) -> Result<SaleDetail, TailFinError>
Single sale listing detail.
Hits bff-house.591.com.tw/v1/touch/sale/detail. Returns the
curated SaleDetail subset of the wire data block —
title, price (raw + numeric), area / layout / floor / shape,
lat / lng, age, fitment, agent contact (linkman, mobile,
telephone, email, identity, company_name).
post_id is the bare numeric sale listing ID. The wire
expects an S-prefixed form ("S19599759") — the adapter
adds the prefix for you. Uses device_id=tail-fin-rust-client
and device=touch query params; verified live 2026-05-01 that
the endpoint returns status: 0 without a non-empty device_id.
Sourcepub async fn sale_similar_wares(
&self,
post_id: u64,
) -> Result<Vec<SaleSimilarWare>, TailFinError>
pub async fn sale_similar_wares( &self, post_id: u64, ) -> Result<Vec<SaleSimilarWare>, TailFinError>
Similar sale listings — curated “you might also like” set.
Hits bff-house.591.com.tw/v2/web/sale/similar-wares. Returns
up to ~5 SaleSimilarWare entries (compact title / price /
area / room / cover-photo URL) for cross-listing browse.
Anonymous, no special headers needed.
Sourcepub async fn sales(
&self,
id: u64,
limit: usize,
) -> Result<(u32, Vec<SaleListing>), TailFinError>
pub async fn sales( &self, id: u64, limit: usize, ) -> Result<(u32, Vec<SaleListing>), TailFinError>
Fetch active sale listings near a community.
Listings are flattened across all room types, sorted by post time (API order).