<jupyter_start><jupyter_text>Planet API Python Client This tutorial is an introduction to [Planet](https://www.planet.com)'s Data API using the official [Python client](https://github.com/planetlabs/planet-client-python), the `planet` module. RequirementsThis tutorial assumes familiarity with the [Python](https://python.org) programming language throughout. Python modules used in this tutorial are:* [IPython](https://ipython.org/) and [Jupyter](https://jupyter.org/)* [planet](https://github.com/planetlabs/planet-client-python)* [geojsonio](https://pypi.python.org/pypi/geojsonio)You should also have an account on the Planet Platform and retrieve your API key from your [account page](https://www.planet.com/account/). Useful links * [Documentation](https://planetlabs.github.io/planet-client-python/index.html)* [Planet Data API reference](https://www.planet.com/docs/reference/data-api/) This tutorial will cover the basic operations possible with the Python client, particularly those that interact with the Data API. The basic workflow for interacting with the Data API is:1. search item types based on filters1. activate assets1. download assets  Set upIn order to interact with the Planet API using the client, we need to import `planet.api`.<jupyter_code>from planet import api<jupyter_output><empty_output><jupyter_text>We next need to create a `ClientV1` object registered with our API key. The API key will be automatically read from the `PL_API_KEY` environment variable if it exists. If not it can be provided when creating the `api` object.<jupyter_code>client = api.ClientV1()
# client = api.ClientV1(api_key="<KEY>") <-- not a real key

# print client.auth.value<jupyter_output><empty_output><jupyter_text>`ClientV1` provides basic low-level access to Planet’s API. Only one `ClientV1` should be in existence for an application. The client is thread safe and takes care to avoid API throttling and also retries any throttled requests. Any exceptional HTTP responses are handled by translation to one of the `planet.api.exceptions` classes. We will also create a small helper function to print out JSON with proper indentation.<jupyter_code>import json

def p(data):
    print(json.dumps(data, indent=2))<jupyter_output><empty_output><jupyter_text>Let's also read in a GeoJSON geometry into a variable so we can use it during testing.<jupyter_code>with open("data/san-francisco.json") as f:
    geom = json.loads(f.read())<jupyter_output><empty_output><jupyter_text>SearchingWe can search for items that are interesting by using the `quick_search` member function. Searches, however, always require a proper request that includes a filter that selects the specific items to return as seach results. FiltersThe Planet Python client also includes `planet.api.filters` to assist in the creation of search filters.<jupyter_code>from planet.api import filters<jupyter_output><empty_output><jupyter_text>The possible filters include `and_filter`, `date_filter`, `range_filter` and so on, mirroring the options supported by the Planet API.<jupyter_code>from datetime import datetime
start_date = datetime(year=2017, month=1, day=1)

date_filter = filters.date_range('acquired', gte=start_date)
cloud_filter = filters.range_filter('cloud_cover', lte=0.1)
and_filter = filters.and_filter(date_filter, cloud_filter)
p(and_filter)<jupyter_output><empty_output><jupyter_text>In addition to the filter, a properly-constructed request also contains the list of item types that we want to select.<jupyter_code>item_types = ["REOrthoTile", "PSOrthoTile"]
req = filters.build_search_request(and_filter, item_types)
p(req)
res = client.quick_search(req)<jupyter_output><empty_output><jupyter_text>The results of `quick_search` can be handled in different ways, but most commonly the user will either iterating through the list of items (`items_iter`) or writing items to a GeoJSON file (`json_encode`).In either case, the number of items must be specified.<jupyter_code>for item in res.items_iter(4):
    print item['id'], item['properties']['item_type']<jupyter_output><empty_output><jupyter_text>If the number of items requested is more than 250, the client will automatically fetch more pages of results in order to get the exact number requested.<jupyter_code>with open('output/results.json','w') as f:
    res.json_encode(f,1000)<jupyter_output><empty_output><jupyter_text>This GeoJSON file can be opened and viewed in any compatible application.  Assets and downloadsAfter a search returns results, the Python client can be used to check for assets and initiate downloads.The list of assets for an item can be retrieved with `get_assets` or `get_assets_by_id`.<jupyter_code>print item['id']
assets = client.get_assets(item).get()
for asset in sorted(assets.keys()):
    print asset
activation = client.activate(assets['analytic'])
activation.response.status_code<jupyter_output><empty_output><jupyter_text>A response of 202 means that the request has been accepted and the activation will begin shortly. A 204 code indicates that the asset is already active and no further action is needed. A 401 code means the user does not have permissions to download this file.Below, we are polling the API until the item is done activation. This may take awhile.<jupyter_code>import time

asset_activated = False

while asset_activated == False:
    
    # Get asset and its activation status
    assets = client.get_assets(item).get()
    asset = assets.get('analytic')
    asset_status = asset["status"]
    
    # If asset is already active, we are done
    if asset_status == 'active':
        asset_activated = True
        print("Asset is active and ready to download")
   
    # Still activating. Wait and check again.
    else:
        print("...Still waiting for asset activation...")
        time.sleep(3)
callback = api.write_to_file(directory='output/')
body = client.download(assets['analytic_xml'], callback=callback)
body.await()<jupyter_output><empty_output><jupyter_text>Saved SearchesThe Python API client can also help in managing saved searches on the Planet Platform.<jupyter_code>searches = client.get_searches()
for search in searches.items_iter(100):
    print search['id'], search['name']
item_types = ["PSScene3Band"]
san_francisco_filter = filters.geom_filter(geom)
req = filters.build_search_request(san_francisco_filter, item_types, name="San Francisco")
p(req)
res = client.create_search(req)
search = res.get()
print search["id"], search["name"]
res = client.saved_search(search["id"])
for item in res.items_iter(20):
    print item["id"], item["properties"]["view_angle"]<jupyter_output><empty_output><jupyter_text>StatisticsThe Python API client can also help report statistical summaries of the amount of data in the Planet API.<jupyter_code>item_types = ["PSScene3Band"]
san_francisco_filter = filters.geom_filter(geom)
req = filters.build_search_request(san_francisco_filter, item_types, interval="year")
stats = client.stats(req).get()
p(stats)
assets = client.get_assets(item)
assets.last_modified()
assets.get()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09396636993076163, 'fraction_numerical': 0.005934718100890208, 'mean_word_length': 5.918866080156403, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 54, 'https://': 10, 'lorem ipsum': 0, 'www.': 3, 'xml': 1}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '45254', 'license': 'Apache-2.0', 'n_tokens_mistral': 2104, 'n_tokens_neox': 2030, 'n_words': 709, 'path': 'jupyter-notebooks/data-api-tutorials/planet_cli_introduction.ipynb', 'repo_name': 'patrickcgray/notebooks', 'size': '7088'}