Skip to main content

Search Tutorial

Overview

This document describes how to use the Discovery REST API.

Sample requests to the API are demonstrated using curl commands in a bash shell. This is a low-level way of using the API and some of the curl commands may be difficult to understand at first. But using curl has the benefit of showing the exact URLs and query parameters necessary to use the API.

For application development a higher-level client library will generally be used. Possible options are the maxar-platform Python package or a client library generated directly from the API's OpenAPI specification.

Introduction

The Discovery API provides methods for requesting collections or items and performing searches. For most users the search method will be the one most often used.

Base URL:

https://api.maxar.com/discovery/v1

Collections

Catalog items are organized into collections. See the STAC specification for a description of collections and their properties.

Searching collections

Pagination is supported through the GET /collections endpoint with the sortby, limit, and page parameters.

The value of sortby is a SQL-style ordering clause. However only two properties are supported for ordering: datetime and id. Each may have an optional ASC or DESC modifier, where ASC is the default and may be omitted.

The value of the limit parameter supports a maximum value of 100 currently.

curl -s -H "$auth_header" "https://api.maxar.com/discovery/v1/collections?sortby=datetime+DESC&limit=5&page=50" 
    {
"collections": [{
"id": "Vivid_Advanced_Barrie_CA_22Q4",
"links": [{
"rel": "alternate",
"href": "https://api.maxar.com/discovery/v1/mosaic-item.json",
"type": "application/json",
"title": "Vivid_Advanced_Barrie_CA_22Q4"
}, {
"rel": "items",
"href": "https://api.maxar.com/discovery/v1/collections/Vivid_Advanced_Barrie_CA_22Q4/items",
"type": "application/geo+json"
}],
"title": "Vivid_Advanced_Barrie_CA_22Q4",
"extent": {
"spatial": {
"bbox": [
[-79.8046875, 44.2529296875, -79.584960938, 44.47265625]
]
},
"temporal": {
"interval": [
["2022-09-08T16:17:02.530000Z", "2022-09-08T16:18:06.180000Z"]
]
}
},
"license": "proprietary",
"keywords": ["maxar", "flame"],
"providers": [{
"url": "https://www.maxar.com",
"name": "maxar"
}],
"properties": {
"title": "Vivid_Advanced_Barrie_CA_22Q4",
"datetime": "2022-10-25T03:02:09Z",
"resolution": 0.3
},
"description": "STAC collection for mosaic Vivid_Advanced_Barrie_CA_22Q4",
"stac_version": "0.9.0"
}],
"numberReturned": 5,
"timestamp": "2022-11-14T19:50:23.956505Z",
"links": [{
"rel": "next",
"href": "https://api.maxar.com/discovery/v1/collections?limit=5&sortby=datetime+DESC&page=51",
"type ": "application/geo+json"
}]
}

Viewing a collection

The canonical path of a collection is /collections/{collectionId}.

Perform a GET on this path to see a collection's definition. For example here is the definition of the "wv02" collection:

curl -s -H "$auth_header" https://api.maxar.com/discovery/v1/collections/wv02 | jq
{
"id": "wv02",
"type": "Collection",
"links": [
{
"rel": "root",
"href": "https://api.maxar.com/discovery/v1/dg-archive.json",
"type": "application/json"
},
{
"rel": "items",
"href": "https://api.maxar.com/discovery/v1/wv02/items",
"type": "application/geo+json"
},
{
"rel": "data-sheet",
"href": "https://dgv4-cms-production.s3.amazonaws.com/uploads/document/file/130/WorldView2-DS-WV2-rev2.pdf",
"type": "application/pdf"
}
],
"title": "WorldView-2",
"assets": {},
"extent": {
"spatial": {
"bbox": [],
"description": "Direction of image scan."
},
"view:off_nadir": {
"range": {
"maximum": 90,
"minimum": 0
},
"description": "Average angle between the nadir (the point right above the target) and the vector between the sensor and the target. Values in degrees."
},
"off_nadir_start": {
"range": {
"maximum": 90,
"minimum": 0
},
"description": "Start angle between the nadir (the point right above the target) and the vector between the sensor and the target. Values in degrees."
},
"collect_time_end": {
"description": "ISO-8601 formatted UTC datetime string of when the collection of the image ended."
},
"view:sun_azimuth": {
"range": {
"maximum": 360,
"minimum": 0
},
"description": "Average of the angle between the vector pointing true North from the sample point and the sun's vector on the horizontal plane. Values in degrees."
},
"raw_archive_state": {
"values": [
"offline",
"online"
],
"description": "Current state of the image in raw archive."
},
"collect_time_start": {
"description": "ISO-8601 formatted UTC datetime string of when the collection of the image began."
},
"pan_resolution_avg": {
"description": "Average size of pixel when mapped to the ground for pan bandsets. Values in meters."
},
"pan_resolution_end": {
"description": "End size of pixel when mapped to the ground for pan bandsets. Values in meters."
},
"pan_resolution_max": {
"description": "Maximum size of pixel when mapped to the ground for pan bandsets. Values in meters."
},
"pan_resolution_min": {
"description": "Minimum size of pixel when mapped to the ground for pan bandsets. Values in meters."
},
"view:sun_elevation": {
"range": {
"maximum": 90,
"minimum": -90
},
"description": "Angle of the sun above the horizon, viewed from the given sample point. Values in degrees."
},
"multi_resolution_avg": {
"description": "Average size of pixel when mapped to the ground for multispectral bandsets. Values in meters."
},
"multi_resolution_end": {
"description": "End size of pixel when mapped to the ground for multispectral bandsets. Values in meters."
},
"multi_resolution_max": {
"description": "Maximum size of pixel when mapped to the ground for multispectral bandsets. Values in meters."
},
"multi_resolution_min": {
"description": "Minimum size of pixel when mapped to the ground for multispectral bandsets. Values in meters."
},
"pan_resolution_start": {
"description": "Start size of pixel when mapped to the ground for pan bandsets. Values in meters."
},
"acquisition_rev_number": {
"description": "Revolution number of the satellite in which the image was acquired/collected"
},
"multi_resolution_start": {
"description": "Start size of pixel when mapped to the ground for multispectral bandsets. Values in meters."
},
"stereo_pair_identifiers": {
"description": "List of stereo pair identifiers that the image is part of."
},
"spacecraft_to_target_azimuth_avg": {
"range": {
"maximum": 360,
"minimum": 0
},
"description": "Average value of the angle between the vector pointing true North from the spacecraft and the sample point's vector on the horizontal plane. Values in degrees."
},
"spacecraft_to_target_azimuth_end": {
"range": {
"maximum": 360,
"minimum": 0
},
"description": "End value of the angle between the vector pointing true North from the spacecraft and the sample point's vector on the horizontal plane. Values in degrees."
},
"spacecraft_to_target_azimuth_max": {
"range": {
"maximum": 360,
"minimum": 0
},
"description": "Average value of the angle between vector pointing true North from the sample point and the spacecraft's vector on the horizontal plane. Values in degrees."
},
"target_to_spacecraft_azimuth_end": {
"range": {
"maximum": 360,
"minimum": 0
},
"description": "End value of the angle between the vector pointing true North from the sample point and the spacecraft's vector on the horizontal plane. Values in degrees."
},
"target_to_spacecraft_azimuth_max": {
"range": {
"maximum": 360,
"minimum": 0
},
"description": "Maximum value of the angle between the vector pointing true North from the sample point and the spacecraft's vector on the horizontal plane. Values in degrees."
},
"target_to_spacecraft_azimuth_min": {
"range": {
"maximum": 360,
"minimum": 0
},
"description": "Minimum value of the angle between the vector pointing true North from the sample point and the spacecraft's vector on the horizontal plane. Values in degrees."
},
"spacecraft_to_target_azimuth_start": {
"range": {
"maximum": 360,
"minimum": 0
},
"description": "Start value of the angle between the vector pointing true North from the sample point and the spacecraft's vector on the horizontal plane. Values in degrees."
},
"target_to_spacecraft_elevation_avg": {
"range": {
"maximum": 90,
"minimum": 0
},
"description": "Average angle of the spacecraft above the horizon, viewed from sample point. Values in degrees."
},
"target_to_spacecraft_elevation_end": {
"range": {
"maximum": 90,
"minimum": 0
},
"description": "End angle of the spacecraft above the horizon, viewed from sample point. Values in degrees."
},
"target_to_spacecraft_elevation_max": {
"range": {
"maximum": 90,
"minimum": 0
},
"description": "Maximum angle of the spacecraft above the horizon, viewed from sample point. Values in degrees."
},
"target_to_spacecraft_elevation_min": {
"range": {
"maximum": 90,
"minimum": 0
},
"description": "Minimum angle of the spacecraft above the horizon, viewed from sample point. Values in degrees."
},
"target_to_spacecraft_elevation_start": {
"range": {
"maximum": 90,
"minimum": 0
},
"description": "Start angle of the spacecraft above the horizon, viewed from sample point. Values in degrees."
}
},
"properties": {
"eo:platform": "worldview-02",
"eo:instrument": "VNIR",
"eo:constellation": "maxar"
},
"description": "WorldView-2 Imagery",
"stac_version": "1.0.0"
}

Items

The canonical path to an item is /collections/{collectionId}/items/{itemId}. So to directly GET an item this way you must know its collection. See below for documentation of the /search endpoint where items may be queried by id without regard to collection.

Here is an example of getting the image 1020010010DD0000 in the wv01 collection:

curl -s -H "$auth_header" https://api.maxar.com/discovery/v1/collections/wv01/items/1020010010DD0000 | jq
 {
"id": "1020010010DD0000",
"bbox": [
-71.770808,
-74.794935,
-71.010839,
-74.71916
],
"type": "Feature",
"links": [
{
"rel": "collection",
"href": "https://api.maxar.com/discovery/v1/collections/wv01"
},
{
"rel": "root",
"href": "https://api..maxar.com/discovery/v1/collections/dg-archive"
},
{
"rel": "self",
"href": "https://api.maxar.com/discovery/v1/collections/wv01/items/1020010010DD0000"
}
],
"assets": {
"browse": {
"href": "https://api.maxar.com/discovery/v1/collections/wv01/items/1020010010DD0000/assets/collections/dg-archive/assets/browse/1020010010DD0000.browse.tif",
"type": "image/tiff; application=geotiff",
"title": "Browse Image"
},
"cloud-cover": {
"href": "https://api.maxar.com/discovery/v1/collections/wv01/items/1020010010DD0000/assets/collections/wv01/assets/cloud-cover/1020010010DD0000.cloud.json",
"type": "application/geo+json",
"title": "Cloud Cover"
},
"sample-point-set": {
"href": "https://api.maxar.com/discovery/v1/collections/wv01/items/1020010010DD0000/assets/collections/wv01/assets/sample-point-sets/1020010010DD0000.sample-points.json",
"type": "application/geo+json",
"title": "Sample Point Set"
}
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-71.770808,
-74.727724
],
[
-71.770734,
-74.794935
],
[
-71.010839,
-74.786512
],
[
-71.010931,
-74.71916
],
[
-71.770808,
-74.727724
]
]
]
},
"collection": "wv01",
"properties": {
"gsd": 0.707835555077,
"title": "DigitalGlobe WV01 Image 1020010010DD0000",
"datetime": "2011-02-09T13:11:08.974402Z",
"eo:bands": [
{
"name": "pan",
"center_wavelength": 650
}
],
"platform": "worldview-01",
"instruments": [
"VNIR"
],
"associations": [],
"view:azimuth": 6.24952793121,
"constellation": "digitalglobe",
"off_nadir_avg": 32.9030418396,
"off_nadir_end": 32.7110824585,
"off_nadir_max": 33.0951957703,
"off_nadir_min": 32.7110824585,
"rda_available": false,
"eo:cloud_cover": 98.9121283347,
"scan_direction": "reverse",
"view:off_nadir": 32.9030418396,
"off_nadir_start": 33.0951957703,
"collect_time_end": "2011-02-09T13:11:09.390358Z",
"view:sun_azimuth": 61.6186828613,
"collect_time_start": "2011-02-09T13:11:08.974402Z",
"pan_resolution_avg": 0.707835555077,
"pan_resolution_end": 0.705121338367,
"pan_resolution_max": 0.710432112217,
"pan_resolution_min": 0.705121338367,
"view:sun_elevation": 22.5309238434,
"multi_resolution_avg": null,
"multi_resolution_end": null,
"multi_resolution_max": null,
"multi_resolution_min": null,
"pan_resolution_start": 0.710432112217,
"acquisition_rev_number": 18885,
"multi_resolution_start": null,
"stereo_pair_identifiers": [
"67aff054-1d95-4c34-8ab2-f07a844d93af-inv"
],
"spacecraft_to_target_azimuth_avg": 6.24952793121,
"spacecraft_to_target_azimuth_end": 6.47290420532,
"spacecraft_to_target_azimuth_max": 6.47290420532,
"spacecraft_to_target_azimuth_min": 6.02687835693,
"target_to_spacecraft_azimuth_avg": null,
"target_to_spacecraft_azimuth_end": null,
"target_to_spacecraft_azimuth_max": null,
"target_to_spacecraft_azimuth_min": null,
"spacecraft_to_target_azimuth_start": 6.02687835693,
"target_to_spacecraft_azimuth_start": null,
"target_to_spacecraft_elevation_avg": null,
"target_to_spacecraft_elevation_end": null,
"target_to_spacecraft_elevation_max": null,
"target_to_spacecraft_elevation_min": null,
"target_to_spacecraft_elevation_start": null
},
"stac_version": "1.0.0",
"stac_extensions": [
"eo",
"view",
"https://schemas.maxar.com/discovery/v1/schemas/maxar-stac-item-extension-schema-1-0.json"
]
}

See the STAC item specification for descriptions of the properties of a STAC item.

Search overview

This section describes the API's search command. It provides query parameters for searching the catalog in a variety of ways including area of interest, time, and by arbitrary STAC item properties.

Search method endpoints

There are two endpoints that can be used to search the catalog:

  • /search
  • /collections/{collectionId}/items

The /search endpoint is the primary method for searching. But every collection also has its own endpoint for searching only its items. Both of these endpoints support the same query parameters except for collections. The collection-specific endpoint does not support the collections query parameter since it is intended for searching only items in its collection, as specified by collectionId in the URL's path. If you are searching on a particular collection there is no real difference in calling search and using the collections query parameter, or calling the collection-specific search endpoint.

The rest of this document will describe calling the /search method. Everything applies to the collection-specific search endpoint as well, except for the handling of the collections query parameter.

Query parameters

Query parameters supported by the search methods are described in the STAC specification.

The following parameters are supported:

ParameterDescription
collectionsList of collections to search within.
idsComma-separated list of STAC item IDs to return. The items returned are still subject to whatever other search filters are specified.
bboxBounding box in format "west,south,east,north" in WGS84 decimal degrees. When performing a spatial search specify either of the parameters "bbox" of "intesects", but not both.
intersectsGeoJSON geometry to search by. Only STAC items whose geometries intersect this geometry are selected. When performing a spatial search specify either of the parameters "bbox" of "intesects", but not both.
datetimeDatetime interval, either closed or open ended.
filterA cql2 filter expression for filtering items. GET requests use cql2-text expressions while POST requests require cql2-json. You can filter on any property inside a STAC item's "properties" object.
sortbyAn array of property names, prefixed by either "+" for ascending or "-" for descending. If no prefix is provided, "+" is assumed. Currently only two properties are supported: datetime and id.
limitMaximum number of items to return.
pagePage number to retrieve, starting from page 1

The bbox and intersects parameters are mutually exclusive.

The bbox parameter may include elevation values as the STAC specification allows, but the search methods currently ignore them.

The ids parameter is generally used by itself to retrieve one or more particular features. Additional parameters may be specified along with ids to further restrict the query.

Note: this is different behavior from what is described in the STAC API specification.

As mentioned earlier the collections parameter may not be used when searching on a collection-specific endpoint.

Details

The search methods always return a GeoJSON FeatureCollection, even if the response contains only a single feature. If no features satisfy the search criteria then the response is a GeoJSON FeatureCollection with an empty features array.

There is a 30-second timeout on all queries. A query that times out will generally return an HTTP status code of 504.

The search methods support three different ways of specifying query parameters:

  • GET using URL query parameters
  • POST using form parameters (Content-Type application/x-www-form-urlencoded)
  • POST using JSON body containing query parameters (Content-Type application/json)

The examples later in this document demonstrate each of these methods.

The query parameters collections, ids, and bbox each specify a list of values. When using the first two query methods specify a string with comma-separated values. But when posting a JSON body specify a JSON array.

When using the intersects query parameter you should typically use the third method of posting a JSON object, since a GeoJSON geometry can be large. It may be easier to work with as a property inside a JSON object. It is possible to pass an intersects query parameter using the first two methods, in which case its value is a JSON-encoded string.

Paging and ordering

The search methods always page their results and are currently configured with a page size of 100 features. The search method's limit and page query parameters can be used to repeatedly request result pages until the full results of a search are received.

Catalog search does not currently handle paging the way it is described in the current STAC API specification. The specification describes returning a link in the response to a URL for fetching the next page of results. If this remains in the API's version 1.0 release then the catalog will implement it then.

There is always an ordering applied to the results of any query since it's necessary for paging to work. This default is descending order by the standard STAC item property "datetime". In the unlikely event that two STAC items have the same datetime value the item id is used as a secondary sort property.

The search endpoints support a sortby query parameter for providing a different ordering. The value of sortby is a SQL-style ordering clause. However only two properties are supported for ordering: datetime and id. Each may have an optional ASC or DESC modifier, where ASC is the default and may be omitted.

These are examples of allowed sortby values:

  • datetime DESC, id (default ordering if none specified)
  • datetime ASC
  • id ASC
  • id DESC

The sortby parameter does not support ordering by arbitrary STAC item properties, such as eo:cloud_cover. Clients that need such ordering have to perform it on the client side.

Efficient searches using spatial and temporal filters

A STAC catalog is intended to be searched spatially and/or temporally. It is not required that a search include a spatial and/or temporal component, but searches that don't may time out before they are able to complete. It is important to provide either a spatial or temporal filter to cut down the search's set of candidate features before the filter clause is applied.

A spatial search is performed by specifying either the bbox or intersects query parameters. The bbox parameter is for simple spatial searches on a bounding box of west, east, south, and north coordinates. The intersects parameter is for searching on an arbitrary GeoJSON geometry. In either case a catalog feature satisfies the spatial query if it intersects the bounding box or geometry.

Temporal searches are performed on the datetime property that every STAC item is required to have.

Datetime values in STAC items and queries are always required to have the format "yyyy-mm-ddThh:mm:ssZ" (see RFC 3339 Section 5.6). The seconds may have a fractional component as well.

The value of the datetime query parameter can specify either a closed time range or an open-ended time range, with a slash indicating how its value is interpreted:

datetime query valueDescription
start-time/end-timeFeature datetime between start-time (inclusive) and end-time (exclusive)
start-time/Feature datetime greater than or equal to start-time
/end-timeFeature datetime less than end-time

There are some searches that can be performed efficiently without a spatial or temporal component. Searching by item ID using the ids query parameter is always fast. Searching on a single collection with no other query parameter will be fast, with features returned by the default ordering of decreasing datetime.

Performing a query using only the filter query parameter will not be efficient and will likely timeout.

Specifying collections when searching

A query against the /search endpoint operates on all collections in the Discovery catalog. Many users will want to search only for satellite imagery, but might get unexpected results since the catalog contains geospatial products besides just imagery from Maxar satellites. For example the catalog contains collections for mosaics, imagery stereo pairs, digital elevation models (DEM), and other products.

If you are only searching for imagery you should do this in one of two ways. You can use the endpoint /collections/{collectionId}/items to search for images from one particular satellite. Or you can use the /search endpoint and specify the collections query parameter, which is a list of collections to search. When searching for imagery one of these two methods should be used to avoid receiving unexpected STAC items in the response from unrelated collections.

In the common situation where you want to search the catalog for any Maxar satellite imagery you should specify the collections to search as "ge01", "wv01", "wv02", "wv03-vnir", "wv04", "lg01", "lg02", "lg03", "lg04".

All OGC services accept an API key or a bearer token for authentication.

Bearer tokens

Oauth bearer tokens can also be used to authenticate API requests. Bearer tokens have a brief duration and must be refreshed frequently.

To authenticate with a bearer token, use the Authorization header:

Authorization: Bearer <Oauth2 token>

Learn about OAuth2 bearer tokens

API keys

API keys can be passed using query parameters or in a custom header. No Authorization header is needed when using API keys.

To use the query parameter method, include this query parameter in your request:

maxar_api_key=<your API key>

The custom header takes the form of:

maxar-api-key: <your API key>

API keys expire 180 days from creation by default. A custom expiration date can be set but cannot exceed 180 days.

Learn about API keys

Search examples

Below are examples of performing catalog searches using the various query parameters.

The GeoJSON feature collections returned by searches are generally large so they are not included in full in the output. Instead the jq command is used to filter out certain properties to demonstrate that the searches produced some kind of expected value. Remove the jq commands from the examples and run them yourself to see what the full output looks like.

Search by ID

One of the simplest searches is by ID.

The query parameter ids is used to search by item ID. This is the easiest way to lookup a particular image in the catalog since it is not necessary to know the collection it belongs to.

Here is a search on a particular ID:

curl -s -H "$auth_header" 'https://api.maxar.com/discovery/v1/search?ids=1020010079424900' | \
jq -r '.features[] | [.id, .properties.datetime, .properties.platform] | @csv'
    "1020010079424900","2018-09-07T20:57:58.335106Z","worldview-01"

Any number of item IDs can be specified in the ids parameter. Here is an example of searching on multiple IDs by doing a POST with a form parameter:

curl -s -H "$auth_header" -d 'ids=102001009C00D000,10300100AEB46000,104001006215C400' 'https://api.maxar.com/discovery/v1/search' | \
jq -r '.features[] | [.id, .properties.datetime, .properties.platform] | @csv'
    "10300100AEB46000","2020-10-17T20:32:44.965746Z","worldview-02"
"102001009C00D000","2020-10-17T20:01:45.956688Z","worldview-01"
"104001006215C400","2020-10-17T19:38:34.841756Z","worldview-03"

It is not an error to search on an id that does not exist in the catalog. It is also not an error to search on an id of a feature that the caller does not have permission to access. The returned GeoJSON feature collection will contain whatever features match the requested set of IDs, with no mention of id values that don't exist or that the user does not have permission to access.

Search by location and datetime

This example demonstrates the commonly performed search involving a collection, bounding box, and acquisition date. The search performs a GET using the following query parameters:

  • collections: Search only for WorldView-1 and WorldView-2 images.
  • bbox: Search in the bounding box west=-105, east=-104, south=40, north=41 degrees.
  • datetime: Search for datetime in the range Jan 1, 2020 to Jan 7, 2020.

First perform the search and save the result in a variable:

features=$(curl -s -H "$auth_header" 'https://api.maxar.com/discovery/v1/search?collections=wv01,wv02&bbox=-105,40,-104,41&datetime=2020-01-01/2020-01-07')

The bounding box and date range are large enough that it's likely the query hit the maximum page size of 100 features:

echo "$features" | jq '.features | length'

Verify that only WorldView-1 and WorldView-2 images were returned:

echo "$features" | jq -r '.features[] | .properties.platform' | sort | uniq
    worldview-02

Verify that datetime values in the returned features are in the expected range. Note that if the sortby query parameter is not specified, returned features are in descending order by their datetime property:

echo "$features" | jq -r '.features[] | .properties.datetime' | sort | head -1
echo "$features" | jq -r '.features[] | .properties.datetime' | sort | tail -1
    2020-01-02T18:01:15.140202Z
2020-01-02T18:02:20.189742Z

Search using ordering

The above example used the default order enforced by the service: descending by datetime, ascending by id.

Here is the same query but in ascending order by datetime. Note that now the datetime values in the first page of returned results are from the beginning of the date range rather than the end:

features=$(curl -s -H "$auth_header" 'https://api.maxar.com/discovery/v1/search?collections=wv01,wv02&bbox=-105,40,-104,41&datetime=2020-01-01/2020-01-07&sortby=datetime')
echo "$features" | jq -r '.features[] | .properties.datetime' | sort | head -1
echo "$features" | jq -r '.features[] | .properties.datetime' | sort | tail -1
    2020-01-02T18:01:15.140202Z
2020-01-02T18:02:20.189742Z

Since the default ordering returns newest images first, you can easily find images recently added to the catalog. For example this query returns the most recently added WorldView-2 image in the catalog (at the time of this writing):

curl -s -H "$auth_header" 'https://api.maxar.com/discovery/v1/search?collections=wv02&limit=1' | \
jq -r '.features[] | [.id, .properties.datetime] | @csv'
    "10300100DDD53800","2022-11-14T19:02:08.750085Z"

Search by geometry

The query parameter "intersects" allows an arbitrary GeoJSON geometry to be specified. Any item whose geometry intersects this shape satisfies the query. Any type of geometry may be used; it does not have to be a polygon.

Here is an example that follows from the previous query. Arbitrarily select the first item returned above and use its geometry as an intersection polygon to query on. Search against all of the standard imagery collections. The result is all images that intersect the given geometry.

This example also demonstrates calling the search endpoint by performing a POST of a JSON object, where the query parameters are object properties. This is useful when using the intersects query parameter since a GeoJSON geometry can be large and is awkward to pass as a GET query parameter.

# Get the GeoJSON geometry of the first feature returned from the previous example
footprint=$(echo "$features" | jq -r '.features[0].geometry')

# Create JSON with the query parameters
body=$(jq <<EOF
{
"intersects": $footprint,
"collections": ["ge01", "wv01", "wv02", "wv03-vnir", "wv04"]
}
EOF
)

# Pretty-print the query parameters
echo "$body"

# POST to /search passing in body on stdin
surrounding_features=$(echo "$body" | curl -s --data @- -H "Content-Type: application/json" -H "$auth_header" 'https://api.maxar.com/discovery/v1/search')

    {
"intersects": {
"type": "Polygon",
"coordinates": [
[
[
-104.813772,
41.956477
],
[
-104.813915,
41.898241
],
[
-104.813968,
41.840149
],
[
-104.813949,
41.782301
],
(EXTRA VERTICES OMITTED FOR CLARITY)
]
]
},
"collections": [
"ge01",
"wv01",
"wv02",
"wv03-vnir",
"wv04",
"lg01",
"lg02",
"lg03",
"lg04",

]
}

Count the number of features returned:

echo "$surrounding_features" | jq '.features | length'

Search by properties

The filter query parameter allows a filter clause to be applied to STAC item properties. Free-form SQL-style queries such as these can be applied:

  • eo:cloud_cover < 20
  • (gsd < 0.5) and (view:azimuth < 50)
  • title like '%DigitalGlobe%'

Filters in a filter parameter are logically applied after any other query parameters. The query parameters ids, collections, bbox, intersects, and datetime can all be considered to be applied first. Whatever features are selected by these primary filters then have the filter clause applied to them.

Only values in a STAC item's properties object may be filtered using the filter parameter. JSON properties at the "top level" of a STAC item, like stac_version and assets, cannot be queried this way.

Searching by a filter clause also does not work on properties whose values are arrays or objects. See this example STAC specification for examples with the property eo:bands.. The filter clause does not support any syntax for querying such nested properties.

CQL2-text Syntax

This section describes the CQL2-text syntax used by the filter query parameter in GET requests.

Here are a few general rules:

  • Strings must be delimited by single quotes. There is no support for escape sequences inside strings.
  • String comparisons are always case-sensitive.
  • Keywords and literals such as null, true, false, like, etc. are not case sensitive.
  • Some common STAC item properties contain a prefix and colon, e.g. eo:cloud_cover.

There is no special handling required for these property names because of the colon.

The following clauses are allowed. Note that property names are always on the left side:

  • property is null
  • property is not null
  • property is bool (where bool is true or false.
  • property operator value (where value is a number or string)
  • property like string
  • property not like string
  • property in list (where list is a parenthesis-delimited list of numbers or strings)

These are the operators allowed between property and value:

  • =
  • !=
  • <> (Same as !=)
  • <
  • >
  • <=
  • >=

A simple example would be:

eo:cloud_cover < 10

Logical operators can be used to filter on multiple values:

eo:cloud_cover < 10 AND gsd < 0.5

CQL2-JSON syntax

Search requests POSTed to the search endpoint with a filter object use CQL2-JSON filters. These are JSON objects that have op (short for "operator") and args (short for "arguments") properties.

The CQL2-text example eo:cloud_cover < 10 would translate to the following CQL2-JSON object:

{
"op": "<",
"args": [
{ "property": "eo:cloud_cover" },
10
]
}

The logical AND used in eo:cloud_cover < 10 AND gsd < 0.5 is used as an operator, with the two filter statements as arguments:

{
"op": "and",
"args": [
{
"op": "<",
"args": [
{ "property": "eo:cloud_cover" },
10
]
},
{
"op": "<",
"args": [
{ "property": "gsd" },
0.5
]
}
]
}

While less readable, CQL2-JSON filters are more expressive and robust to program against.

See these examples or refer to the CQL2 specification

Examples

This example repeats one from earlier that searched on collections, bounding box, and datetime range. This time a where-clause is added for filtering on the properties eo:cloud_cover and view:off_nadir:

features=$(
curl -s -H "$auth_header" \
-d 'collections=wv01,wv02' \
-d 'bbox=-105,40,-104,41' \
-d 'datetime=2020-01-01/2020-01-07' \
-d 'filter=view:off_nadir < 5 and eo:cloud_cover < 10' \
'https://api.maxar.com/discovery/v1/search')

The original query hit the page limit of 100 features since the query on collections, bounding box, and datetime range was so broad. Adding the filter clause cuts down on the number of returned features:

echo "$features" | jq '.features | length'

Querying area-based properties

The Discovery API supports an extension for "area-based properties". These are properties calculated on the fly during a search based on the query's area of interest (in either the "bbox" or "intersects" query parameter.)

The API supports two area-based properties:

  • area:cloud_cover_percentage
  • area:avg_off_nadir_angle

The idea behind this extension is that cloud cover and off-nadir angles vary across an image, sometimes significantly, and users are only really interested in these properties where an image intersects their area of interest.

To calculate area-based properties pass the query parameter area-based-calc with the value true. For GET requests, provide it as a query string parameter. For POST requests, include in the JSON payload.

Only items found in the imagery sub-catalog/collection and its descendants (wv-02 and other sensors) can compute area-based properties since the calculations depend on items having cloud and sample point assets. Other collections of images such as cloud-optimized-archive can not calculate area-based properties and will return null values in these fields. This can cause confusion when making comparisons since null < 5 is considered a valid and true statement in some programming languages. This can lead to assumptions that an item with no cloud data has a zero percent cloud cover.

Example

Consider a query on imagery collections using the bounding box (-106.1, 38, -105.8, 38.2). We include the query parameter area-based-calc=true:

curl -s -H "$auth_header" 'https://api.maxar.com/discovery/v1/search' \
-d 'collections=imagery' \
-d 'bbox=-106.1,38,-105.8,38.2'
-d 'area-based-calc=true'

Many images will be returned but we will consider the WorldView-2 image 10300100E97D1100 as an example. Here is the image shown with the bounding box in blue, cloud polygons in red, and sample points in green:

ABC image

The returned GeoJSON feature for image 10300100E97D1100 looks something like this. Note the two new area: fields have been added to the item properties:

{
"id": "10300100E97D1100",
"bbox": [-106.068367, 38.000641, -105.852665, 38.606203],
"type": "Feature",
"links": [...],
"assets": {...},
"geometry": {...},
"properties": {
"title": "Maxar WV02 Image 10300100E97D1100",
"datetime": "2023-07-02T17:49:32.327760Z",
"gsd": 0.509,
"view:off_nadir": 18.46069,
"eo:cloud_cover": 31.948,
"area:avg_off_nadir_angle": 18.18528775,
"area:cloud_cover_percentage": 2.091,
...
}
}

This image has about 32% cloud cover, but the part of the image that intersects the bounding box is mostly cloud free with only 2% cloud cover.

The image doesn't label the off nadir angles of the sample points, but the value for the property area:avg_off_nadir_angle is the average value for the points inside the bounding box. Off nadir angles often vary only slightly across an image so the calculated value may not be too different from the average value across the image as a whole.

Note that the response contains no information about the area of interest used to calculate the two area-based properties.

Sorting by area-based properties

The two area-based properties can be included among columns in the sortby query parameter. This is especially useful for the area:cloud_cover_percentage property since users are often interested in images with the least cloud cover, and it's helpful to have them returned first in the response.

Here is the same query used in the previous example but with sorting by area:cloud_cover_percentage. To keep the number of returned results smaller we search only on the wv02 collection. Note how the value being sorted on is actually specified as properties.area:cloud_cover_percentage since the service includes the area-based calculations in the returned features' properties objects.

curl -s -H "$auth_header" 'https://api.maxar.com/discovery/v1/search' \
-d 'collections=wv02' \
-d 'area-based-calc=true' \
-d 'bbox=-106.1,38,-105.8,38.2' \
-d 'sortby=properties.area:cloud_cover_percentage' \
-d 'limit=50' \
| jq -r '.features[] | [.id, .properties["area:cloud_cover_percentage"]] | @tsv'
1030010005BB6B00	0
1030010006AB6C00 0
10300100049DC600 0
103001000DA34800 0
1030010007429900 0
103001002D61F000 0
103001002D04A300 0
1030010005B59100 0
103001004B580700 0
103001004B320C00 0
103001005D11BC00 0
103001005ECDFE00 0
103001005C885700 0
1030010064C9F000 0
103001005692B900 0
10300100646A3D00 0
1030010064884400 0
1030010075A48600 0
1030010075128E00 0
1030010072500500 0
1030010073B68800 0
10300100850C0E00 0
103001008992B800 0
103001008E23FE00 0
103001008D139700 0
10300100E8268000 0
10300100E947EF00 0
1030010040613E00 0.20857633877889697
10300100656FC900 0.3030690663788226
103001001CA33C00 0.8687489964387967
10300100E05D6600 0.9742435945897585
10300100E05FD200 1.1797531859622232
10300100E97D1100 2.090983149358771
1030010057AD2000 2.7927273099415975
10300100669E3E00 4.065454610008397
10300100070B2E00 4.286469782220525
10300100683E2100 5.386625592976212
10300100EA774200 13.619869848133119
10300100717C2600 16.103287446107547
1030010009CC8700 20.187832934930455
10300100342C8500 30.335529429913613
10300100E9400800 37.46685013836615
10300100EA4C8700 79.00561362324578
10300100569F4800 81.23405302086331
103001004F545C00 89.32739783316474
103001001A269300 90.85772951504416
10300100ED72BF00 95.48955078718566
103001004F432300 96.92725749279602
103001004F4EC600 99.72791110158892
10300100CAD59600

It is common for images to be cloud free so the first items in the response have zero cloud cover.

Note that sorting by area-based properties takes additional time for the service to perform. The service must first determine all imagery items that satisfy the spatial and temporal filters, calculate the area-based properties for all of them, then finally apply sorting to them. If you do not require that results be returned in sorted order by area-based properties then it is faster not to.

Querying special time fields

While every item record includes a datetime property, imagery items include additional finer-grained temporal fields. These break out different intervals from the datetime field like the date only and local time of day.

  • utc_hour: UTC hour of image collection.
  • local_hour: Local hour of image collection, based on timezone offset of image footprint's centroid.
  • utc_month_day: UTC month and day of image collection. Can be used to search for imagery based on season. In format MM-DD with zero-padded two-digit month and day values.
  • local_month_day: Like utc_month_day, but based on timezone offset of image footprint's centroid.
  • timezone_offset: Integer timezone offset of image, based on timezone offset of image footprint's centroid.
  • utc_time_of_day: UTC time of day of image collection. In format HH:MM:SS.
  • local_time_of_day: Local time of image collection, based on timezone of image footprint's centroid.
  • local_time_of_day_with_timezone: Local time of image collection, based on timezone offset of image footprint's centroid. In format HH:MM:SS+TT00 or HH:MM:SS-TT00, where TT is the timezone offset as zero-padded two-digit value.

A datetime of 2020-10-09T19:29:33.604260Z converts to the following extended properties:

PropertyValue
datetime"2020-10-09T19:29:33.604260Z"
utc_hour19
local_hour8
utc_month_day"10-09"
local_month_day"10-09"
timezone_offset-11
utc_time_of_day"19:29:33"
local_time_of_day"08:29:33"
local_time_of_day_with_timezone"08:29:33-1100"

When using general date range queries, always use the datetime field for higher performance.

Querying antimeridian images

Sometimes satellite images cross the antimeridian. Their STAC item footprint geometries will likewise cross the antimeridian. In the West part of the footprint verticies will be in the Eastern hemisphere with longitudes near 180° East. The East part of the geometry will have vertices in the Western hemisphere with longitudes near 180° West. There are only a handful of places on Earth where land areas cross the antimeridian such as Fiji, Eastern Russia, and Antarctica.

If a geometry crosses the antimeridian then its vertices with longitudes in the Western hemisphere will have values greater than 180°. This avoids cutting the geometry and maintains continuity in the longitude values, which can be helpful for some applications that have trouble dealing with the antimeridian. Applications may choose to modify such longitudes in returned features by subtracting 360 from them. Or applications may first split the returned geometries on the antimeridian, then subtract 360 from longitudes in the part in the Western hemisphere. It is up to applications using the Discovery API to decide how to deal with longitudes in features that cross the antimeridian.

The bbox property of an image that crosses the antimeridian is handled similarly. It will have an east longitude > 180°.

As an example here is the geometry and bbox of the WorldView-3 image 104001006A8D9C00. It is located in Fiji and spans the antimeridian. Notice that vertices in the Western hemisphere have longitudes > 180°:

curl -s -H "$auth_header" "https://api.maxar.com/discovery/v1/collections/wv03-vnir/items/104001006A8D9C00" | jq '{bbox,geometry}'
  {
"bbox": [
179.848856,
-17.036345,
180.008652,
-16.738788
],
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
179.849571,
-16.759354
],
[
179.849597,
-16.760238
],
[
179.849583,
-16.76627
],
[
179.849575
]
]
]
}
}

Note that these extended longitude values are not used when performing a geospatial search using the Discovery API. When you search using either the bbox or intersects query parameters all longitudes must be in the range [-180, 180].

If you use the bbox query parameter and want to search across the antimeridian then the East side of the box will be in the Western hemisphere. For instance if you wanted to search over Fiji you might specify bbox=179,-17,-179,-16. See the The GeoJSON specification for more information.

The same applies when using the intersects query parameter with a GeoJSON geometry that crosses the antimeridian. For example here is a GeoJSON geometry for a parallelogram over Fiji that could be used as the value of intersects:

{
"type": "Polygon",
"coordinates": [
[
[179, -17],
[180, -17],
[-179, -16],
[180, -16],
[179, -17]
]
]
}

Returned features that cross the antimeridian will always have longitudes > 180° for those vertices in the Western hemisphere, even though the search geometries specified by bbox and intersects don't use that convention.

Querying stereo pairs

Each satellite has a collection for its intrack stereo pairs (for WorldView-3 there are separate collections for VNIR and SWIR images):

  • ge01-intrack-stereo
  • lg01-intrack-stereo
  • lg02-intrack-stereo
  • wv01-intrack-stereo
  • wv02-intrack-stereo
  • wv03-swir-intrack-stereo
  • wv03-vnir-intrack-stereo

The STAC items in these collections describe the two images that make up a stero pair.

Here is an example. This query returns the most recent GeoEye-1 stereo pair in the collection ge01-intrack-stereo. The feature's geometry is the area of intersection of the two images in the stereo pair. A few properties describe the stereo pair. The image_identifiers array contains the images belonging to the pair:

curl -s -H "$auth_header" "https://api.maxar.com/discovery/v1/collections/ge01-intrack-stereo/items?limit=1" | jq
{
"type": "FeatureCollection",
"features": [{
"id": "e2bc6c55-158c-4c6e-8d5f-c853bae5d750-inv",
"bbox": [
-46.0261275643433,
-13.7973296846791,
-45.8390802456912,
-13.6848373230799
...
],
"type": "Feature",
"links": [{
"rel": "collection",
"href": "https://api.maxar.com/discovery/v1/collections/ge01-intrack-stereo",
"type": "application/json"
},
{
"rel": "root",
"href": "https://apit.maxar.com/discovery/v1/collections/maxar-stereo",
"type": "application/json"
},
{
"rel": "self",
"href": "https://api.maxar.com/discovery/v1/collections/ge01-intrack-stereo/items/e2bc6c55-158c-4c6e-8d5f-c853bae5d750-inv",
"type": "application/geo+json"
}
],
"assets": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-46.02608389,
-13.7018126
],
[
-46.0261275643433,
-13.6911991273234
],
[
-45.861675619949,
-13.6848373230799
]
]
]
},
"collection": "ge01-intrack-stereo",
"properties": {
"title": "Maxar InTrack Stereo e2bc6c55-158c-4c6e-8d5f-c853bae5d750-inv",
"datetime": "2022-10-03T13:14:02.685706Z",
"stereo_type": "InTrack",
"max_bisector": 60.977203369140625,
"min_bisector": 60.83549499511719,
"max_asymmetry": 6.603069305419922,
"min_asymmetry": 6.200501918792725,
"max_convergence": 33.477142333984375,
"min_convergence": 33.238887786865234,
"image_identifiers": [
"105001002F288000",
"105001002F287E00"
]
},
"stac_version": "1.0.0"
}],
"numberReturned": 1,
"timestamp": "2022-10-03T16:33:30.925411Z",
"links": [{
"rel": "next",
"href": "https://api.maxar.com/discovery/v1/collections/ge01-intrack-stereo/items?limit=1&page=2"
}]
}

Querying WorldView-3 image associations

The catalog contains a collection multi-sensor-image-pair that describes associated images. Currently this is only used with WorldView-3 images since that satellite is the only one with multiple sensors. Each item in the multi-sensor-image-pair collection describes the association between a WorldView-3 VNIR and SWIR image.

Each WorldView-3 STAC item has an associations property with the STAC item ID of its association. So given a WorldView-3 STAC item you can find its associated image (if any) by querying its association in the multi-sensor-image-pair collection, and then querying the STAC item of its associated image.

Here is an example starting with the WorldView-3 SWIR image 104A010079117E00. Here is its STAC item associations property:

curl -s -H "$auth_header" "https://api.maxar.com/discovery/v1/collections/wv03-swir/items/104A010079117E00" | jq '.properties.associations'
[
{
"association_type": "VNIR_SWIR",
"association_identifier": "a2288b60-bed2-467b-8e40-d2bbd31b0a57-inv"
}
]

Note: the associations property is array-valued. In general an image might be associaed with multiple other images in multiple ways. Currently only a "VNIR_SWIR" association is supported for use only by WorldView-3 images.

To find the assoicated image query the catalog for the association's STAC item:

curl -s -H "$auth_header" "https://api.maxar.com/discovery/v1/collections/multi-sensor-image-pair/items/a2288b60-bed2-467b-8e40-d2bbd31b0a57-inv" | jq
  {
"id": "a2288b60-bed2-467b-8e40-d2bbd31b0a57-inv",
"bbox": [
-35.030944,
-8.043394,
-34.869169,
-7.90298
],
"type": "Feature",
"links": [{
"rel": "collection",
"href": "https://api.maxar.com/discovery/v1/collections/multi-sensor-image-pair"
},
{
"rel": "self",
"href": "https://api.maxar.com/discovery/v1/collections/multi-sensor-image-pair/items/a2288b60-bed2-467b-8e40-d2bbd31b0a57-inv"
}
],
"assets": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[179, -17],
[180, -17],
[-179, -16],
[180, -16],
[179, -17]
]
]
},
"stac_version": "1.0.0

The association's image_identifiers property contains the STAC item IDs of the two images in the association. One of these is the ID of the image we started with, 104A010079117E00. The other is its assoicated image.

Requesting assets

STAC items can have an assets property containing URL's to an item's constituent files. The particular assets associated with an item depend on what kind of geospatial product the item is.

STAC items for satellite images generally have these assets:

  • browse: A low-resolution browse image in GeoTIFF format
  • cloud-cover: A GeoJSON file containing cloud polygons
  • sample-point-set: A GeoJSON file of sample points

For example an earlier section above requested the WorldView-1 image 1020010010DD0000 and the returned STAC item's assets property contained this browse object:

"browse": {
"href": "https://api.maxar.com/discovery/v1/collections/wv01/items/1020010010DD0000/assets/collections/dg-archive/assets/browse/1020010010DD0000.browse.tif",
"type": "image/tiff; application=geotiff",
"title": "Browse Image"
}

To retrieve the browse image, make a GET request to the given URL. You must provide the Authentication header with a bearer token or use an API key with a no authorization header.

Note that a GET on an asset URL generally causes an HTTP redirect and a returned HTTP status of 302. You should then request the redirected URL. If you are using curl to GET an asset URL specify the --location command line option.

Satellite image properties

This section lists the properties that are generally provided for satellite images. Collections in the catalog for other products besides images will have different properties.

Required properties of STAC items

  • datetime

Properties from STAC common metadata

  • constellation
  • gsd
  • instruments
  • platform
  • title

Properties of eo extension:

  • eo:bands
  • eo:cloud_cover

Properties of view extension:

  • view:azimuth
  • view:off_nadir
  • view:sun_azimuth
  • view:sun_elevation

Properties specific to Maxar satellite imagery

  • associations
  • collect_time_end
  • collect_time_start
  • multi_resolution_avg
  • multi_resolution_end
  • multi_resolution_max
  • multi_resolution_min
  • multi_resolution_start
  • off_nadir_avg
  • off_nadir_end
  • off_nadir_max
  • off_nadir_min
  • off_nadir_start
  • pan_resolution_avg
  • pan_resolution_end
  • pan_resolution_max
  • pan_resolution_min
  • pan_resolution_start
  • rda_available
  • scan_direction
  • spacecraft_to_target_azimuth_avg
  • spacecraft_to_target_azimuth_end
  • spacecraft_to_target_azimuth_max
  • spacecraft_to_target_azimuth_min
  • spacecraft_to_target_azimuth_start
  • stereo_pair_identifiers
  • target_to_spacecraft_azimuth_avg
  • target_to_spacecraft_azimuth_end
  • target_to_spacecraft_azimuth_max
  • target_to_spacecraft_azimuth_min
  • target_to_spacecraft_azimuth_start
  • target_to_spacecraft_elevation_avg
  • target_to_spacecraft_elevation_end
  • target_to_spacecraft_elevation_max
  • target_to_spacecraft_elevation_min
  • target_to_spacecraft_elevation_start