End-to-End Ordering Quickstart

This guide walks you through the complete workflow for purchasing satellite imagery via the DataCosmos API — from authentication to downloading your data. Each step builds on the previous one, showing how data flows through the entire process.

By the end of this guide you will have:

  1. Authenticated and obtained an access token
  2. Searched the STAC catalogue for satellite imagery
  3. Placed an order and completed checkout
  4. Downloaded the purchased data

NOTE

A complete runnable script combining all steps is provided at the end of this page.


Prerequisites

Before you begin, make sure you have:

  • A valid token (JWT) — To get API credentials follow the steps described in the Authentication page.
  • Organisation ID — Your organisation's numeric identifier (associated with your account).
  • Contract ID — The contract under which purchases are made.

For Bash examples: curl and jq must be installed.

For Python examples: Install the requests library:

pip install requests

Step 1: Authenticate

All DataCosmos API calls require a bearer token. Obtain one by following the steps described in the Authentication page.

Bash

# Authenticate and store the access token
DATACOSMOS_ACCESS_TOKEN="<your access token here>"

  Python

import requests

access_token = "<your access token here>"

session = requests.Session()

session.headers.update(
    {"Authorization": f'Bearer {access_token}'}
)

print("Authentication configuration completed.")

NOTE

The Python examples use a requests.Session object that automatically includes the bearer token in all subsequent requests. For full details see the Authentication page.


Step 2: Search the Catalogue

Use the STAC Search API to find satellite images matching your area of interest. This example searches for Sentinel-2 L2A images over a bounding box in Chile with less than 20% cloud cover.

Endpoint

POST https://app.open-cosmos.com/api/data/v0/stac/search

Bash

# Search for Sentinel-2 images with low cloud cover in a bounding box
SEARCH_RESULTS=$(curl --request POST "https://app.open-cosmos.com/api/data/v0/stac/search" \
  --header "Content-Type: application/json" \
  --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}" \
  --data-raw '{
    "collections": ["sentinel-2-l2a"],
    "bbox": [-71.72, -27.68, -71.02, -27.06],
    "limit": 5,
    "query": {
      "eo:cloudcover": {
        "lte": 20
      }
    }
  }')

# Extract the first result's collection and item IDs
COLLECTION=$(echo "$SEARCH_RESULTS" | jq -r '.features[0].collection')
ITEM_ID=$(echo "$SEARCH_RESULTS" | jq -r '.features[0].id')

echo "Found item: ${COLLECTION} / ${ITEM_ID}"

  Python

# Search for Sentinel-2 images with low cloud cover
search_body = {
    "collections": ["sentinel-2-l2a"],
    "bbox": [-71.72, -27.68, -71.02, -27.06],
    "limit": 5,
    "query": {
        "eo:cloudcover": {
            "lte": 20,
        },
    },
}

response = session.post(
    "https://app.open-cosmos.com/api/data/v0/stac/search",
    json=search_body,
)

search_results = response.json()
features = search_results.get("features", [])

if not features:
    print("No results found. Try adjusting your search parameters.")
else:
    # Pick the first result
    selected_item = features[0]
    collection = selected_item["collection"]
    item_id = selected_item["id"]
    price = selected_item["properties"]["opencosmos:price"]
    currency = selected_item["properties"]["opencosmos:price_currency"]
    print(f"Found {len(features)} result(s). Selected: {collection} / {item_id} ({price} {currency})")

NOTE

The bbox parameter uses the format [west, south, east, north] in decimal degrees. The query parameter supports CQL filters such as eo:cloudcover. See the STAC Search API for all available search parameters.


Step 3: Place an Order

Create an order for the selected item. If required (depends on the user configuration), you will need to initiate the checkout with the payment gateway to complete payment.

3a. Create the Order

Endpoint

POST https://app.open-cosmos.com/api/data/v0/order/orders

Bash

# Create an order for the selected item
ORDER_RESPONSE=$(curl --request POST "https://app.open-cosmos.com/api/data/v0/order/orders" \
  --header "Content-Type: application/json" \
  --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}" \
  --data-raw "{
    \"type\": \"IMAGE\",
    \"data\": {
      \"order_line_items\": [
        {
          \"collection\": \"${COLLECTION}\",
          \"item\": \"${ITEM_ID}\",
          \"level\": \"L2A\"
        }
      ]
    },
    \"organisation\": YOUR_ORGANISATION_ID,
    \"contract_id\": YOUR_CONTRACT_ID,
    \"licenses\": [
      {
        \"license_link\": \"https://example.com/license/eula-v1\",
        \"license_name\": \"DataCosmos EULA\",
        \"license_version\": \"1.0\",
        \"accepted\": true
      }
    ]
  }")

ORDER_ID=$(echo "$ORDER_RESPONSE" | jq -r '.data.id')
ORDER_STATUS=$(echo "$ORDER_RESPONSE" | jq -r '.data.status')

echo "Order created: ${ORDER_ID} (status: ${ORDER_STATUS})"

  Python

# Create an order for the selected item
order_payload = {
    "type": "IMAGE",
    "data": {
        "order_line_items": [
            {
                "collection": collection,
                "item": item_id,
                "level": "L2A",
            }
        ]
    },
    "organisation": YOUR_ORGANISATION_ID,  # Replace with your organisation ID
    "contract_id": YOUR_CONTRACT_ID,       # Replace with your contract ID
    "licenses": [
        {
            "license_link": "https://example.com/license/eula-v1",
            "license_name": "DataCosmos EULA",
            "license_version": "1.0",
            "accepted": True,
        }
    ],
}

response = session.post(
    "https://app.open-cosmos.com/api/data/v0/order/orders",
    json=order_payload,
)

order = response.json()["data"]
order_id = order["id"]
print(f"Order created: {order_id} (status: {order['status']})")

3b. Initiate Checkout

If the order requires payment, use the checkout endpoint to get a checkout payment URL. If the payment URL is not provided no redirection is needed.

Endpoint

GET https://app.open-cosmos.com/api/data/v0/order/orders/{order_id}/checkout

Bash

# Get the checkout URL
CHECKOUT_RESPONSE=$(curl --request GET \
  "https://app.open-cosmos.com/api/data/v0/order/orders/${ORDER_ID}/checkout" \
  --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}")

CHECKOUT_URL=$(echo "$CHECKOUT_RESPONSE" | jq -r '.data.checkout_url')
echo "Complete payment at: ${CHECKOUT_URL}"

  Python

# Get the checkout URL
response = session.get(
    f"https://app.open-cosmos.com/api/data/v0/order/orders/{order_id}/checkout",
)

checkout_url = response.json()["data"]["checkout_url"]
print(f"Complete payment at: {checkout_url}")

NOTE

The create order response may already include a checkout_redirect_url. If so, you can use that URL directly without calling the checkout endpoint separately. See Purchasing Catalog Images for full details on order management.


Step 4: Download Data

After completing payment, poll the order status until it changes to PAID, then download the assets from the STAC item.

4a. Wait for Payment Confirmation

Bash

# Poll until the order is paid
MAX_ATTEMPTS=30
POLL_INTERVAL=10

for i in $(seq 1 $MAX_ATTEMPTS); do
  STATUS=$(curl --silent --request GET \
    "https://app.open-cosmos.com/api/data/v0/order/orders/${ORDER_ID}" \
    --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}" | jq -r '.data.status')

  echo "Attempt ${i}: status = ${STATUS}"

  if [ "$STATUS" = "PAID" ]; then
    echo "Payment confirmed!"
    break
  elif [ "$STATUS" = "CANCELLED" ]; then
    echo "Order was cancelled."
    break
  fi

  sleep $POLL_INTERVAL
done

  Python

import time

# Poll until the order is paid
max_attempts = 30
poll_interval_seconds = 10

for attempt in range(max_attempts):
    response = session.get(
        f"https://app.open-cosmos.com/api/data/v0/order/orders/{order_id}",
    )
    status = response.json()["data"]["status"]

    if status == "PAID":
        print("Payment confirmed!")
        break
    elif status == "CANCELLED":
        print("Order was cancelled.")
        break
    else:
        print(f"Attempt {attempt + 1}: status = {status} — waiting {poll_interval_seconds}s...")
        time.sleep(poll_interval_seconds)

4b. Download Assets

Once the order is paid, retrieve the STAC item and download all associated assets.

Bash

# Download all assets from the purchased item
ITEM_RESPONSE=$(curl --silent --request GET \
  "https://app.open-cosmos.com/api/data/v0/stac/collections/${COLLECTION}/items/${ITEM_ID}" \
  --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}")

# Download each asset
echo "$ITEM_RESPONSE" | jq -r '.assets | to_entries[] | "\(.value.title)\t\(.value.href)"' | while IFS=$'\t' read -r title href; do
  echo "Downloading: ${title}"
  curl --silent --output "${title}" \
    --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}" \
    "${href}"
done

echo "All assets downloaded."

  Python

import os

# Retrieve the STAC item to get asset download links
response = session.get(
    f"https://app.open-cosmos.com/api/data/v0/stac/collections/{collection}/items/{item_id}",
)

item_data = response.json()

# Download each asset
for asset_key, asset in item_data["assets"].items():
    filename = asset.get("title", asset_key)
    print(f"Downloading: {filename}")
    asset_response = session.get(asset["href"])
    with open(filename, "wb") as fp:
        fp.write(asset_response.content)

print("All assets downloaded.")

Complete End-to-End Scripts

Below are complete, runnable scripts that combine all five steps into a single workflow.

Python

"""
DataCosmos End-to-End Ordering Example

Prerequisites:
  - pip install requests
  - data_cosmos_api_credentials.json in the same directory
  - Replace YOUR_ORGANISATION_ID and YOUR_CONTRACT_ID with real values
"""

import json
import os
import time

import requests

# ──────────────────────────────────────────────
# Configuration
# ──────────────────────────────────────────────
CREDENTIALS_FILE = "data_cosmos_api_credentials.json"
ORGANISATION_ID = 42       # Replace with your organisation ID
CONTRACT_ID = 456          # Replace with your contract ID
DOWNLOAD_DIR = "downloads"

# ──────────────────────────────────────────────
# Step 1: Authenticate
# ──────────────────────────────────────────────
print("Step 1: Authenticating...")

with open(CREDENTIALS_FILE) as fp:
    oauth_body = json.load(fp)

session = requests.Session()
token_response = session.post(
    "https://login.open-cosmos.com/oauth/token",
    data=oauth_body,
).json()

session.headers.update(
    {"Authorization": f'{token_response["token_type"]} {token_response["access_token"]}'}
)
print("  Authenticated successfully.\n")

# ──────────────────────────────────────────────
# Step 2: Search the Catalogue
# ──────────────────────────────────────────────
print("Step 2: Searching the catalogue...")

search_body = {
    "collections": ["sentinel-2-l2a"],
    "bbox": [-71.72, -27.68, -71.02, -27.06],
    "limit": 5,
    "query": {
        "eo:cloudcover": {
            "lte": 20,
        },
    },
}

response = session.post(
    "https://app.open-cosmos.com/api/data/v0/stac/search",
    json=search_body,
)
response.raise_for_status()

features = response.json().get("features", [])
if not features:
    print("  No results found. Try adjusting your search parameters.")
    exit(1)

selected_item = features[0]
collection = selected_item["collection"]
item_id = selected_item["id"]
print(f"  Found {len(features)} result(s). Selected: {collection} / {item_id}\n")

# ──────────────────────────────────────────────
# Step 3: Retrieve Pricing
# ──────────────────────────────────────────────
print("Step 3: Retrieving pricing...")

response = session.get(
    "https://app.open-cosmos.com/api/data/v0/order/price",
    params={"collection": collection, "item": item_id, "level": "L2A"},
)
response.raise_for_status()

price_data = response.json()["data"]
print(f"  Price: {price_data['final']} {price_data['currency']}\n")

# ──────────────────────────────────────────────
# Step 4: Place an Order
# ──────────────────────────────────────────────
print("Step 4: Creating order...")

order_payload = {
    "type": "IMAGE",
    "data": {
        "order_line_items": [
            {
                "collection": collection,
                "item": item_id,
                "level": "L2A",
            }
        ]
    },
    "organisation": ORGANISATION_ID,
    "contract_id": CONTRACT_ID,
    "licenses": [
        {
            "license_link": "https://example.com/license/eula-v1",
            "license_name": "DataCosmos EULA",
            "license_version": "1.0",
            "accepted": True,
        }
    ],
}

response = session.post(
    "https://app.open-cosmos.com/api/data/v0/order/orders",
    json=order_payload,
)
response.raise_for_status()

order = response.json()["data"]
order_id = order["id"]
print(f"  Order created: {order_id} (status: {order['status']})")

# Get checkout URL
response = session.get(
    f"https://app.open-cosmos.com/api/data/v0/order/orders/{order_id}/checkout",
)
response.raise_for_status()

checkout_url = response.json()["data"]["checkout_url"]
print(f"  Complete payment at: {checkout_url}\n")

# ──────────────────────────────────────────────
# Step 5: Wait for Payment and Download Data
# ──────────────────────────────────────────────
print("Step 5: Waiting for payment confirmation...")

max_attempts = 30
poll_interval = 10

for attempt in range(max_attempts):
    response = session.get(
        f"https://app.open-cosmos.com/api/data/v0/order/orders/{order_id}",
    )
    status = response.json()["data"]["status"]

    if status == "PAID":
        print("  Payment confirmed!\n")
        break
    elif status == "CANCELLED":
        print("  Order was cancelled.")
        exit(1)
    else:
        print(f"  Attempt {attempt + 1}: status = {status} — waiting {poll_interval}s...")
        time.sleep(poll_interval)

# Download assets
print("Downloading assets...")

response = session.get(
    f"https://app.open-cosmos.com/api/data/v0/stac/collections/{collection}/items/{item_id}",
)
response.raise_for_status()

item_data = response.json()
os.makedirs(DOWNLOAD_DIR, exist_ok=True)

for asset_key, asset in item_data["assets"].items():
    filename = asset.get("title", asset_key)
    filepath = os.path.join(DOWNLOAD_DIR, filename)
    print(f"  Downloading: {filename}")
    asset_response = session.get(asset["href"])
    with open(filepath, "wb") as fp:
        fp.write(asset_response.content)

print(f"\nDone! All assets saved to '{DOWNLOAD_DIR}/'.")

Bash

#!/usr/bin/env bash
#
# DataCosmos End-to-End Ordering Example
#
# Prerequisites:
#   - curl and jq installed
#   - data_cosmos_api_credentials.json in the same directory
#   - Replace YOUR_ORGANISATION_ID and YOUR_CONTRACT_ID below
#
set -euo pipefail

# ──────────────────────────────────────────────
# Configuration
# ──────────────────────────────────────────────
CREDENTIALS_FILE="data_cosmos_api_credentials.json"
ORGANISATION_ID=42    # Replace with your organisation ID
CONTRACT_ID=456       # Replace with your contract ID
DOWNLOAD_DIR="downloads"

# ──────────────────────────────────────────────
# Step 1: Authenticate
# ──────────────────────────────────────────────
echo "Step 1: Authenticating..."

DATACOSMOS_ACCESS_TOKEN=$(curl --silent --request POST "https://login.open-cosmos.com/oauth/token" \
  --header "Content-Type: application/json" \
  -d @"${CREDENTIALS_FILE}" | jq -r ".access_token")

echo "  Authenticated successfully."
echo ""

# ──────────────────────────────────────────────
# Step 2: Search the Catalogue
# ──────────────────────────────────────────────
echo "Step 2: Searching the catalogue..."

SEARCH_RESULTS=$(curl --silent --request POST "https://app.open-cosmos.com/api/data/v0/stac/search" \
  --header "Content-Type: application/json" \
  --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}" \
  --data-raw '{
    "collections": ["sentinel-2-l2a"],
    "bbox": [-71.72, -27.68, -71.02, -27.06],
    "limit": 5,
    "query": {
      "eo:cloudcover": {
        "lte": 20
      }
    }
  }')

RESULT_COUNT=$(echo "$SEARCH_RESULTS" | jq '.features | length')
COLLECTION=$(echo "$SEARCH_RESULTS" | jq -r '.features[0].collection')
ITEM_ID=$(echo "$SEARCH_RESULTS" | jq -r '.features[0].id')

echo "  Found ${RESULT_COUNT} result(s). Selected: ${COLLECTION} / ${ITEM_ID}"
echo ""

# ──────────────────────────────────────────────
# Step 3: Retrieve Pricing
# ──────────────────────────────────────────────
echo "Step 3: Retrieving pricing..."

PRICE_RESPONSE=$(curl --silent --request GET \
  "https://app.open-cosmos.com/api/data/v0/order/price?collection=${COLLECTION}&item=${ITEM_ID}&level=L2A" \
  --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}")

FINAL_PRICE=$(echo "$PRICE_RESPONSE" | jq -r '.data.final')
CURRENCY=$(echo "$PRICE_RESPONSE" | jq -r '.data.currency')

echo "  Price: ${FINAL_PRICE} ${CURRENCY}"
echo ""

# ──────────────────────────────────────────────
# Step 4: Place an Order
# ──────────────────────────────────────────────
echo "Step 4: Creating order..."

ORDER_RESPONSE=$(curl --silent --request POST "https://app.open-cosmos.com/api/data/v0/order/orders" \
  --header "Content-Type: application/json" \
  --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}" \
  --data-raw "{
    \"type\": \"IMAGE\",
    \"data\": {
      \"order_line_items\": [
        {
          \"collection\": \"${COLLECTION}\",
          \"item\": \"${ITEM_ID}\",
          \"level\": \"L2A\"
        }
      ]
    },
    \"organisation\": ${ORGANISATION_ID},
    \"contract_id\": ${CONTRACT_ID},
    \"licenses\": [
      {
        \"license_link\": \"https://example.com/license/eula-v1\",
        \"license_name\": \"DataCosmos EULA\",
        \"license_version\": \"1.0\",
        \"accepted\": true
      }
    ]
  }")

ORDER_ID=$(echo "$ORDER_RESPONSE" | jq -r '.data.id')
ORDER_STATUS=$(echo "$ORDER_RESPONSE" | jq -r '.data.status')

echo "  Order created: ${ORDER_ID} (status: ${ORDER_STATUS})"

CHECKOUT_RESPONSE=$(curl --silent --request GET \
  "https://app.open-cosmos.com/api/data/v0/order/orders/${ORDER_ID}/checkout" \
  --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}")

CHECKOUT_URL=$(echo "$CHECKOUT_RESPONSE" | jq -r '.data.checkout_url')
echo "  Complete payment at: ${CHECKOUT_URL}"
echo ""

# ──────────────────────────────────────────────
# Step 5: Wait for Payment and Download Data
# ──────────────────────────────────────────────
echo "Step 5: Waiting for payment confirmation..."

MAX_ATTEMPTS=30
POLL_INTERVAL=10

for i in $(seq 1 $MAX_ATTEMPTS); do
  STATUS=$(curl --silent --request GET \
    "https://app.open-cosmos.com/api/data/v0/order/orders/${ORDER_ID}" \
    --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}" | jq -r '.data.status')

  if [ "$STATUS" = "PAID" ]; then
    echo "  Payment confirmed!"
    break
  elif [ "$STATUS" = "CANCELLED" ]; then
    echo "  Order was cancelled."
    exit 1
  fi

  echo "  Attempt ${i}: status = ${STATUS} — waiting ${POLL_INTERVAL}s..."
  sleep $POLL_INTERVAL
done

echo ""
echo "Downloading assets..."

mkdir -p "${DOWNLOAD_DIR}"

ITEM_RESPONSE=$(curl --silent --request GET \
  "https://app.open-cosmos.com/api/data/v0/stac/collections/${COLLECTION}/items/${ITEM_ID}" \
  --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}")

echo "$ITEM_RESPONSE" | jq -r '.assets | to_entries[] | "\(.value.title)\t\(.value.href)"' | while IFS=$'\t' read -r title href; do
  echo "  Downloading: ${title}"
  curl --silent --output "${DOWNLOAD_DIR}/${title}" \
    --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}" \
    "${href}"
done

echo ""
echo "Done! All assets saved to '${DOWNLOAD_DIR}/'."

Where to Next

For more detail on each step, see these pages:

Ordering API Overview | Pricing API