diff --git a/README.md b/README.md index 4addbf2..d792f4e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Sign Up for an API Key -To use the ParclLabs API, you need an API key. To get an API key, sign up at [ParclLabs](https://dashboard.parcllabs.com/signup). +To use the Parcl Labs API, you need an API key. To get an API key, sign up at [ParclLabs](https://dashboard.parcllabs.com/signup). ## Installation @@ -13,6 +13,34 @@ You can install the package via pip: pip install parcllabs ``` +### Search +Search is your entry point into finding one or many of over 70,000 markets in the United States. You can search for markets by name, state, region, fips, or zip code. You can also search for markets by their Parcl ID. + +#### Search Markets +```python +import os + +from parcllabs import ParclLabsClient + + +api_key = os.getenv('PARCLLABS_API_KEY') +client = ParclLabsClient(api_key) + +# all cities in EAST_NORTH_CENTRAL census region +results = client.search.markets.retrieve_many( + location_type='CITY', + region='EAST_NORTH_CENTRAL', + as_dataframe=True +) +print(results.head()) +# parcl_id country geoid state_fips_code name state_abbreviation region location_type +# 0 5333443 USA 5500100 55 Abbotsford City WI EAST_NORTH_CENTRAL CITY +# 1 5387920 USA 1700113 17 Abingdon City IL EAST_NORTH_CENTRAL CITY +# 2 5403368 USA 5500275 55 Adams City WI EAST_NORTH_CENTRAL CITY +# 3 5278505 USA 2600440 26 Adrian City MI EAST_NORTH_CENTRAL CITY +# 4 5332624 USA 3901000 39 Akron City OH EAST_NORTH_CENTRAL CITY +``` + ### Rental Market Metrics #### Gross Yield diff --git a/parcllabs/__init__.py b/parcllabs/__init__.py index 1a142db..ba6a91e 100644 --- a/parcllabs/__init__.py +++ b/parcllabs/__init__.py @@ -34,3 +34,4 @@ ) from parcllabs.services.portfolio_metrics import PortfolioMetricsSFHousingStockOwnership +from parcllabs.services.search import SearchMarkets \ No newline at end of file diff --git a/parcllabs/__version__.py b/parcllabs/__version__.py index 8d91de2..9bef2a6 100644 --- a/parcllabs/__version__.py +++ b/parcllabs/__version__.py @@ -1 +1 @@ -VERSION = "0.1.7" +VERSION = "0.1.8" diff --git a/parcllabs/plabs_client.py b/parcllabs/plabs_client.py index 47abc9a..02c2bd4 100644 --- a/parcllabs/plabs_client.py +++ b/parcllabs/plabs_client.py @@ -29,7 +29,7 @@ ) from parcllabs.services.portfolio_metrics import PortfolioMetricsSFHousingStockOwnership - +from parcllabs.services.search import SearchMarkets class ParclLabsClient: def __init__(self, api_key: str): @@ -70,6 +70,7 @@ def __init__(self, api_key: str): self.portfolio_metrics_sf_housing_stock_ownership = ( PortfolioMetricsSFHousingStockOwnership(client=self) ) + self.search_markets = SearchMarkets(client=self) def get(self, url: str, params: dict = None): """ diff --git a/parcllabs/services/search.py b/parcllabs/services/search.py new file mode 100644 index 0000000..8175e39 --- /dev/null +++ b/parcllabs/services/search.py @@ -0,0 +1,197 @@ +from enum import Enum +from typing import Any, Mapping, Optional, List + +import pandas as pd + +from parcllabs.services.base_service import ParclLabsService + +valid_locations = [ + "COUNTY", + "CITY", + "ZIP5", + "CDP", + "VILLAGE", + "TOWN", + "CBSA", + "ALL", +] + +valid_regions = [ + "EAST_NORTH_CENTRAL", + "EAST_SOUTH_CENTRAL", + "MIDDLE_ATLANTIC", + "MOUNTAIN", + "NEW_ENGLAND", + "PACIFIC", + "SOUTH_ATLANTIC", + "WEST_NORTH_CENTRAL", + "WEST_SOUTH_CENTRAL", + "ALL", +] + +valid_state_abbreviations = [ + "AK", + "AL", + "AR", + "AZ", + "CA", + "CO", + "CT", + "DC", + "DE", + "FL", + "GA", + "HI", + "IA", + "ID", + "IL", + "IN", + "KS", + "KY", + "LA", + "MA", + "MD", + "ME", + "MI", + "MN", + "MO", + "MS", + "MT", + "NC", + "ND", + "NE", + "NH", + "NJ", + "NM", + "NV", + "NY", + "OH", + "OK", + "OR", + "PA", + "PR", + "RI", + "SC", + "SD", + "TN", + "TX", + "UT", + "VA", + "VI", + "VT", + "WA", + "WI", + "WV", + "WY", + "ALL", +] + +valid_state_fips_codes = [ + "01", + "02", + "04", + "05", + "06", + "08", + "09", + "10", + "11", + "12", + "13", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "53", + "54", + "55", + "56", + "60", + "66", + "69", + "72", + "78", + "ALL", +] + +class SearchMarkets(ParclLabsService): + """ + Gets weekly updated rolling counts of newly listed for sale properties, segmented into 7, 30, 60, and 90 day periods ending on a specified date, based on a given . + """ + + def _as_pd_dataframe(self, data: List[Mapping[str, Any]]) -> Any: + return pd.DataFrame(data) + + def retrieve( + self, + query: Optional[str] = None, + location_type: str = None, + region: str = None, + state_abbreviation: str = None, + state_fips_code: str = None, + parcl_id: int = None, + geoid: str = None, + params: Optional[Mapping[str, Any]] = None, + as_dataframe: bool = False, + ): + if location_type is not None and location_type not in valid_locations: + raise ValueError(f"location_type value error. Valid values are: {valid_locations}. Received: {location_type}") + + if region is not None and region not in valid_regions: + raise ValueError(f"region value error. Valid values are: {valid_regions}. Received: {region}") + + if state_abbreviation is not None and state_abbreviation not in valid_state_abbreviations: + raise ValueError(f"state_abbreviation value error. Valid values are: {valid_state_abbreviations}. Received: {state_abbreviation}") + + if state_fips_code is not None and state_fips_code not in valid_state_fips_codes: + raise ValueError(f"state_fips_code value error. Valid values are: {valid_state_fips_codes}. Received: {state_fips_code}") + + params = { + "query": query, + "location_type": location_type, + "region": region, + "state_abbreviation": state_abbreviation, + "state_fips_code": state_fips_code, + "parcl_id": parcl_id, + "geoid": geoid, + **(params or {}), + } + results = self._request( + url="/v1/search/markets", params=params + ) + + if as_dataframe: + return self._as_pd_dataframe(results.get('items')) + return results \ No newline at end of file