First steps: complete an item transaction lifecycle

In this tutorial you will create a STAC Item, read it back, replace it using an ETag, and finally delete it. This gives you the core workflow used by the client.

You need:

  • Python 3.10 or newer;
  • a STAC API implementing the Transaction Extension;
  • a collection where you may create and delete an item.

The examples use https://stac.example.com and example-collection as placeholders. Substitute values for your service.

1. Install the client

Install the package with the PySTAC extra because the endpoint modules use pystac.Item directly:

python -m pip install "stac-transaction-api-client[pystac]"

2. Create a client

Create an unauthenticated client first:

from stac_transaction_api_client import Client

client = Client(base_url="https://stac.example.com")
collection_id = "example-collection"

If your service requires a bearer token, use AuthenticatedClient instead:

from stac_transaction_api_client import AuthenticatedClient

client = AuthenticatedClient(
    base_url="https://stac.example.com",
    token="your-access-token",
)
collection_id = "example-collection"

The endpoint calls are the same for both client classes.

3. Build a PySTAC Item

Create a small point item for the tutorial:

from datetime import datetime, timezone

from pystac import Item

item = Item(
    id="stac-transaction-client-tutorial",
    geometry={"type": "Point", "coordinates": [12.5, 41.9]},
    bbox=[12.5, 41.9, 12.5, 41.9],
    datetime=datetime.now(timezone.utc),
    properties={"tutorial:step": "created"},
)

The collection identifier is supplied in the request path. The Transaction Extension defines the server as responsible for populating the item's collection field from that path.

4. Create the item

Use post_feature.sync_detailed so you retain the status code and response headers in addition to the parsed body:

from http import HTTPStatus

from stac_transaction_api_client.api.transaction import post_feature

created = post_feature.sync_detailed(
    collection_id,
    client=client,
    body=item,
)

if created.status_code == HTTPStatus.CREATED:
    print("created")
elif created.status_code == HTTPStatus.ACCEPTED:
    print("accepted for asynchronous processing")
else:
    raise RuntimeError(f"create failed: {created.status_code}")

For the rest of this tutorial, use a service that completes the operation synchronously so the item can be fetched immediately.

5. Retrieve the item and its ETag

The current get_feature module exposes detailed sync and async calls. Fetch the item with sync_detailed:

from stac_transaction_api_client.api.transaction import get_feature

current = get_feature.sync_detailed(
    collection_id,
    item.id,
    client=client,
)

if current.parsed is None:
    raise RuntimeError(f"item was not returned: {current.status_code}")

print(current.parsed.id)

The Transaction Extension supports optimistic locking with ETags. The current client requires an if_match value for PUT and DELETE, so read the ETag from the detailed response:

etag = current.headers.get("ETag")
if etag is None:
    raise RuntimeError("the service did not return an ETag")

6. Replace the item

Change a property on the returned PySTAC Item and send the complete item back with update_feature:

from stac_transaction_api_client.api.transaction import update_feature

current.parsed.properties["tutorial:step"] = "replaced"

replaced = update_feature.sync_detailed(
    collection_id,
    current.parsed.id,
    client=client,
    body=current.parsed,
    if_match=etag,
)

if replaced.status_code not in {
    HTTPStatus.OK,
    HTTPStatus.ACCEPTED,
    HTTPStatus.NO_CONTENT,
}:
    raise RuntimeError(f"replace failed: {replaced.status_code}")

A stale ETag can result in 412 Precondition Failed. Fetch the resource again before retrying rather than reusing an old ETag.

7. Delete the item

Fetch the item again so the delete uses its current ETag:

latest = get_feature.sync_detailed(
    collection_id,
    item.id,
    client=client,
)
latest_etag = latest.headers.get("ETag")

if latest_etag is None:
    raise RuntimeError("the service did not return an ETag")

Delete it:

from stac_transaction_api_client.api.transaction import delete_feature

deleted = delete_feature.sync_detailed(
    collection_id,
    item.id,
    client=client,
    if_match=latest_etag,
)

if deleted.status_code not in {
    HTTPStatus.OK,
    HTTPStatus.ACCEPTED,
    HTTPStatus.NO_CONTENT,
}:
    raise RuntimeError(f"delete failed: {deleted.status_code}")

You have now completed the core transaction lifecycle.

Where to go next

Use Perform item transactions when you need focused recipes for authentication, patching, deleting, or asynchronous calls. Use the reference when you need exact function signatures and return types.