BYOC API examples

The following API requests are written in Python. To execute them you need to create an OAuth client as is explained here. It is named oauth in these examples. The examples are structured in a way to be as separable as possible, however in may cases doing all the steps in each chapter makes sense.

Creating a collection

To create a collection with the name <MyCollection> and S3 bucket <MyBucket>:

collection = {
'name': '<MyCollection',
's3Bucket': '<MyBucket>'
}
response = oauth.post('https://services.sentinel-hub.com/api/v1/byoc/collections', json=collection)
response.raise_for_status()

Extracting the collection id from the response:

import json
collection = json.loads(response.text)['data']
collection_id = collection['id']

Creating a tile

To create a tile with the path <MyTile>:

tile = {
'path': '<MyTile>',
}
response = oauth.post(f'https://services.sentinel-hub.com/api/v1/byoc/collections/{collection_id}/tiles', json=tile)
response.raise_for_status()

If your tile has a known sensing time, e.g. October 21, 2019 at 14:51, add this information by using the following payload:

tile = {
'path': '<MyTile>',
'sensingTime': '2019-10-21T14:51:00'
}

If you want to provide a cover geometry, set it as the value of the coverGeometry field:

tile = {
'path': '<MyTile>',
'coverGeometry': <MyCoverGeometry>
}

For information on how to prepare a cover geometry, see Preparing a cover geometry.

To extract the tile id from the response:

import json
tile = json.loads(response.text)['data']
tile_id = tile['id']

Preparing a cover geometry

To obtain a cover geometry automatically, you can use the gdal_trace_outline script which gives you a cover geometry in the WKT format:

import subprocess
command = f'gdal_trace_outline <MyCOG> -out-cs en -wkt-out wkt.txt'
subprocess.run(command, shell=True, check=True)

Once complete, transform the geometry into the GeoJSON format:

from osgeo import ogr
import json
f = open('wkt.txt')
geom = ogr.CreateGeometryFromWkt(f.read())
cover_geometry = json.loads(geom.ExportToJson())

If the CRS is something other than WGS84, make sure to set its URN <CrsUrn> under crs.properties.name. For example urn:ogc:def:crs:EPSG::32633 for EPSG:32633.

cover_geometry['crs'] = {
'properties': {
'name': '<CrsUrn>'
}
}

To obtain the URN automatically from a raster file you can use the following Python scriptget_crn_urn.py.

Checking the tile ingestion status

To check the ingestion status of the tile, first get the tile:

response = oauth.get(f'https://services.sentinel-hub.com/api/v1/byoc/collections/{collection_id}/tiles/{tile_id}')
response.raise_for_status()

Then extract its status from the response:

import json
tile = json.loads(response.text)['data']
status = tile['status']
if status == 'INGESTED':
print('Tile ingested')
elif status == 'FAILED':
print('Tile failed to ingest')
else:
print(status)

To check why a tile failed to ingest:

print(tile['additionalData']['failedIngestionCause'])

Listing tiles

Tiles are paginated and to traverse all pages use the link from response that points to the next page, which is located at links.next. You can also control pagination using query parameters viewtoken and count, where viewtoken offsets tiles and count select the number of tiles. By default a page has 100 tiles, which is also the limit.

import time
url = f'https://services.sentinel-hub.com/api/v1/byoc/collections/{collection_id}/tiles'
while url is not None:
response = oauth.get(url)
response.raise_for_status()
output = response.json()
tiles = output['data']
links = output['links']
for tile in tiles:
print(tile['path'])
# sets url to None if there's no link to the next set of tiles
url = links.get('next', None)
# waits a bit before fetching the next set
time.sleep(0.1)

Processing tiles

To get data from your tiles using the Process API you need to specify which BYOC collection is to be used as the data source. The remaining parameters (i.e. bounds or evalscript) are as any other request.

First, set the BYOC collection as the data source. To get the collection_id see Creating a collection.

input = {
"data": [{
"type": "CUSTOM",
"dataFilter": {
"collectionId": collection_id
}
}]
}

Set some bounds:

input['bounds'] = {
"properties": {
"crs": "http://www.opengis.net/def/crs/EPSG/0/4326"
},
"bbox": [14.4, 46.15, 14.6, 45.95]
}

Provide an evalscript that uses your bands. This is an example of an evalscript using a band named MyBand:

evalscript = """
//VERSION=3
function setup() {
return {
input: ["MyBand"],
output: { bands: 1}
};
}
function evaluatePixel(sample) {
return [sample.MyBand];
}
"""

Finally, construct the request and send it to the Process API:

request = {
"input": input,
"evalscript": evalscript,
}
response = oauth.post('https://services.sentinel-hub.com/api/v1/process',
json=request, headers={'Accept': 'image/tiff'})
response.raise_for_status()

To save the response to a file:

out_path = 'output.tiff'
with open(out_path, 'wb') as handle:
for block in response.iter_content(1024):
handle.write(block)