How to publish STAC data at eodc¶
This guide explains how customers can publish and manage their own data in the eodc-hosted STAC API endpoint (https://cstac.discovery.eodc.eu). The typical workflow consists of preparing STAC metadata, authenticating to the API, and creating STAC Collections and Items.
Prerequisites¶
Valid eodc user credentials to authenticate against the STAC API.
Your dataset described using valid STAC metadata:
Collection – describes a dataset
Items – describe individual scenes or data entries
Assets – links to the actual data files
Example structure:
dataset/
├── collection.json
└── items/
├── item_001.json
├── item_002.json
└── ...
Ready-to-use sample data is available in the eodc-examples GitHub repository, if you want to try the workflow before ingesting your own data.
Access control¶
Some metadata fields are required by the STAC catalogue. In particular, the access field defines who can see the data.
Example: visible to all users in an organisation
{
"access": {
"visibility": "organisation",
"allowed_users": [],
"allowed_organisations": ["eodc"]
}
}
Example: visible only to specific users
{
"access": {
"visibility": "user",
"allowed_users": ["john.smith@example.org", "jane.smith@example.org"],
"allowed_organisations": []
}
}
Example: visible to everyone, managed by all users with an email address at example.org
{
"access": {
"visibility": "public",
"allowed_users": [],
"allowed_organisations": [],
"allowed_domains": ["example.org"]
}
}
Example: visible only to members of a group
{
"access": {
"visibility": "group",
"allowed_groups": ["example-group"]
}
}
Groups are set up by eodc on request. Your group memberships are included in your access token, so you may need to log in again after being added to a group.
visibility is one of public, organisation, user, domain or group. For organisation, user, domain and group, the listed organisations, users, email domains or groups can both see and manage the collection. A public collection is visible to everyone, but only the owners listed in allowed_organisations, allowed_users, allowed_domains or allowed_groups can change or delete it, so it needs at least one entry there.
The access block of a public collection is visible to everyone as well. Name its owners by allowed_groups, allowed_domains or allowed_organisations rather than by individual email addresses.
If the access field is missing or configured incorrectly, the data may not be visible in the catalogue.
Authenticate¶
from getpass import getpass
from eodc_connect.auth import EODCConnection
import requests
username = input("Enter your eodc username: ")
password = getpass("Enter your eodc password: ")
conn = EODCConnection(username=username, password=password)
token = conn.get_access_token()
HEADERS = {
"Authorization": f"Bearer {token}",
"Accept": "application/json",
"Content-Type": "application/json",
}
Create a Collection¶
WRITE_BASE = "https://cstac.discovery.eodc.eu"
url = f"{WRITE_BASE}/collections"
r = requests.post(url, headers=HEADERS, json=collection)
r.raise_for_status()
Create Items¶
items_url = f"{WRITE_BASE}/collections/{collection_id}/items"
for item in items:
r = requests.post(items_url, headers=HEADERS, json=item)
r.raise_for_status()
Update an Item¶
Existing STAC Items can be updated using a PUT request:
Retrieve the current Item with
GETModify the desired fields (e.g. metadata or assets)
Replace the Item via
PUT
item_url = f"{WRITE_BASE}/collections/{collection_id}/items/{item_id}"
r = requests.get(item_url, headers=HEADERS)
r.raise_for_status()
current = r.json()
current.setdefault("properties", {})
current["properties"]["updated"] = "2026-07-21T12:00:00Z"
r = requests.put(item_url, headers=HEADERS, json=current)
r.raise_for_status()
Bulk ingest Items¶
For larger batches, use the bulk endpoint instead of one request per Item. It writes many Items to the same Collection in a single request:
POST /collections/{collection_id}/bulk_items
The request body holds two fields:
items– a dictionary mapping each Itemidto the full Item JSONmethod–"insert"(default; fails if any Item already exists) or"upsert"(inserts new Items and replaces existing ones)
bulk_url = f"{WRITE_BASE}/collections/{collection_id}/bulk_items"
payload = {
"items": {item["id"]: item for item in items},
"method": "upsert",
}
r = requests.post(bulk_url, headers=HEADERS, json=payload)
r.raise_for_status()
All Items must belong to the Collection in the URL. If the endpoint returns 404 or 405, bulk ingest is not enabled; create the Items one by one as shown above instead.
Large Collections¶
For Collections with many thousands of Items, split the ingestion into batches:
Send 100–500 Items per request. Larger requests take longer and are more likely to time out.
Set a client timeout of at least 120 seconds, e.g.
requests.post(..., timeout=120).Use
"method": "upsert", so that a batch can safely be sent again.Retry failed batches (
5xxresponses or timeouts) after a short pause. A timeout does not necessarily mean that the batch failed, so check the Item count of the Collection afterwards.Leave out
itemlinks in the Collection JSON. Items are linked automatically, and listing every Item in the Collection makes it very large and slow to load.
import time
def ingest(items, batch_size=200):
for i in range(0, len(items), batch_size):
batch = {item["id"]: item for item in items[i:i + batch_size]}
for attempt in range(5):
try:
r = requests.post(bulk_url, headers=HEADERS, json={"items": batch, "method": "upsert"}, timeout=120)
if r.status_code < 500:
r.raise_for_status()
break
except requests.exceptions.Timeout:
pass
time.sleep(10 * (attempt + 1))
else:
raise RuntimeError(f"Batch starting at item {i} failed")
To check the number of Items in a Collection:
r = requests.get(f"{WRITE_BASE}/search", headers=HEADERS, params={"collections": collection_id, "limit": 1})
print(r.json()["numberMatched"])
Delete a Collection¶
del_url = f"{WRITE_BASE}/collections/{collection_id}"
r = requests.delete(del_url, headers=HEADERS)
r.raise_for_status()
Common mistakes¶
Missing access metadata — if the access field is missing, the dataset may not be visible to other users.
Invalid STAC structure — make sure the metadata follows the STAC specification. The stac-validator tool can help catch issues before ingestion:
stac-validator collection.json
Incorrect asset links — assets must point to valid and accessible data files.
Troubleshooting¶
Item creation fails — check that the collection exists, item IDs are unique, and the metadata is valid JSON.
Data not visible in the catalogue — check the access settings, organisation name, and permissions.
Validation errors — run a STAC validator before ingestion.
Support¶
If you encounter problems publishing STAC data at eodc, please contact support@eodc.eu.
Worked example¶
See the Customer STAC ingestion notebook for a full runnable walkthrough — authenticating, loading sample data, creating a Collection and Items, updating an Item, bulk ingesting Items, and cleaning up.