Uploading your own data

Geopera isn’t only a place to buy imagery — you can bring your own rasters (COGs and other geospatial files) into a project, where they become first-class STAC items + assets: searchable, renderable, clippable, and analysable like any other data.

The upload is a session with four operations. You reserve quota, get a signed URL, push bytes straight to storage, then finalize — which ingests the file into items and assets and runs the post-upload pipeline.

StepOperationSide-effectScope
Reserve a sessionuploads.initiatecomputeuploads:write
Get a signed URLuploads.signed_urlcomputeuploads:write
(optional) report progressuploads.progresscomputeuploads:write
Finalize → items + assetsuploads.completecomputeuploads:write
Abandonuploads.failcomputeuploads:write

Best practice — make it a COG. Render and analytics are fastest when the uploaded raster is a Cloud-Optimized GeoTIFF with overviews and embedded statistics. See the COG conventions referenced in Processing & analytics.

1. Initiate the session

uploads.initiate reserves storage quota up front (so a too-large upload fails fast with 402 rather than half-landing) and requires editor/admin on the target project.

bash
curl -s -X POST https://api.geopera.com/v1/op/uploads.initiate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "your-project-id",
    "file_count": 1,
    "total_bytes": 524288000,
    "transfer_method": "signed_url",
    "target_collection_id": "optional-collection-id"
  }'
json
{ "id": "u_8f12..." }

Inputs: project_id (required), file_count, total_bytes (used for the quota reservation), and optionally target_collection_id / target_item_id to ingest into an existing collection or item, plus asset_key and is_categorical hints. The returned id is the upload session id used by every later step.

If the org doesn’t have enough storage quota, this returns 402 Payment Required — upgrade or free space before retrying.

2. Get a signed URL

uploads.signed_url mints a short-lived, presigned URL for one file. You upload the bytes directly to object storage — they never pass through the API.

bash
curl -s -X POST https://api.geopera.com/v1/op/uploads.signed_url \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "upload_id": "u_8f12...",
    "file_name": "harbour_2024.tif",
    "content_type": "image/tiff"
  }'
json
{
  "upload_url": "https://storage.googleapis.com/...&X-Goog-Signature=...",
  "object_path": "uploads/u_8f12.../harbour_2024.tif"
}

3. Upload the bytes

PUT the file straight to upload_url:

bash
curl -X PUT \
  -H "Content-Type: image/tiff" \
  --upload-file ./harbour_2024.tif \
  "<upload_url>"

For multi-file uploads, request one signed URL per file (repeat step 2) and PUT each. Optionally report progress with uploads.progress (upload_id, status, bytes_uploaded) — useful to drive a progress bar or for resumable clients.

4. Complete the upload

uploads.complete finalizes the session: it creates the STAC item(s) and asset record(s) for the uploaded file(s) and runs the post-upload pipeline (which derives band names, statistics, and a default visualization profile so the data is immediately renderable).

bash
curl -s -X POST https://api.geopera.com/v1/op/uploads.complete \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "upload_id": "u_8f12..." }'
json
{ "id": "u_8f12...", "item_ids": ["it_3a9c..."] }

complete is a producer: each item it creates is recorded with a provenance edge back to the upload session, in the same transaction (see Provenance & lineage). A completion that produces no new item (already complete, metadata-only) is a legitimate no-op.

The new items now show up in items.search and can be rendered, clipped, and fed to analytics like any other item.

5. Abandon a session

If something goes wrong client-side, release the reservation with uploads.fail:

bash
curl -s -X POST https://api.geopera.com/v1/op/uploads.fail \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "upload_id": "u_8f12...",
    "error_message": "client aborted",
    "error_step": "transfer"
  }'

This marks the session failed and returns the reserved storage to your quota.