import {z} from 'zod' export type GetApiV1ZillowPropertyZpidArgs = {zpid: string}; export type GetApiV1ZillowPropertyZpidOptions = {baseUrl?: string | undefined, apiKey?: string | undefined, fetch?: typeof fetch | undefined}; export const listingSubType = z.object({is_fsba: z.boolean().nullable().optional(), is_fsbo: z.boolean().nullable().optional(), is_foreclosure: z.boolean().nullable().optional(), is_bank_owned: z.boolean().nullable().optional(), is_for_auction: z.boolean().nullable().optional(), is_coming_soon: z.boolean().nullable().optional(), is_new_home: z.boolean().nullable().optional(), is_pending: z.boolean().nullable().optional()}); export const zestimateHistoryPoint = z.object({date: z.string().nullable().optional(), date_utc: z.number().nullable().optional(), date_at: z.string().nullable().optional(), value: z.number().int().nullable().optional()}); export const address = z.object({street_address: z.string().nullable().optional(), city: z.string().nullable().optional(), state: z.string().nullable().optional(), zipcode: z.string().nullable().optional(), community: z.string().nullable().optional(), subdivision: z.string().nullable().optional(), neighborhood: z.string().nullable().optional()}); export const homeFacts = z.object({bathrooms_full: z.number().int().nullable().optional(), bathrooms_half: z.number().int().nullable().optional(), bathrooms_three_quarter: z.number().int().nullable().optional(), bathrooms_one_quarter: z.number().int().nullable().optional(), stories: z.number().int().nullable().optional(), stories_decimal: z.number().nullable().optional(), levels: z.string().nullable().optional(), property_condition: z.string().nullable().optional(), architectural_style: z.string().nullable().optional(), structure_type: z.string().nullable().optional(), building_name: z.string().nullable().optional(), construction_materials: z.array(z.string()).optional(), foundation_details: z.array(z.string()).optional(), roof_type: z.string().nullable().optional(), year_built_effective: z.number().int().nullable().optional(), above_grade_finished_area: z.string().nullable().optional(), below_grade_finished_area: z.string().nullable().optional(), lot_size_dimensions: z.string().nullable().optional(), main_level_bedrooms: z.number().int().nullable().optional(), main_level_bathrooms: z.number().int().nullable().optional(), basement: z.string().nullable().optional(), has_basement: z.boolean().nullable().optional(), attic: z.string().nullable().optional(), heating: z.array(z.string()).optional(), cooling: z.array(z.string()).optional(), appliances: z.array(z.string()).optional(), flooring: z.array(z.string()).optional(), utilities: z.array(z.string()).optional(), electric: z.array(z.string()).optional(), gas: z.array(z.string()).optional(), sewer: z.array(z.string()).optional(), water_source: z.array(z.string()).optional(), green_building_verification_type: z.array(z.string()).optional(), green_energy_efficient: z.array(z.string()).optional(), green_energy_generation: z.array(z.string()).optional(), green_sustainability: z.array(z.string()).optional(), green_water_conservation: z.array(z.string()).optional(), interior_features: z.array(z.string()).optional(), exterior_features: z.array(z.string()).optional(), lot_features: z.array(z.string()).optional(), community_features: z.array(z.string()).optional(), accessibility_features: z.array(z.string()).optional(), door_features: z.array(z.string()).optional(), window_features: z.array(z.string()).optional(), laundry_features: z.array(z.string()).optional(), patio_and_porch_features: z.array(z.string()).optional(), fencing: z.array(z.string()).optional(), other_structures: z.array(z.string()).optional(), view: z.array(z.string()).optional(), has_view: z.boolean().nullable().optional(), waterfront_features: z.array(z.string()).optional(), water_view: z.string().nullable().optional(), water_body_name: z.string().nullable().optional(), security_features: z.array(z.string()).optional(), parking_features: z.array(z.string()).optional(), parking_capacity: z.number().int().nullable().optional(), garage_parking_capacity: z.number().int().nullable().optional(), carport_parking_capacity: z.number().int().nullable().optional(), covered_parking_capacity: z.number().int().nullable().optional(), open_parking_capacity: z.number().int().nullable().optional(), has_attached_garage: z.boolean().nullable().optional(), has_garage: z.boolean().nullable().optional(), has_carport: z.boolean().nullable().optional(), has_open_parking: z.boolean().nullable().optional(), pool_features: z.array(z.string()).optional(), has_private_pool: z.boolean().nullable().optional(), spa_features: z.array(z.string()).optional(), fireplaces: z.number().int().nullable().optional(), fireplace_features: z.array(z.string()).optional(), has_fireplace: z.boolean().nullable().optional(), association_name: z.string().nullable().optional(), association_name2: z.string().nullable().optional(), association_fee: z.string().nullable().optional(), association_fee2: z.string().nullable().optional(), association_fee_includes: z.array(z.string()).optional(), association_amenities: z.array(z.string()).optional(), association_phone: z.string().nullable().optional(), has_association: z.boolean().nullable().optional(), hoa_fee: z.string().nullable().optional(), hoa_fee_total: z.string().nullable().optional(), tax_annual_amount: z.number().nullable().optional(), price_per_square_foot: z.number().int().nullable().optional(), has_land_lease: z.boolean().nullable().optional(), land_lease_amount: z.string().nullable().optional(), land_lease_expiration_date: z.string().nullable().optional(), can_raise_horses: z.boolean().nullable().optional(), additional_parcels_description: z.string().nullable().optional(), road_surface_type: z.array(z.string()).optional(), on_market_date: z.string().nullable().optional(), cumulative_days_on_market: z.number().int().nullable().optional(), offer_review_date: z.string().nullable().optional(), number_of_units_in_community: z.number().int().nullable().optional(), availability_date: z.string().nullable().optional(), lease_term: z.string().nullable().optional(), tenant_pays: z.array(z.string()).optional(), has_pets_allowed: z.boolean().nullable().optional(), pets_max_weight: z.number().int().nullable().optional(), has_rent_control: z.boolean().nullable().optional(), elementary_school: z.string().nullable().optional(), middle_school: z.string().nullable().optional(), high_school: z.string().nullable().optional(), elementary_school_district: z.string().nullable().optional(), middle_school_district: z.string().nullable().optional(), high_school_district: z.string().nullable().optional(), parcel_number: z.string().nullable().optional(), subdivision_name: z.string().nullable().optional(), municipality: z.string().nullable().optional(), city_region: z.string().nullable().optional(), zoning: z.string().nullable().optional(), zoning_description: z.string().nullable().optional(), ownership: z.string().nullable().optional(), ownership_type: z.string().nullable().optional(), property_sub_type: z.array(z.string()).optional(), special_listing_conditions: z.string().nullable().optional(), listing_terms: z.string().nullable().optional(), inclusions: z.string().nullable().optional(), exclusions: z.string().nullable().optional(), is_new_construction: z.boolean().nullable().optional(), is_senior_community: z.boolean().nullable().optional(), has_home_warranty: z.boolean().nullable().optional(), furnished: z.boolean().nullable().optional(), development_status: z.string().nullable().optional(), park_name: z.string().nullable().optional(), at_a_glance_facts: z.array(z.record(z.string(), z.unknown())).optional(), rooms: z.array(z.record(z.string(), z.unknown())).optional()}); export const agentAttribution = z.object({agent_name: z.string().nullable().optional(), agent_phone: z.string().nullable().optional(), agent_email: z.string().nullable().optional(), agent_license_number: z.string().nullable().optional(), co_agent_name: z.string().nullable().optional(), co_agent_number: z.string().nullable().optional(), co_agent_license_number: z.string().nullable().optional(), broker_name: z.string().nullable().optional(), broker_phone: z.string().nullable().optional(), buyer_agent_name: z.string().nullable().optional(), buyer_brokerage_name: z.string().nullable().optional(), mls_id: z.string().nullable().optional(), mls_name: z.string().nullable().optional(), mls_disclaimer: z.string().nullable().optional(), listing_agreement: z.string().nullable().optional(), listing_attribution_contact: z.string().nullable().optional(), provider_logo: z.string().nullable().optional(), true_status: z.string().nullable().optional(), last_checked: z.string().nullable().optional(), last_updated: z.string().nullable().optional(), listing_agents: z.array(z.record(z.string(), z.unknown())).optional(), listing_offices: z.array(z.record(z.string(), z.unknown())).optional()}); export const mortgageRate = z.object({rate: z.number().nullable().optional(), rate_source: z.string().nullable().optional(), last_updated_utc: z.number().nullable().optional(), last_updated_at: z.string().nullable().optional()}); export const mortgageRates = z.object({fifteen_year_fixed: mortgageRate.nullable().optional(), thirty_year_fixed: mortgageRate.nullable().optional(), arm_5: mortgageRate.nullable().optional()}); export const priceHistoryEvent = z.object({date: z.string().nullable().optional(), date_utc: z.number().nullable().optional(), date_at: z.string().nullable().optional(), event: z.string().nullable().optional(), price: z.number().int().nullable().optional(), price_per_square_foot: z.number().int().nullable().optional(), price_change_rate: z.number().nullable().optional(), source: z.string().nullable().optional(), buyer_agent: z.string().nullable().optional(), seller_agent: z.string().nullable().optional(), posting_is_rental: z.boolean().nullable().optional()}); export const taxHistoryEvent = z.object({year_utc: z.number().nullable().optional(), year_at: z.string().nullable().optional(), value: z.number().int().nullable().optional(), value_increase_rate: z.number().nullable().optional(), tax_paid: z.number().nullable().optional(), tax_increase_rate: z.number().nullable().optional()}); export const school = z.object({name: z.string().nullable().optional(), rating: z.number().int().nullable().optional(), grades: z.string().nullable().optional(), level: z.string().nullable().optional(), type: z.string().nullable().optional(), distance: z.number().nullable().optional(), link: z.string().nullable().optional(), student_count: z.number().int().nullable().optional(), assigned: z.boolean().nullable().optional()}); export const photo = z.object({url: z.string().nullable().optional(), caption: z.string().nullable().optional(), subject_type: z.string().nullable().optional(), sources: z.array(z.record(z.string(), z.unknown())).optional()}); export const openHouse = z.object({start_utc: z.number().nullable().optional(), start_at: z.string().nullable().optional(), end_utc: z.number().nullable().optional(), end_at: z.string().nullable().optional(), note: z.string().nullable().optional()}); export const nearbyRegion = z.object({name: z.string().nullable().optional(), region_type: z.string().nullable().optional(), url: z.string().nullable().optional()}); export const listing = z.object({position: z.number().int(), zpid: z.string().nullable().optional(), id: z.string().nullable().optional(), detail_url: z.string().nullable().optional(), home_type: z.string().nullable().optional(), home_status: z.string().nullable().optional(), status_text: z.string().nullable().optional(), status_type: z.string().nullable().optional(), marketing_status: z.string().nullable().optional(), contingent_listing_type: z.string().nullable().optional(), price: z.number().int().nullable().optional(), price_raw: z.string().nullable().optional(), currency: z.string().nullable().optional(), price_change: z.number().int().nullable().optional(), date_price_changed_utc: z.number().nullable().optional(), date_price_changed_at: z.string().nullable().optional(), price_reduction: z.string().nullable().optional(), flex_field_text: z.string().nullable().optional(), zestimate: z.number().int().nullable().optional(), rent_zestimate: z.number().int().nullable().optional(), tax_assessed_value: z.number().int().nullable().optional(), beds: z.number().nullable().optional(), baths: z.number().nullable().optional(), living_area: z.number().int().nullable().optional(), lot_area_value: z.number().nullable().optional(), lot_area_unit: z.string().nullable().optional(), address: z.string().nullable().optional(), street_address: z.string().nullable().optional(), unit: z.string().nullable().optional(), city: z.string().nullable().optional(), state: z.string().nullable().optional(), zipcode: z.string().nullable().optional(), country: z.string().nullable().optional(), is_undisclosed_address: z.boolean().nullable().optional(), latitude: z.number().nullable().optional(), longitude: z.number().nullable().optional(), broker_name: z.string().nullable().optional(), provider_listing_id: z.string().nullable().optional(), days_on_zillow: z.number().int().nullable().optional(), is_zillow_owned: z.boolean().nullable().optional(), is_featured: z.boolean().nullable().optional(), is_showcase: z.boolean().nullable().optional(), is_fsba: z.boolean().nullable().optional(), is_new_construction: z.boolean().nullable().optional(), is_premier_builder: z.boolean().nullable().optional(), is_preforeclosure_auction: z.boolean().nullable().optional(), is_non_owner_occupied: z.boolean().nullable().optional(), img_src: z.string().nullable().optional(), has_image: z.boolean().nullable().optional(), has_video: z.boolean().nullable().optional(), has_3d_model: z.boolean().nullable().optional(), has_open_house: z.boolean().nullable().optional(), open_house_start: z.string().nullable().optional(), open_house_end: z.string().nullable().optional(), photos: z.array(z.string()).optional()}); export const property = z.object({zpid: z.string(), id: z.string().nullable().optional(), url: z.string().nullable().optional(), home_status: z.string().nullable().optional(), home_type: z.string().nullable().optional(), property_type: z.string().nullable().optional(), listing_type: z.string().nullable().optional(), posting_product_type: z.string().nullable().optional(), listing_data_source: z.string().nullable().optional(), mls_id: z.string().nullable().optional(), parcel_id: z.string().nullable().optional(), county_fips: z.string().nullable().optional(), provider_listing_id: z.string().nullable().optional(), broker_id: z.string().nullable().optional(), contingent_listing_type: z.string().nullable().optional(), listing_sub_type: listingSubType.nullable().optional(), price: z.number().int().nullable().optional(), currency: z.string().nullable().optional(), list_price_low: z.number().int().nullable().optional(), monthly_hoa_fee: z.number().int().nullable().optional(), property_tax_rate: z.number().nullable().optional(), annual_homeowners_insurance: z.number().int().nullable().optional(), last_sold_price: z.number().int().nullable().optional(), date_sold_utc: z.number().nullable().optional(), date_sold_at: z.string().nullable().optional(), price_change: z.number().int().nullable().optional(), price_change_date_utc: z.number().nullable().optional(), price_change_date_at: z.string().nullable().optional(), zestimate: z.number().int().nullable().optional(), rent_zestimate: z.number().int().nullable().optional(), zestimate_low_percent: z.string().nullable().optional(), zestimate_high_percent: z.string().nullable().optional(), rent_zestimate_low_percent: z.string().nullable().optional(), rent_zestimate_high_percent: z.string().nullable().optional(), zestimate_30_days_ago: z.number().int().nullable().optional(), rent_zestimate_30_days_ago: z.number().int().nullable().optional(), tax_assessed_value: z.number().int().nullable().optional(), zestimate_history: z.array(zestimateHistoryPoint).optional(), bedrooms: z.number().nullable().optional(), bathrooms: z.number().nullable().optional(), living_area: z.number().int().nullable().optional(), living_area_units: z.string().nullable().optional(), lot_size: z.number().int().nullable().optional(), lot_area_value: z.number().nullable().optional(), lot_area_units: z.string().nullable().optional(), year_built: z.number().int().nullable().optional(), move_in_ready: z.boolean().nullable().optional(), move_in_completion_date: z.string().nullable().optional(), latitude: z.number().nullable().optional(), longitude: z.number().nullable().optional(), street_address: z.string().nullable().optional(), abbreviated_address: z.string().nullable().optional(), city: z.string().nullable().optional(), state: z.string().nullable().optional(), zipcode: z.string().nullable().optional(), county: z.string().nullable().optional(), country: z.string().nullable().optional(), time_zone: z.string().nullable().optional(), neighborhood: z.string().nullable().optional(), is_undisclosed_address: z.boolean().nullable().optional(), is_income_restricted: z.boolean().nullable().optional(), days_on_zillow: z.number().int().nullable().optional(), time_on_zillow: z.string().nullable().optional(), page_view_count: z.number().int().nullable().optional(), favorite_count: z.number().int().nullable().optional(), tour_view_count: z.number().int().nullable().optional(), photo_count: z.number().int().nullable().optional(), description: z.string().nullable().optional(), what_i_love: z.string().nullable().optional(), home_insights: z.array(z.string()).optional(), marketing_name: z.string().nullable().optional(), brokerage_name: z.string().nullable().optional(), is_showcase_listing: z.boolean().nullable().optional(), has_vr_model: z.boolean().nullable().optional(), has_3d_model: z.boolean().nullable().optional(), virtual_tour_url: z.string().nullable().optional(), interactive_floor_plan_url: z.string().nullable().optional(), street_view_image_url: z.string().nullable().optional(), static_map_url: z.string().nullable().optional(), new_construction_type: z.string().nullable().optional(), builder_name: z.string().nullable().optional(), promotion_headline: z.string().nullable().optional(), promotion_description: z.string().nullable().optional(), has_promotion: z.boolean().nullable().optional(), address: address.nullable().optional(), home_facts: homeFacts.nullable().optional(), agent: agentAttribution.nullable().optional(), mortgage_rates: mortgageRates.nullable().optional(), price_history: z.array(priceHistoryEvent).optional(), tax_history: z.array(taxHistoryEvent).optional(), schools: z.array(school).optional(), photos: z.array(photo).optional(), open_house_schedule: z.array(openHouse).optional(), nearby_cities: z.array(nearbyRegion).optional(), nearby_neighborhoods: z.array(nearbyRegion).optional(), nearby_zipcodes: z.array(nearbyRegion).optional(), nearby_homes: z.array(listing).optional(), scraped_utc: z.number().nullable().optional(), scraped_at: z.string().nullable().optional()}); export const getApiV1ZillowPropertyZpidResponse = z.object({property: property.optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Get Property Detail * * GET /v1/zillow/property/{zpid} */ export const getApiV1ZillowPropertyZpid = async (args: GetApiV1ZillowPropertyZpidArgs, options?: GetApiV1ZillowPropertyZpidOptions) => { const {baseUrl = "https://scrapebadger.com", fetch: fetchFn = fetch, apiKey} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (apiKey !== undefined) headers.set("x-api-key", apiKey) const url = `${baseUrl.replace(/\/+$/, '')}/v1/zillow/property/${encodeURIComponent(String(args.zpid))}${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(getApiV1ZillowPropertyZpidResponse, await res.json()) };