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. Retrieved pricing for the selected item
  4. Placed an order and completed checkout
  5. 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. Retrieve your contracts with:
    curl "https://app.open-cosmos.com/api/data/v1/dpap/organisations/{organisation_id}/policies" \
      --header "Authorization: Bearer ${DATACOSMOS_ACCESS_TOKEN}"
    
    Pick the contract_id from the returned array — the default contract has "default_contract": true.

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.

Passing contract_id and project in the request body causes the API to embed pricing (opencosmos:price and opencosmos:price_currency) directly in each returned item, so you can skip the separate pricing step.

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
# contract_id and project are included in the body to get prices in results
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,
    "contract_id": YOUR_CONTRACT_ID,
    "project": "YOUR_PROJECT_ID",
    "query": {
      "eo:cloudcover": {
        "lte": 20
      }
    }
  }')

# Extract the first result's collection, item ID, and price
COLLECTION=$(echo "$SEARCH_RESULTS" | jq -r '.features[0].collection')
ITEM_ID=$(echo "$SEARCH_RESULTS" | jq -r '.features[0].id')
PRICE=$(echo "$SEARCH_RESULTS" | jq -r '.features[0].properties["opencosmos:price"]')
CURRENCY=$(echo "$SEARCH_RESULTS" | jq -r '.features[0].properties["opencosmos:price_currency"]')

echo "Found item: ${COLLECTION} / ${ITEM_ID} (${PRICE} ${CURRENCY})"

  Python

# Search for Sentinel-2 images with low cloud cover
# contract_id and project are included in the body to get prices in results
search_body = {
    "collections": ["sentinel-2-l2a"],
    "bbox": [-71.72, -27.68, -71.02, -27.06],
    "limit": 5,
    "contract_id": YOUR_CONTRACT_ID,   # Replace with your contract ID
    "project": "YOUR_PROJECT_ID",      # Replace with your project ID
    "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"].get("opencosmos:price")
    currency = selected_item["properties"].get("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: Retrieve Pricing

NOTE

If you passed contract_id in the search request (Step 2), the price is already available as opencosmos:price and opencosmos:price_currency on each returned item. You can skip this step and use those values directly.

If you need to fetch the price separately — for example when pricing a specific sub-area geometry — call the dedicated pricing endpoint. The contract_id is required.

Endpoint

GET https://app.open-cosmos.com/api/data/v0/order/price

Bash

PRICE_RESPONSE=$(curl --request GET \
  "https://app.open-cosmos.com/api/data/v0/order/price?collection=${COLLECTION}&item=${ITEM_ID}&contract_id=YOUR_CONTRACT_ID" \
  --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}"

  Python

response = session.get(
    "https://app.open-cosmos.com/api/data/v0/order/price",
    params={
        "collection": collection,
        "item": item_id,
        "contract_id": YOUR_CONTRACT_ID,  # Replace with your contract ID
    },
)

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

NOTE

See the Pricing API page for full parameter details, including sub-area pricing with a custom geometry, and batch pricing for multiple items.


Step 4: Place an Order

Create an order for the selected item. If your organisation has sufficient credits, the order is paid automatically inside the create request and no further steps are needed. If credits do not cover the full amount, a Stripe checkout URL is returned and you must complete payment via the checkout endpoint.

4a. 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
  }")

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
}

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']})")

NOTE

Check the create response before proceeding:

  • If status is "PAID" and checkout_redirect_url is "" — your organisation's credits covered the order. It is already paid. Skip Step 4b and go directly to Step 5.
  • If status is "UNPAID" and checkout_redirect_url is non-empty — credits were insufficient. Proceed to Step 4b to complete payment via Stripe.

4b. Initiate Stripe Checkout (only if Step 4a returned an unpaid order)

This step is only needed when the create order response returned a non-empty checkout_redirect_url, meaning credits did not cover the full order total and a Stripe payment is required. Skip this step if the order is already PAID.

The checkout_url in the response tells you which path applies:

  • Empty string — the order has been paid with credits; proceed directly to Step 5.
  • Non-empty URL — direct the user to this Stripe checkout page to complete card payment.

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')

if [ -z "$CHECKOUT_URL" ]; then
  echo "Order completed with credits — no Stripe checkout required."
else
  echo "Complete payment at: ${CHECKOUT_URL}"
fi

  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"]

if not checkout_url:
    print("Order completed with credits — no Stripe checkout required.")
else:
    print(f"Complete payment at: {checkout_url}")

NOTE

See Purchasing Catalog Images for full details on the create order response, both payment paths, and order management.


Step 5: Download Data

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

5a. Wait for Payment Confirmation

NOTE

If the order was paid with credits in Step 4b, the status is already PAID and the first poll will return immediately. No waiting is needed in that case.

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)

5b. 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, "contract_id": CONTRACT_ID},
)
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,
}

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 (empty means order was paid with credits)
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"]
if not checkout_url:
    print("  Order completed with credits — no Stripe checkout required.\n")
else:
    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}&contract_id=${CONTRACT_ID}" \
  --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}
  }")

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')

if [ -z "$CHECKOUT_URL" ]; then
  echo "  Order completed with credits — no Stripe checkout required."
else
  echo "  Complete payment at: ${CHECKOUT_URL}"
fi
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