Build with HyperSR.
Find a Sentinel-2 acquisition, review the processing cost, and retrieve georeferenced multispectral products through the REST API or Python SDK.
Overview
HyperSR processes one supported Sentinel-2 Level-2A scene and a WGS84 bounding box per job. Discovery, quoting and processing are separate requests. A search costs no processing credits; confirming a quote reserves the required amount.
Connect to your deployment
The versioned base path is /v1. A configured local API commonly uses http://localhost:8000/v1; the intended hosted address is https://api.hypersr.com/v1. These examples describe the implemented contract and do not imply that a hosted service or model is enabled.
Download the customer OpenAPI specification to import into your API client. The endpoint reference below includes parameters, request fields and response examples on this page. FastAPI also serves its deployment schema at /openapi.json and interactive reference at /docs on the API origin.
Quickstart
Use Python 3.11 or newer. Install the published Python SDK in your project. No repository checkout is needed.
uv init my-hypersr-project
cd my-hypersr-project
uv add hypersr
# Save the Python example as quickstart.py, then run: uv run quickstart.py
# Existing pip environment: python -m pip install hypersrexport HYPERSR_API_KEY="<your-api-key>"
export HYPERSR_API_BASE="http://localhost:8000/v1"
# For a hosted deployment, use its configured HTTPS API address.Set HYPERSR_API_KEY and HYPERSR_API_BASE for your configured deployment. Discover enabled models first with GET /v1/models; use the model's slug in quotes. The example requires hypersr_v1 to be enabled, suitable scene coverage and enough credits.
import os
from uuid import uuid4
from hypersr import HyperSR
# Set these for your configured local, staging or production API.
with HyperSR(
api_key=os.environ["HYPERSR_API_KEY"],
base_url=os.environ["HYPERSR_API_BASE"],
) as client:
bbox = (2.10, 41.30, 2.12, 41.32) # replace with your area
search = client.scenes.search(
bbox=bbox, target_date="2024-01-10",
days_before=7, days_after=3,
max_cloud_cover=20, selection="nearest",
)
scene = search.selected
print("Acquisition:", scene.scene_id, scene.acquired_at)
print("Scene cloud cover:", scene.cloud_cover)
if input("Use this acquisition? [y/N] ").lower() != "y":
raise SystemExit("No processing requested.")
quote = client.jobs.quote(
scene_id=scene.scene_id, bbox=bbox, model="hypersr_v1",
)
print("Credits:", quote.required_credits / 1000)
print("Quote expires:", quote.expires_at)
if input("Confirm processing? [y/N] ").lower() != "y":
raise SystemExit("Quote was not submitted.")
# Persist this key if your application needs to retry after restarting.
key = str(uuid4())
job = client.jobs.submit(quote_id=quote.id, idempotency_key=key)
result = client.jobs.wait(job.id, timeout=1800, poll_interval=5)
if result.status != "completed":
raise RuntimeError(f"{result.status}: {result.error_message}")
for role in ("reflectance", "scl", "cloud_mask", "provenance"):
suffix = ".json" if role == "provenance" else ".tif"
client.jobs.download_role(job.id, role, f"{role}{suffix}")#!/usr/bin/env bash
set -euo pipefail
# Set HYPERSR_API_KEY and HYPERSR_API_BASE in your environment.
# Example local base: http://localhost:8000/v1
# These examples require curl and jq.
[ -n "${HYPERSR_API_KEY:-}" ] || { echo "Set HYPERSR_API_KEY first" >&2; exit 1; }
[ -n "${HYPERSR_API_BASE:-}" ] || { echo "Set HYPERSR_API_BASE first" >&2; exit 1; }
curl --connect-timeout 10 --max-time 30 --fail-with-body -sS "$HYPERSR_API_BASE/scenes/search" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"bbox":[2.1,41.3,2.12,41.32],"target_date":"2024-01-10","days_before":7,"days_after":3,"max_cloud_cover":20,"selection":"nearest"}' > search.json
# Review the actual acquisition before continuing.
jq '.selected' search.json
read -r -p "Use the selected acquisition? [y/N] " CONFIRM
[ "$CONFIRM" = "y" ] || exit 0
SCENE_ID=$(jq -r '.selected.scene_id' search.json)
BODY=$(jq -n --arg scene "$SCENE_ID" \
'{scene_id:$scene,bbox:[2.1,41.3,2.12,41.32],model:"hypersr_v1"}')
curl --connect-timeout 10 --max-time 30 --fail-with-body -sS "$HYPERSR_API_BASE/jobs/quote" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Content-Type: application/json" --data "$BODY" > quote.json
# Review required_credits / 1000 and expires_at. Run this ONLY to confirm.
jq . quote.json
read -r -p "Confirm processing? [y/N] " CONFIRM
[ "$CONFIRM" = "y" ] || exit 0
QUOTE_ID=$(jq -r '.quote_id' quote.json)
# Choose a unique key per operation; retain it for retries of this quote.
[ -n "${HYPERSR_REQUEST_KEY:-}" ] || { echo "Set HYPERSR_REQUEST_KEY first" >&2; exit 1; }
curl --connect-timeout 10 --max-time 30 --fail-with-body -sS "$HYPERSR_API_BASE/jobs" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Idempotency-Key: $HYPERSR_REQUEST_KEY" \
-H "Content-Type: application/json" \
--data "$(jq -n --arg q "$QUOTE_ID" '{quote_id:$q}')" > job.json
# Bound the polling period. A local timeout does not cancel the job.
POLL_DEADLINE=$((SECONDS + 1800))
while [ "$SECONDS" -lt "$POLL_DEADLINE" ]; do
STATUS=$(jq -r '.status' job.json)
case "$STATUS" in
completed) break ;;
failed|cancelled) jq . job.json; echo "Job ended: $STATUS" >&2; exit 1 ;;
esac
sleep 5
JOB_ID=$(jq -r '.job_id' job.json)
curl --connect-timeout 10 --max-time 30 --fail-with-body -sS "$HYPERSR_API_BASE/jobs/$JOB_ID" \
-H "Authorization: Bearer $HYPERSR_API_KEY" > job.json
done
[ "$(jq -r '.status' job.json)" = "completed" ] || { echo "Polling timed out" >&2; exit 1; }
JOB_ID=$(jq -r '.job_id' job.json)
curl --connect-timeout 10 --max-time 30 --fail-with-body -sS "$HYPERSR_API_BASE/jobs/$JOB_ID/outputs" \
-H "Authorization: Bearer $HYPERSR_API_KEY" > outputs.json
DOWNLOAD_URL=$(jq -r '.items[] | select(.role == "reflectance") | .download_url' outputs.json)
[ -n "$DOWNLOAD_URL" ] && [ "$DOWNLOAD_URL" != "null" ] || { echo "No reflectance output" >&2; exit 1; }
# The signed URL is already authenticated; do not attach the API bearer header.
DOWNLOAD_MAX_TIME="${HYPERSR_DOWNLOAD_TIMEOUT_SECONDS:-600}"
curl --connect-timeout 10 --max-time "$DOWNLOAD_MAX_TIME" --fail-with-body -sS "$DOWNLOAD_URL" -o reflectance.tifimport { createWriteStream } from "node:fs";
import { rename, rm } from "node:fs/promises";
import { Readable, Transform } from "node:stream";
import { pipeline } from "node:stream/promises";
import { createInterface } from "node:readline/promises";
import { createHash, randomUUID } from "node:crypto";
import { stdin as input, stdout as output } from "node:process";
const apiKey = process.env.HYPERSR_API_KEY;
const configuredBase = process.env.HYPERSR_API_BASE;
if (!apiKey || !configuredBase) {
throw new Error("Set HYPERSR_API_KEY and HYPERSR_API_BASE before running this script");
}
const apiBase = configuredBase.replace(/\/$/, "");
const headers = {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
};
async function request(path, options = {}) {
const response = await fetch(`${apiBase}${path}`, {
...options,
signal: AbortSignal.timeout(30_000),
headers: { ...headers, ...(options.headers ?? {}) },
});
const body = await response.json();
if (!response.ok) throw new Error(`${body.error?.code ?? response.status}: ${body.error?.message ?? "Request failed"}`);
return body;
}
const bbox = [2.10, 41.30, 2.12, 41.32];
const search = await request("/scenes/search", {
method: "POST",
body: JSON.stringify({ bbox, target_date: "2024-01-10", days_before: 7, days_after: 3, max_cloud_cover: 20, selection: "nearest" }),
});
console.log("Selected acquisition:", search.selected.scene_id, search.selected.acquired_at);
const prompts = createInterface({ input, output });
if ((await prompts.question("Continue with this acquisition? [y/N] ")).toLowerCase() !== "y") {
await prompts.close();
process.exit(0);
}
const quote = await request("/jobs/quote", {
method: "POST",
body: JSON.stringify({ scene_id: search.selected.scene_id, bbox, model: "hypersr_v1" }),
});
console.log(`Quote: ${quote.required_credits / 1000} credits; expires ${quote.expires_at}`);
if ((await prompts.question("Confirm processing? [y/N] ")).toLowerCase() !== "y") {
await prompts.close();
process.exit(0);
}
await prompts.close();
// Persist this key if the request may be retried after a network timeout.
const requestKey = process.env.HYPERSR_REQUEST_KEY ?? randomUUID();
const job = await request("/jobs", {
method: "POST",
headers: { "Idempotency-Key": requestKey },
body: JSON.stringify({ quote_id: quote.quote_id }),
});
console.log("Submitted job:", job.job_id);
const deadline = Date.now() + 30 * 60 * 1000;
let current = job;
while (!["completed", "failed", "cancelled"].includes(current.status)) {
if (Date.now() >= deadline) throw new Error(`Polling timed out for ${current.job_id}`);
await new Promise((resolve) => setTimeout(resolve, 5000));
current = await request(`/jobs/${current.job_id}`);
}
if (current.status !== "completed") throw new Error(current.error_message ?? current.status);
const products = await request(`/jobs/${current.job_id}/outputs`);
const reflectance = products.items.find(({ role }) => role === "reflectance");
if (!reflectance?.download_url) throw new Error("No reflectance output was returned");
// Signed URLs are separate credentials: do not send the API bearer header.
const downloadTimeout = Number(process.env.HYPERSR_DOWNLOAD_TIMEOUT_MS ?? 600_000);
const artifact = await fetch(reflectance.download_url, { signal: AbortSignal.timeout(downloadTimeout) });
if (!artifact.ok || !artifact.body) throw new Error(`Download failed: ${artifact.status}`);
const temporaryPath = `reflectance.tif.${randomUUID()}.part`;
const digest = createHash("sha256");
let bytes = 0;
const verifier = new Transform({
transform(chunk, encoding, callback) {
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding);
bytes += buffer.length;
digest.update(buffer);
callback(null, buffer);
},
});
try {
await pipeline(Readable.fromWeb(artifact.body), verifier, createWriteStream(temporaryPath));
if (reflectance.size_bytes != null && bytes !== reflectance.size_bytes) {
throw new Error(`Size mismatch: expected ${reflectance.size_bytes}, received ${bytes}`);
}
const checksum = reflectance.checksum?.toLowerCase().replace(/^sha256:/, "");
if (checksum && !/^[a-f0-9]{64}$/.test(checksum)) {
throw new Error("Unsupported SHA-256 checksum metadata");
}
if (checksum && digest.digest("hex") !== checksum) {
throw new Error("SHA-256 checksum mismatch");
}
await rename(temporaryPath, "reflectance.tif");
} catch (error) {
await rm(temporaryPath, { force: true });
throw error;
}Run the JavaScript example as a Node.js 22 .mjs script on a trusted machine. The cURL example uses Bash and jq. Both prompt before submitting the quote. A polling timeout leaves the job running; inspect the saved job ID before deciding what to do next.
The dashboard follows the same acquisition review → quote → explicit confirmation flow. Local mock-4x outputs are synthetic development artifacts. Dedicated GPU, real-provider processing and hosted storage acceptance are separate from local API and SDK checks.
Authentication
Customer requests use a HyperSR API key or a Supabase session access token in the Authorization header. Credentials identify the account; request bodies cannot select another owner.
Authorization: Bearer <HYPERSR_API_KEY>Create a named key in the dashboard or with POST /v1/api-keys. The secret is returned once; store it securely. Key lists expose only metadata and a prefix. Revoke an owned key with DELETE /v1/api-keys/{key_id}.
Keep API keys on trusted servers or in local environment variables. Do not put them in URLs, public JavaScript or logs. Authenticated API responses use Cache-Control: no-store. Signed storage URLs are separate credentials and must not receive your API bearer header.
Temporal search
POST /v1/scenes/search finds acquisitions around a target date. It returns a recommendation and ranked candidates before you quote or process anything. Review the actual acquisition timestamp: it can differ from your target date.
{
"bbox": [
2.1,
41.3,
2.12,
41.32
],
"target_date": "2024-01-10",
"days_before": 7,
"days_after": 3,
"max_cloud_cover": 20,
"selection": "nearest"
}| Field | Type / default | Description |
|---|---|---|
bbox | Required · four numbers | WGS84 west, south, east, north. One scene must cover the area. |
target_date | Required · YYYY-MM-DD | Target calendar date; nearest ranking uses 00:00:00 UTC on that date. |
days_before | Integer · 7 | Lookback from the target, inclusive. Allowed: 0–90. |
days_after | Integer · 3 | Lookahead from the target, inclusive. Allowed: 0–3. |
max_cloud_cover | Number · 100 | Strict scene cloud percentage, 0–100. Zero requires a measured 0%. |
selection | String · nearest | nearest or clearest. Selection mode is explicit. |
How acquisitions are ranked
Nearest: minimum time difference from target-date UTC midnight, then lower scene cloud cover, then scene ID. Clearest: lower scene cloud cover, then that time difference, then scene ID. Both modes enforce the cloud limit. Unknown cloud values rank after measured values and are excluded when the limit is below 100%.
The window includes all of its start and end calendar days. In the response, searched_start is inclusive and searched_end is the exclusive UTC midnight after the last included day. With a target of January 10, seven days before and three after, the window is January 3–13 inclusive.
eo:cloud_cover percentage. HyperSR does not currently measure clouds inside your requested AOI during search. A low scene percentage does not guarantee a clear AOI.{
"selected": {
"scene_id": "S2B_31TDF_20240112_0_L2A",
"acquired_at": "2024-01-12T10:49:39.287Z",
"cloud_cover": 0.012893
},
"candidates": [
{
"scene_id": "S2B_31TDF_20240112_0_L2A",
"acquired_at": "2024-01-12T10:49:39.287Z",
"cloud_cover": 0.012893
}
],
"target_date": "2024-01-10",
"searched_start": "2024-01-03T00:00:00Z",
"searched_end": "2024-01-14T00:00:00Z",
"cloud_cover_scope": "scene",
"selection": "nearest"
}selected is the first ranked candidate. Each candidate contains scene_id, the actual timezone-aware acquired_at, and a nullable cloud percentage. The example illustrates the shape with catalogue metadata, not an inference result; a full response can contain additional candidates.
No qualifying acquisition returns 404 SCENES_NOT_FOUND. Broaden the dates or cloud limit explicitly. Provider failures and incomplete searches return a CATALOG_* error; HyperSR does not present a truncated search as the nearest overall result.
Review, then quote the exact scene
Pass the reviewed scene_id and matching bounds to /jobs/quote. Recommendations are advisory; the quote establishes the exact processing choice. The worker does not silently substitute another date. Catalogue metadata screens coverage; source availability, bands and valid data still require ingestion checks.
Areas & inputs
Bounds are [west, south, east, north] in longitude/latitude degrees, EPSG:4326. Coordinates must be finite and ordered. Antimeridian-spanning boxes need to be split. The API calculates area geodetically and enforces its configured per-job minimum and maximum. The current default processing range is 1–10 km² per scene; saving an AOI does not bypass processing limits. Quickstart bounds cover approximately 3.72 km².
A scene ID is a provider catalogue identifier such as S2B_31TDF_20240112_0_L2A, not a download URL. The current ingestion provider is Earth Search's Sentinel-2 L2A collection. The requested footprint must be covered by a single supported scene. Arbitrary imagery URLs and uploads are not accepted by this API.
Omit bands or send an empty list to use the model defaults. Otherwise, choose a subset of its supported_bands. The supported output_format is cog.
Quotes & confirmation
POST /v1/jobs/quote creates an expiring, account-owned quote bound to the scene, area, model and bands. It does not reserve processing credits. Prices and processing limits come from the server. Every SR scene must cover at least 1 km² and at most the deployment's per-job limit; smaller areas are rejected before processing.
{
"scene_id": "S2B_31TDF_20240112_0_L2A",
"bbox": [
2.1,
41.3,
2.12,
41.32
],
"model": "hypersr_v1",
"bands": [
"B02",
"B03",
"B04",
"B08"
],
"output_format": "cog"
}{
"quote_id": "quote_example",
"model": "hypersr_v1",
"area_km2": 3.72,
"required_credits": 3720,
"expires_at": "2030-01-10T12:05:00Z"
}required_credits uses integer internal units: divide by 1,000 for displayed credits. The example's 3720 units mean 3.720 credits. The server quote is authoritative; approximate area or price calculations cannot replace it.
Confirm once, retry safely
POST /v1/jobs
Authorization: Bearer <HYPERSR_API_KEY>
Content-Type: application/json
Idempotency-Key: <unique-stable-operation-key>
{"quote_id":"quote_example"}Confirmation reserves credits and creates the queued job atomically. Reuse the same key and quote when retrying an uncertain submission. A key reused with different parameters returns 409 IDEMPOTENCY_CONFLICT. Use a new key for a new processing operation. Expired quotes require a new quote and another review.
Jobs & polling
Processing is asynchronous. Poll GET /v1/jobs/{job_id} or use client.jobs.wait(). A job includes its scene, model/version, bands, bounds, credit amounts, timestamps and any safe error details. Progress can be null; only reported processing progress should be displayed.
queued→claimed→running→completedTransient failures can move through retry_pending back to queued. Permanent failed and cancelled states are terminal, as is completed. Failed jobs release their reservations. Completion charges once.
POST /v1/jobs/{job_id}/cancel cancels jobs that are still queued or claimed. Later cancellation returns 409 JOB_NOT_CANCELLABLE. An SDK polling timeout ends your wait; it does not cancel the job.
GET /v1/jobs?offset=0&limit=20&status=completed&search=S2B
GET /v1/jobs/{job_id}
POST /v1/jobs/{job_id}/cancelJob lists return items and a filtered total. Use offset and limit (maximum 100), exact status, and a literal case-insensitive search over scene or job identifiers. Lists include only the current account's resources.
Outputs & downloads
New completed products contain four typed artifacts. Request GET /v1/jobs/{job_id}/outputs; use each item's role to select it. Older products can have no role field.
reflectanceMultispectral imageCOG on a 2.5 m grid, retaining supported source encoding and spatial metadata.sclScene classificationFull Sentinel-2 SCL, categorically resampled to the output grid.cloud_maskCloud flagsAligned binary mask for SCL cloud classes 8, 9 and 10.provenanceProcessing recordJSON with source scene, model, settings, encoding and validation metadata.{
"items": [
{
"role": "reflectance",
"format": "cog",
"filename": "reflectance.tif",
"size_bytes": 123456,
"checksum": "<sha256-hex>",
"download_url": "https://storage.example.invalid/object?signature=...",
"expires_at": "2030-01-10T12:15:00Z"
}
],
"total": 1
}Output metadata includes format, filename, size_bytes, SHA-256 checksum, download_url and URL expires_at. Download separately from the signed URL without forwarding the API Authorization header. Treat the URL as a secret until it expires.
URL expiry and product retention are separate. Request new links while the files are retained; expired products cannot receive fresh links. A job that has not completed returns an empty output list. The SDK writes downloads atomically and checks available size, checksum and expiry information.
Clouds and reflectance interpretation
Clouds, shadows and snow remain in model inputs. HyperSR does not claim cloud removal. True nodata and defective observations remain invalid. Use SCL and cloud flags in downstream analysis. Categorical masks use nearest sampling; they do not add fine-scale cloud observations.
Read each GeoTIFF's datatype, scale, offset, nodata and band descriptions. Internal normalization does not mean the downloaded values are normalized reflectance. Source DN encoding can be preserved; apply its scale and offset exactly once when converting to physical reflectance.
Models & bands
GET /v1/models lists enabled models with id, slug, version, optional bundle_sha256, supported_bands and output_resolution. Use slug as the quote's model; the database ID is a separate value.
The selected hypersr_v1 release supports ten Sentinel-2 bands. Availability still depends on the deployment's enabled catalogue. Read returned metadata rather than assuming all models or bands are available.
Native input spacing is 10 m or 20 m depending on the band. B10 is absent from Level-2A surface reflectance. B01 and B09, and other 60 m atmospheric inputs, are outside this model's contract. A 2.5 m output grid does not establish equivalent effective resolution or new observational information.
Python SDK
The typed HyperSR client uses a context manager for connection cleanup. The SDK defaults to the intended hosted base URL; pass base_url explicitly for local or staging work. HTTP timeouts, bounded retries and typed response models are included.
| Area | Client methods | Use |
|---|---|---|
| Discovery | models(), scenes.search(...) | Discover enabled models and review acquisitions. |
| Processing | jobs.quote(), submit(), get(), list(), cancel() | Submit only a reviewed, unexpired quote. |
| Results | jobs.wait(), outputs(), download_role() | Poll to a terminal state; download by stable artifact role. |
| Credits | credits(), credit_transactions() | Inspect balances, expiry lots and immutable movements. |
| Billing | billing_packages(), checkout(), billing_history() | Discover packages and create an explicit Checkout. |
| Workspace | aois(), get_aoi(), create_aoi(), update_aoi(), delete_aoi() | Manage reusable WGS84 boundaries. |
| Account | account(), update_account(), account_summary() | Read and update permitted account fields. |
| Preferences | notification_preferences(), update_notification_preferences() | Control completion and failure notices. |
| Keys | api_keys(), create_api_key(), revoke_api_key() | Store a newly returned key securely. |
Methods in the processing and results rows belong to client.jobs. Search uses client.scenes.search. Other methods are on client. API quote_id and job_id are available as SDK quote.id and job.id.
from hypersr import TimeoutError as HyperSRTimeoutError
try:
result = client.jobs.wait(job.id, timeout=1800, poll_interval=5)
except HyperSRTimeoutError:
print("Still waiting; inspect later:", job.id)
else:
print(result.status, result.error_code, result.error_message)Process multiple reviewed scenes
Each scene needs its own quote and idempotency key, and consumes credits separately. This sequential example prompts for each quote and keeps requests within the deployment's queue limits. Supply scene IDs you have already reviewed and that cover the same bounds.
import os
from uuid import uuid4
from hypersr import HyperSR
scene_ids = os.environ["HYPERSR_SCENE_IDS"].split(",")
bbox = (2.10, 41.30, 2.12, 41.32)
with HyperSR(os.environ["HYPERSR_API_KEY"],
base_url=os.environ["HYPERSR_API_BASE"]) as client:
for scene_id in scene_ids:
quote = client.jobs.quote(
scene_id=scene_id.strip(), bbox=bbox, model="hypersr_v1",
)
print(scene_id, "credits:", quote.required_credits / 1000,
"expires:", quote.expires_at)
if input("Confirm this quote? [y/N] ").lower() != "y":
continue
key = str(uuid4()) # persist per operation for recovery/retries
job = client.jobs.submit(quote_id=quote.id, idempotency_key=key)
result = client.jobs.wait(job.id, timeout=1800, poll_interval=5)
if result.status != "completed":
print(job.id, result.status, result.error_message)
continue
client.jobs.download_role(job.id, "reflectance", f"{job.id}.tif")Read reflectance and band metadata
Install uv add "hypersr[rasterio]" to inspect a downloaded COG. Read the delivered band descriptions, scales, offsets and validity mask. Apply scale and offset exactly once; cloud and classification artifacts remain separate.
import rasterio
with rasterio.open("reflectance.tif") as dataset:
print("CRS:", dataset.crs, "grid:", dataset.res)
print("Bands:", dataset.descriptions)
print("Encoding:", dataset.dtypes, dataset.scales, dataset.offsets)
# Iterate windows to bound memory use for large images.
for _, window in dataset.block_windows(1):
band = dataset.read(1, window=window, masked=True)
reflectance = band * dataset.scales[0] + dataset.offsets[0]
# Analyze valid observations; consult scl.tif and cloud_mask.tif too.
print(window, reflectance.count())Handle an API error
from hypersr import APIError, RequestError
try:
quote = client.jobs.quote(
scene_id=scene_id, bbox=bbox, model="hypersr_v1",
)
except APIError as error:
print(error.code, str(error), error.request_id)
# Correct invalid inputs before retrying. Keep credentials out of logs.
except RequestError:
print("Transport failed; check connectivity before retrying.")QGIS integration
The local QGIS connector supports catalogue search, native-source downloads, map/layer areas, quote review, job monitoring and loading downloaded rasters. Its current catalogue search uses explicit start/end dates and manual scene selection; the target-date search above is available through the REST API, Python SDK and dashboard. See the QGIS installation guide for packaging and desktop acceptance details.
Credits & billing
All API credit amounts use integer internal units. 1 displayed credit = 1,000 units; one displayed credit covers 1 km² at the base model rate. The configured global price is €0.25/km², with no subscriptions or automatic renewal. Model multipliers and geodetic footprint rounding are reflected in the server quote.
GET /v1/credits returns available, reserved, balance, total, credit_lots and nullable next_expiry. Each lot reports remaining units and its actual expiry. Purchases and trial grants expire six calendar months after issue. New purchases do not extend older lots.
Reservations accepted before expiry can settle later. Failure or cancellation releases the original allocation with its original expiry; an already expired release cannot fund new work. Use /credits/transactions for immutable signed ledger entries.
Discover current top-ups with GET /v1/billing/packages. The configured packs are €10/40 credits, €25/100 credits and €100/400 credits; API credit quantities are internal units. The verified-account trial is configured as 10 credits. Availability and eligibility are enforced by the service.
POST /v1/billing/checkout
Authorization: Bearer <HYPERSR_API_KEY>
Idempotency-Key: <unique-stable-checkout-key>
Content-Type: application/json
{"package_id":"<id-from-billing-packages>"}The response supplies a hosted Checkout URL. Reusing a Checkout key with different parameters returns 409 IDEMPOTENCY_KEY_CONFLICT; job submission uses IDEMPOTENCY_CONFLICT. Payment completion is confirmed through verified provider events, not the browser redirect. Read /billing/history for your account's payment records. Test-mode configuration and live payments are separate deployment steps.
Account & workspace
Saved areas: create with name, bbox and optional description. Names are trimmed and limited to 120 characters; descriptions to 500. Read, update or delete by the saved area's ID. Saving an area neither quotes nor processes imagery, so saved AOIs may remain smaller than the 1 km² SR minimum until used for a quote.
Profile: GET /account reads your account; PATCH /account accepts only display_name. Identity and authorization fields are server controlled. /account/summary provides customer-safe workspace aggregates.
Notifications: GET/PATCH /account/notifications uses the boolean fields job_completed and job_failed. Preferences default to enabled, while actual email delivery depends on the deployment's configured provider. Unknown fields and explicit nulls are rejected.
Jobs, areas, keys, payments and output access are scoped to your authenticated account. Missing and other-account resource IDs are not a way to discover another account's data.
Errors & limits
Failures use an error envelope. Keep the returned request_id or X-Request-ID header for troubleshooting, without recording credentials or signed URLs.
{
"error": {
"code": "QUOTE_EXPIRED",
"message": "Quote is missing or expired",
"request_id": "request_example"
}
}| HTTP | Examples | Action |
|---|---|---|
| 400 / 422 | INVALID_REQUEST, INVALID_SCENE, INVALID_AOI, AOI_TOO_LARGE, UNSUPPORTED_BANDS | Correct inputs; do not repeat the same invalid request. |
| 401 / 403 | Authentication or authorization failure | Check the credential, session and account permissions. |
| 402 | INSUFFICIENT_CREDITS | Check available credits or reduce the processing area. |
| 404 | SCENES_NOT_FOUND, JOB_NOT_FOUND | Adjust search criteria or check your own resource ID. |
| 409 | IDEMPOTENCY_CONFLICT, JOB_NOT_CANCELLABLE | Inspect the original operation or current job state. |
| 429 | RATE_LIMIT_EXCEEDED, USER_JOB_LIMIT | Back off; respect Retry-After when supplied. |
| 502 / 503 | CATALOG_*, SUBMISSIONS_PAUSED, storage/billing availability | Retry temporary failures with bounded backoff; retain submission keys. |
The SDK exposes AuthenticationError, ValidationError, NotFoundError, ConflictError, RateLimitError, RequestError and TimeoutError. Inspect the error code as well as the HTTP status.
Safe reads retry selected transient failures with bounded backoff. Mutation retries require an idempotency key, and not every endpoint guarantees duplicate-operation suppression. Submission and Checkout explicitly enforce idempotency. Do not blindly replay key or AOI creation.
Page sizes, AOI area, queue capacity, quote lifetime, rate limits and output retention are deployment controlled. Offset-paginated lists are bounded to 100 items per page. Acquisition candidates use a bounded catalogue search rather than account-history pagination.
Endpoint reference
Paths below are relative to /v1. “Bearer + key” requires both Authorization and an Idempotency-Key header. Public catalogue visibility does not establish that processing is commercially enabled.
Choose an operation for its copyable cURL request, field types, constraints and illustrative JSON response. Replace example IDs with values returned by your deployment. Monetary and credit values in examples are illustrative.
/healthGET/modelsPOST/scenes/searchPOST/jobs/quotePOST/jobsGET/jobsGET/jobs/{job_id}POST/jobs/{job_id}/cancelGET/jobs/{job_id}/outputsGET/outputs/{output_id}/downloadGET/creditsGET/credits/transactionsGET/billing/packagesPOST/billing/checkoutGET/billing/historyGET/api-keysPOST/api-keysDELETE/api-keys/{key_id}GET/accountPATCH/accountGET/account/summaryGET/account/notificationsPATCH/account/notificationsGET/aoisPOST/aoisGET/aois/{aoi_id}PATCH/aois/{aoi_id}DELETE/aois/{aoi_id}GET/healthHealth
GET /v1/health
Liveness: {status: "ok"}.
Authentication: Public.
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/health"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
status | string | Yes | — |
{
"status": "ok"
}Error responses
| HTTP | Error codes |
|---|---|
| 413 | REQUEST_TOO_LARGE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/modelsModels
GET /v1/models
Enabled models in {items, total}; use slug in quote requests.
Authentication: Public.
The enabled model catalogue is deployment-specific. Use slug in quote requests.
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/models"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
items | array<object> | Yes | — |
items[].bundle_sha256 | string | null | No | — |
items[].description | string | Yes | — |
items[].display_name | string | Yes | — |
items[].enabled | boolean | Yes | — |
items[].id | string | Yes | — |
items[].output_resolution | number | Yes | — |
items[].slug | string | Yes | — |
items[].supported_bands | array<string> | Yes | — |
items[].version | string | Yes | — |
total | integer | Yes | — |
{
"items": [
{
"id": "model_example",
"slug": "hypersr_v1",
"version": "1.0.0+sen2neon.20261110",
"bundle_sha256": "<sha256-hex>",
"display_name": "HyperSR v1",
"description": "Enabled multispectral model",
"supported_bands": [
"B02",
"B03",
"B04",
"B05",
"B06",
"B07",
"B08",
"B8A",
"B11",
"B12"
],
"output_resolution": 2.5,
"enabled": true
}
],
"total": 1
}Error responses
| HTTP | Error codes |
|---|---|
| 413 | REQUEST_TOO_LARGE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationPOST/scenes/searchSearch Scene Catalog
POST /v1/scenes/search
Read-only acquisition discovery. See temporal search parameters.
Authentication: Bearer.
Discovery is read-only and does not reserve credits.
JSON request body
| Field | Type | Required | Constraints |
|---|---|---|---|
bbox | array<number> | Yes | Min items: 4 · Max items: 4 |
days_after | integer | No | Default: 3 · Minimum: 0 · Maximum: 3 |
days_before | integer | No | Default: 7 · Minimum: 0 · Maximum: 90 |
max_cloud_cover | number | No | Default: 100 · Minimum: 0 · Maximum: 100 |
selection | "nearest" | "clearest" | No | Default: "nearest" |
target_date | string (date) | Yes | — |
curl --fail-with-body -sS -X POST "$HYPERSR_API_BASE/scenes/search" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"bbox":[2.1,41.3,2.12,41.32],"target_date":"2024-01-10","days_before":7,"days_after":3,"max_cloud_cover":20,"selection":"nearest"}'Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
candidates | array<object> | Yes | — |
candidates[].acquired_at | string (date-time) | Yes | — |
candidates[].cloud_cover | number | null | Yes | — |
candidates[].scene_id | string | Yes | — |
cloud_cover_scope | "scene" | Yes | — |
searched_end | string (date-time) | Yes | — |
searched_start | string (date-time) | Yes | — |
selected | object | Yes | — |
selected.acquired_at | string (date-time) | Yes | — |
selected.cloud_cover | number | null | Yes | — |
selected.scene_id | string | Yes | — |
selection | "nearest" | "clearest" | Yes | — |
target_date | string (date) | Yes | — |
{
"selected": {
"scene_id": "S2B_31TDF_20240112_0_L2A",
"acquired_at": "2024-01-12T10:49:39.287Z",
"cloud_cover": 0.012893
},
"candidates": [
{
"scene_id": "S2B_31TDF_20240112_0_L2A",
"acquired_at": "2024-01-12T10:49:39.287Z",
"cloud_cover": 0.012893
}
],
"target_date": "2024-01-10",
"searched_start": "2024-01-03T00:00:00Z",
"searched_end": "2024-01-14T00:00:00Z",
"cloud_cover_scope": "scene",
"selection": "nearest"
}Error responses
| HTTP | Error codes |
|---|---|
| 400 | INVALID_DATE_WINDOW |
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 404 | SCENES_NOT_FOUND |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUESTINVALID_AOIAOI_TOO_LARGE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
| 502 | CATALOG_INVALID_METADATACATALOG_INVALID_PAGINATIONCATALOG_INVALID_RESPONSECATALOG_REQUEST_FAILEDCATALOG_RESPONSE_TOO_LARGECATALOG_RESULT_LIMITCATALOG_TRUNCATED |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationPOST/jobs/quoteCreate Quote
POST /v1/jobs/quote
Bind scene_id, bbox, model, optional bands and output_format="cog".
Authentication: Bearer.
The server quote is authoritative and expires at expires_at.
JSON request body
| Field | Type | Required | Constraints |
|---|---|---|---|
bands | array<string> | No | — |
bbox | array<number> | Yes | Min items: 4 · Max items: 4 |
model | string | Yes | — |
output_format | "cog" | No | Default: "cog" |
scene_id | string | Yes | Min length: 3 · Max length: 200 · Pattern: ^[A-Za-z0-9_.:-]+$ |
curl --fail-with-body -sS -X POST "$HYPERSR_API_BASE/jobs/quote" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"scene_id":"S2B_31TDF_20240112_0_L2A","bbox":[2.1,41.3,2.12,41.32],"model":"hypersr_v1","bands":["B02","B03","B04","B08"],"output_format":"cog"}'Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
area_km2 | number | Yes | — |
expires_at | string (date-time) | Yes | — |
model | string | Yes | — |
quote_id | string | Yes | — |
required_credits | integer | Yes | — |
{
"quote_id": "quote_example",
"model": "hypersr_v1",
"area_km2": 3.72,
"required_credits": 3720,
"expires_at": "2030-01-10T12:05:00Z"
}Error responses
| HTTP | Error codes |
|---|---|
| 400 | INVALID_SCENEMODEL_UNAVAILABLEUNSUPPORTED_BANDSINVALID_AOI |
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUESTINVALID_SCENEINVALID_AOI |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationPOST/jobsCreate Job
POST /v1/jobs
Confirm {quote_id}. Requires Idempotency-Key. Returns a queued job.
Authentication: Bearer + key. Keep the idempotency key stable for retries of the same operation.
Keep the same Idempotency-Key when retrying an uncertain submission.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
Idempotency-Key | header | string | Required · Unique key that makes a retry safe and deterministic. · Min length: 1 · Max length: 255 |
JSON request body
| Field | Type | Required | Constraints |
|---|---|---|---|
quote_id | string | Yes | — |
curl --fail-with-body -sS -X POST "$HYPERSR_API_BASE/jobs" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Idempotency-Key: $HYPERSR_REQUEST_KEY" \
-H "Content-Type: application/json" \
--data '{"quote_id":"quote_example"}'Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
bbox | array<number> | null | No | — |
charged_credits | integer | Yes | — |
completed_at | string (date-time) | null | No | — |
created_at | string (date-time) | Yes | — |
error_code | string | null | No | — |
error_message | string | null | No | — |
input_scene_id | string | null | No | — |
job_id | string | Yes | — |
model | string | null | No | — |
model_version | string | null | No | — |
progress | number | null | No | — |
quoted_credits | integer | No | Default: 0 |
requested_bands | array<string> | No | — |
reserved_credits | integer | Yes | — |
started_at | string (date-time) | null | No | — |
status | string | Yes | — |
{
"job_id": "job_example",
"status": "queued",
"reserved_credits": 3720,
"charged_credits": 0,
"progress": null,
"created_at": "2030-01-10T12:05:00Z",
"started_at": null,
"completed_at": null,
"error_code": null,
"error_message": null,
"input_scene_id": "S2B_31TDF_20240112_0_L2A",
"bbox": [
2.1,
41.3,
2.12,
41.32
],
"model": "hypersr_v1",
"model_version": "1.0.0+sen2neon.20261110",
"requested_bands": [
"B02",
"B03",
"B04",
"B08"
],
"quoted_credits": 3720
}Error responses
| HTTP | Error codes |
|---|---|
| 400 | IDEMPOTENCY_KEY_REQUIREDQUOTE_EXPIREDMODEL_UNAVAILABLEINVALID_AOI |
| 401 | UNAUTHENTICATED |
| 402 | INSUFFICIENT_CREDITS |
| 403 | FORBIDDEN |
| 409 | IDEMPOTENCY_CONFLICT |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDEDUSER_JOB_LIMIT |
| 500 | INTERNAL_ERROR |
| 503 | SUBMISSIONS_PAUSED |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/jobsList Jobs
GET /v1/jobs
Own jobs; offset, limit, exact status and literal scene/job search.
Authentication: Bearer.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
offset | query | integer | Optional · Default: 0 · Minimum: 0 |
limit | query | integer | Optional · Default: 50 · Minimum: 1 · Maximum: 100 |
status | query | string | null | Optional |
search | query | string | null | Optional |
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/jobs?offset=0&limit=20&status=completed&search=S2B" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
items | array<object> | Yes | — |
items[].bbox | array<number> | null | No | — |
items[].charged_credits | integer | Yes | — |
items[].completed_at | string (date-time) | null | No | — |
items[].created_at | string (date-time) | Yes | — |
items[].error_code | string | null | No | — |
items[].error_message | string | null | No | — |
items[].input_scene_id | string | null | No | — |
items[].job_id | string | Yes | — |
items[].model | string | null | No | — |
items[].model_version | string | null | No | — |
items[].progress | number | null | No | — |
items[].quoted_credits | integer | No | Default: 0 |
items[].requested_bands | array<string> | No | — |
items[].reserved_credits | integer | Yes | — |
items[].started_at | string (date-time) | null | No | — |
items[].status | string | Yes | — |
total | integer | Yes | — |
{
"items": [],
"total": 0
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/jobs/{job_id}Get Job
GET /v1/jobs/{job_id}
Job state, scene, model/version, bands, bounds, credits and timestamps.
Authentication: Bearer.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
job_id | path | string | Required |
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/jobs/$JOB_ID" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
bbox | array<number> | null | No | — |
charged_credits | integer | Yes | — |
completed_at | string (date-time) | null | No | — |
created_at | string (date-time) | Yes | — |
error_code | string | null | No | — |
error_message | string | null | No | — |
input_scene_id | string | null | No | — |
job_id | string | Yes | — |
model | string | null | No | — |
model_version | string | null | No | — |
progress | number | null | No | — |
quoted_credits | integer | No | Default: 0 |
requested_bands | array<string> | No | — |
reserved_credits | integer | Yes | — |
started_at | string (date-time) | null | No | — |
status | string | Yes | — |
{
"job_id": "job_example",
"status": "completed",
"reserved_credits": 0,
"charged_credits": 3720,
"progress": 1,
"created_at": "2030-01-10T12:05:00Z",
"started_at": "2030-01-10T12:05:02Z",
"completed_at": "2030-01-10T12:07:30Z",
"error_code": null,
"error_message": null,
"input_scene_id": "S2B_31TDF_20240112_0_L2A",
"bbox": [
2.1,
41.3,
2.12,
41.32
],
"model": "hypersr_v1",
"model_version": "1.0.0+sen2neon.20261110",
"requested_bands": [
"B02",
"B03",
"B04",
"B08"
],
"quoted_credits": 3720
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 404 | JOB_NOT_FOUND |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationPOST/jobs/{job_id}/cancelCancel Job
POST /v1/jobs/{job_id}/cancel
Cancel queued or claimed jobs and release reserved credits.
Authentication: Bearer.
Only queued and claimed jobs can be cancelled.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
job_id | path | string | Required |
curl --fail-with-body -sS -X POST "$HYPERSR_API_BASE/jobs/$JOB_ID/cancel" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
bbox | array<number> | null | No | — |
charged_credits | integer | Yes | — |
completed_at | string (date-time) | null | No | — |
created_at | string (date-time) | Yes | — |
error_code | string | null | No | — |
error_message | string | null | No | — |
input_scene_id | string | null | No | — |
job_id | string | Yes | — |
model | string | null | No | — |
model_version | string | null | No | — |
progress | number | null | No | — |
quoted_credits | integer | No | Default: 0 |
requested_bands | array<string> | No | — |
reserved_credits | integer | Yes | — |
started_at | string (date-time) | null | No | — |
status | string | Yes | — |
{
"job_id": "job_example",
"status": "cancelled",
"reserved_credits": 0,
"charged_credits": 0,
"progress": null,
"created_at": "2030-01-10T12:05:00Z",
"started_at": null,
"completed_at": null,
"error_code": null,
"error_message": null,
"input_scene_id": "S2B_31TDF_20240112_0_L2A",
"bbox": [
2.1,
41.3,
2.12,
41.32
],
"model": "hypersr_v1",
"model_version": "1.0.0+sen2neon.20261110",
"requested_bands": [
"B02",
"B03",
"B04",
"B08"
],
"quoted_credits": 3720
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 404 | JOB_NOT_FOUND |
| 409 | JOB_NOT_CANCELLABLE |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/jobs/{job_id}/outputsOutputs
GET /v1/jobs/{job_id}/outputs
Retained outputs in {items, total}, with fresh signed URLs.
Authentication: Bearer.
New products expose reflectance, SCL, cloud_mask and provenance roles.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
job_id | path | string | Required |
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/jobs/$JOB_ID/outputs" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
items | array<object> | Yes | — |
items[].checksum | string | null | No | — |
items[].download_url | string | Yes | — |
items[].expires_at | string (date-time) | null | Yes | — |
items[].filename | string | Yes | — |
items[].format | string | Yes | — |
items[].role | "reflectance" | "scl" | "cloud_mask" | "provenance" | null | No | — |
items[].size_bytes | integer | null | Yes | — |
total | integer | Yes | — |
{
"items": [
{
"role": "reflectance",
"format": "cog",
"filename": "reflectance.tif",
"size_bytes": 123456,
"checksum": "<sha256-hex>",
"download_url": "https://storage.example.invalid/object?signature=...",
"expires_at": "2030-01-10T12:15:00Z"
},
{
"role": "scl",
"format": "cog",
"filename": "scl.tif",
"size_bytes": 45678,
"checksum": "<sha256-hex>",
"download_url": "https://storage.example.invalid/object?signature=...",
"expires_at": "2030-01-10T12:15:00Z"
},
{
"role": "cloud_mask",
"format": "cog",
"filename": "cloud_mask.tif",
"size_bytes": 23456,
"checksum": "<sha256-hex>",
"download_url": "https://storage.example.invalid/object?signature=...",
"expires_at": "2030-01-10T12:15:00Z"
},
{
"role": "provenance",
"format": "json",
"filename": "provenance.json",
"size_bytes": 3456,
"checksum": "<sha256-hex>",
"download_url": "https://storage.example.invalid/object?signature=...",
"expires_at": "2030-01-10T12:15:00Z"
}
],
"total": 4
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 404 | JOB_NOT_FOUND |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
| 503 | STORAGE_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/outputs/{output_id}/downloadDownload Output
GET /v1/outputs/{output_id}/download
Download one retained artifact; use the signed URL returned by the outputs endpoint.
Authentication: Signed URL.
Use the complete signed download_url from the outputs response; do not add the API bearer token.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
output_id | path | string | Required |
expires | query | integer | Required |
signature | query | string | Required |
curl --fail-with-body --location "$DOWNLOAD_URL" --output artifact.tif
# Use download_url verbatim; it already includes its signature and expiry.Success response · HTTP 200
Binary artifact (image/tiff or application/json)Error responses
| HTTP | Error codes |
|---|---|
| 403 | DOWNLOAD_URL_INVALIDOUTPUT_FORBIDDEN |
| 404 | OUTPUT_NOT_FOUND |
| 410 | OUTPUT_EXPIRED |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
| 503 | STORAGE_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/creditsCredits
GET /v1/credits
Integer available/reserved balances, credit_lots and next_expiry.
Authentication: Bearer.
Amounts are integer internal units; 1 displayed credit equals 1,000 units.
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/credits" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
available | integer | Yes | — |
balance | integer | Yes | — |
credit_lots | array<object> | Yes | — |
credit_lots[].expires_at | string (date-time) | null | Yes | — |
credit_lots[].remaining | integer | Yes | — |
next_expiry | object | null | Yes | — |
next_expiry.amount | integer | Yes | — |
next_expiry.expires_at | string (date-time) | null | Yes | — |
reserved | integer | Yes | — |
total | integer | Yes | — |
{
"balance": 10000,
"available": 10000,
"reserved": 0,
"total": 10000,
"credit_lots": [
{
"remaining": 10000,
"expires_at": "2030-07-10T00:00:00Z"
}
],
"next_expiry": {
"amount": 10000,
"expires_at": "2030-07-10T00:00:00Z"
}
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/credits/transactionsCredit Transactions
GET /v1/credits/transactions
Immutable signed ledger movements; offset and limit.
Authentication: Bearer.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
offset | query | integer | Optional · Default: 0 · Minimum: 0 |
limit | query | integer | Optional · Default: 50 · Minimum: 1 · Maximum: 100 |
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/credits/transactions?offset=0&limit=50" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
items | array<object> | Yes | — |
items[].amount | integer | Yes | — |
items[].created_at | string (date-time) | Yes | — |
items[].id | string | Yes | — |
items[].job_id | string | null | Yes | — |
items[].metadata | object | No | — |
items[].payment_id | string | null | Yes | — |
items[].transaction_type | string | Yes | — |
total | integer | Yes | — |
{
"items": [
{
"id": "ledger_example",
"amount": 10000,
"transaction_type": "trial_grant",
"job_id": null,
"payment_id": null,
"created_at": "2030-01-10T12:00:00Z",
"metadata": {}
}
],
"total": 1
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/billing/packagesPackages
GET /v1/billing/packages
Configured packs: id, name, amount_cents, currency and credit units.
Authentication: Bearer.
Packages are deployment-configured; do not hard-code prices in clients.
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/billing/packages" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
items | array<object> | Yes | — |
items[].amount_cents | integer | Yes | — |
items[].credits | integer | Yes | — |
items[].currency | string | Yes | — |
items[].id | string | Yes | — |
items[].name | string | Yes | — |
{
"items": [
{
"id": "starter",
"name": "Starter",
"amount_cents": 1000,
"credits": 40000,
"currency": "eur"
}
]
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
| 503 | BILLING_MISCONFIGURED |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationPOST/billing/checkoutCheckout
POST /v1/billing/checkout
Create Checkout from {package_id}; requires Idempotency-Key.
Authentication: Bearer + key. Keep the idempotency key stable for retries of the same operation.
Credits are granted only after verified payment events.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
Idempotency-Key | header | string | Required · Unique key that makes a retry safe and deterministic. · Min length: 1 · Max length: 255 |
JSON request body
| Field | Type | Required | Constraints |
|---|---|---|---|
package_id | string | Yes | — |
curl --fail-with-body -sS -X POST "$HYPERSR_API_BASE/billing/checkout" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Idempotency-Key: $HYPERSR_REQUEST_KEY" \
-H "Content-Type: application/json" \
--data '{"package_id":"starter"}'Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
checkout_url | string | Yes | — |
session_id | string | Yes | — |
{
"checkout_url": "https://checkout.stripe.com/c/pay/cs_test_...",
"session_id": "cs_test_example"
}Error responses
| HTTP | Error codes |
|---|---|
| 400 | IDEMPOTENCY_KEY_REQUIREDINVALID_PACKAGE |
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 409 | IDEMPOTENCY_KEY_CONFLICT |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
| 502 | BILLING_PROVIDER_ERROR |
| 503 | BILLING_UNAVAILABLEBILLING_MISCONFIGURED |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/billing/historyHistory
GET /v1/billing/history
Own payment history; offset and limit.
Authentication: Bearer.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
offset | query | integer | Optional · Default: 0 |
limit | query | integer | Optional · Default: 50 |
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/billing/history?offset=0&limit=50" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
items | array<object> | Yes | — |
items[].amount | integer | Yes | — |
items[].created_at | string (date-time) | Yes | — |
items[].credits | integer | Yes | — |
items[].currency | string | Yes | — |
items[].id | string | Yes | — |
items[].status | string | Yes | — |
total | integer | Yes | — |
{
"items": [
{
"id": "payment_example",
"amount": 1000,
"credits": 40000,
"currency": "eur",
"status": "paid",
"created_at": "2030-01-10T12:00:00Z"
}
],
"total": 1
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/api-keysList Api Keys
GET /v1/api-keys
Key metadata and prefixes; no plaintext secret.
Authentication: Bearer.
Plaintext secrets are never returned by this endpoint.
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/api-keys" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
items | array<object> | Yes | — |
items[].created_at | string (date-time) | Yes | — |
items[].id | string | Yes | — |
items[].key_prefix | string | Yes | — |
items[].last_used_at | string (date-time) | null | Yes | — |
items[].name | string | Yes | — |
items[].revoked_at | string (date-time) | null | Yes | — |
total | integer | Yes | — |
{
"items": [
{
"id": "key_example",
"name": "production-worker",
"key_prefix": "hs_live_abc123",
"created_at": "2030-01-10T12:00:00Z",
"last_used_at": null,
"revoked_at": null
}
],
"total": 1
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationPOST/api-keysCreate Api Key
POST /v1/api-keys
Create from {name}; secret appears once in this response.
Authentication: Bearer.
The secret is shown once. Store it securely before leaving the response.
JSON request body
| Field | Type | Required | Constraints |
|---|---|---|---|
name | string | Yes | Min length: 1 · Max length: 120 |
curl --fail-with-body -sS -X POST "$HYPERSR_API_BASE/api-keys" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"name":"production-worker"}'Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
created_at | string (date-time) | Yes | — |
id | string | Yes | — |
key_prefix | string | Yes | — |
last_used_at | string (date-time) | null | Yes | — |
name | string | Yes | — |
revoked_at | string (date-time) | null | Yes | — |
secret | string | Yes | — |
{
"id": "key_example",
"name": "production-worker",
"key_prefix": "hs_live_abc123",
"secret": "hs_live_<shown-once>",
"created_at": "2030-01-10T12:00:00Z",
"last_used_at": null,
"revoked_at": null
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationDELETE/api-keys/{key_id}Revoke Api Key
DELETE /v1/api-keys/{key_id}
Revoke an owned key. Subsequent use is rejected.
Authentication: Bearer.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
key_id | path | string | Required |
curl --fail-with-body -sS -X DELETE "$HYPERSR_API_BASE/api-keys/$KEY_ID" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
status | string | Yes | — |
{
"status": "revoked"
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 404 | KEY_NOT_FOUND |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/accountGet Account
GET /v1/account
Own account/profile. Identity and roles are server controlled.
Authentication: Bearer.
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/account" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
display_name | string | null | Yes | — |
email | string | Yes | — |
id | string | Yes | — |
role | string | Yes | — |
{
"id": "user_example",
"email": "developer@example.com",
"display_name": "Developer",
"role": "user"
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationPATCH/accountUpdate Account
PATCH /v1/account
Update {display_name}; unknown fields are rejected.
Authentication: Bearer.
Only display_name is customer-editable.
JSON request body
| Field | Type | Required | Constraints |
|---|---|---|---|
display_name | string | null | No | — |
curl --fail-with-body -sS -X PATCH "$HYPERSR_API_BASE/account" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"display_name":"Updated name"}'Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
display_name | string | null | Yes | — |
email | string | Yes | — |
id | string | Yes | — |
role | string | Yes | — |
{
"id": "user_example",
"email": "developer@example.com",
"display_name": "Updated name",
"role": "user"
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/account/summaryAccount Summary
GET /v1/account/summary
Balances, active/completed runs, area and safe worker availability.
Authentication: Bearer.
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/account/summary" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
active_jobs | integer | Yes | — |
available_credits | integer | Yes | — |
completed_jobs | integer | Yes | — |
processed_area_km2 | number | Yes | — |
reserved_credits | integer | Yes | — |
total_credits | integer | Yes | — |
worker_available | boolean | Yes | — |
{
"available_credits": 10000,
"reserved_credits": 0,
"total_credits": 10000,
"processed_area_km2": 3.72,
"active_jobs": 0,
"completed_jobs": 1,
"worker_available": true
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/account/notificationsGet Notification Preferences
GET /v1/account/notifications
Completion/failure notification preferences.
Authentication: Bearer.
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/account/notifications" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
job_completed | boolean | Yes | — |
job_failed | boolean | Yes | — |
{
"job_completed": true,
"job_failed": true
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationPATCH/account/notificationsUpdate Notification Preferences
PATCH /v1/account/notifications
Set job_completed and/or job_failed booleans.
Authentication: Bearer.
At least one known boolean may be sent; explicit null values are rejected.
JSON request body
| Field | Type | Required | Constraints |
|---|---|---|---|
job_completed | boolean | null | No | — |
job_failed | boolean | null | No | — |
curl --fail-with-body -sS -X PATCH "$HYPERSR_API_BASE/account/notifications" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"job_completed":true,"job_failed":false}'Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
job_completed | boolean | Yes | — |
job_failed | boolean | Yes | — |
{
"job_completed": true,
"job_failed": false
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/aoisList Aois
GET /v1/aois
Own saved areas in {items, total}; offset and limit.
Authentication: Bearer.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
offset | query | integer | Optional · Default: 0 · Minimum: 0 |
limit | query | integer | Optional · Default: 50 · Minimum: 1 · Maximum: 100 |
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/aois?offset=0&limit=50" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
items | array<object> | Yes | — |
items[].area_km2 | number | Yes | — |
items[].bbox | array<number> | Yes | — |
items[].created_at | string (date-time) | Yes | — |
items[].description | string | Yes | — |
items[].id | string | Yes | — |
items[].name | string | Yes | — |
items[].updated_at | string (date-time) | Yes | — |
total | integer | Yes | — |
{
"items": [
{
"id": "aoi_example",
"name": "Barcelona test area",
"description": "Optional internal label",
"bbox": [
2.1,
41.3,
2.12,
41.32
],
"area_km2": 3.72,
"created_at": "2030-01-10T12:00:00Z",
"updated_at": "2030-01-10T12:00:00Z"
}
],
"total": 1
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationPOST/aoisCreate Aoi
POST /v1/aois
Save {name, bbox, description?}; returns 201 and the area.
Authentication: Bearer.
JSON request body
| Field | Type | Required | Constraints |
|---|---|---|---|
bbox | array<number> | Yes | Min items: 4 · Max items: 4 |
description | string | No | Default: "" · Max length: 500 |
name | string | Yes | Min length: 1 · Max length: 120 |
curl --fail-with-body -sS -X POST "$HYPERSR_API_BASE/aois" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"name":"Barcelona test area","description":"Optional internal label","bbox":[2.1,41.3,2.12,41.32]}'Success response · HTTP 201
| Field | Type | Required | Constraints |
|---|---|---|---|
area_km2 | number | Yes | — |
bbox | array<number> | Yes | — |
created_at | string (date-time) | Yes | — |
description | string | Yes | — |
id | string | Yes | — |
name | string | Yes | — |
updated_at | string (date-time) | Yes | — |
{
"id": "aoi_example",
"name": "Barcelona test area",
"description": "Optional internal label",
"bbox": [
2.1,
41.3,
2.12,
41.32
],
"area_km2": 3.72,
"created_at": "2030-01-10T12:00:00Z",
"updated_at": "2030-01-10T12:00:00Z"
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUESTINVALID_AOI |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationGET/aois/{aoi_id}Get Aoi
GET /v1/aois/{aoi_id}
One owned area, geodetic area and timestamps.
Authentication: Bearer.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
aoi_id | path | string | Required |
curl --fail-with-body -sS -X GET "$HYPERSR_API_BASE/aois/$AOI_ID" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
area_km2 | number | Yes | — |
bbox | array<number> | Yes | — |
created_at | string (date-time) | Yes | — |
description | string | Yes | — |
id | string | Yes | — |
name | string | Yes | — |
updated_at | string (date-time) | Yes | — |
{
"id": "aoi_example",
"name": "Barcelona test area",
"description": "Optional internal label",
"bbox": [
2.1,
41.3,
2.12,
41.32
],
"area_km2": 3.72,
"created_at": "2030-01-10T12:00:00Z",
"updated_at": "2030-01-10T12:00:00Z"
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 404 | AOI_NOT_FOUND |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationPATCH/aois/{aoi_id}Update Aoi
PATCH /v1/aois/{aoi_id}
Change supplied name, description or bbox fields.
Authentication: Bearer.
Send only fields to change; explicit null values are rejected.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
aoi_id | path | string | Required |
JSON request body
| Field | Type | Required | Constraints |
|---|---|---|---|
bbox | array<number> | null | No | — |
description | string | null | No | — |
name | string | null | No | — |
curl --fail-with-body -sS -X PATCH "$HYPERSR_API_BASE/aois/$AOI_ID" \
-H "Authorization: Bearer $HYPERSR_API_KEY" \
-H "Content-Type: application/json" \
--data '{"description":"Updated label"}'Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
area_km2 | number | Yes | — |
bbox | array<number> | Yes | — |
created_at | string (date-time) | Yes | — |
description | string | Yes | — |
id | string | Yes | — |
name | string | Yes | — |
updated_at | string (date-time) | Yes | — |
{
"id": "aoi_example",
"name": "Barcelona test area",
"description": "Updated label",
"bbox": [
2.1,
41.3,
2.12,
41.32
],
"area_km2": 3.72,
"created_at": "2030-01-10T12:00:00Z",
"updated_at": "2030-01-10T12:00:00Z"
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 404 | AOI_NOT_FOUND |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationDELETE/aois/{aoi_id}Delete Aoi
DELETE /v1/aois/{aoi_id}
Delete the saved area; existing jobs keep their bounds.
Authentication: Bearer.
Parameters & headers
| Name | Location | Type | Required / constraints |
|---|---|---|---|
aoi_id | path | string | Required |
curl --fail-with-body -sS -X DELETE "$HYPERSR_API_BASE/aois/$AOI_ID" \
-H "Authorization: Bearer $HYPERSR_API_KEY"Success response · HTTP 200
| Field | Type | Required | Constraints |
|---|---|---|---|
status | string | Yes | — |
{
"status": "deleted"
}Error responses
| HTTP | Error codes |
|---|---|
| 401 | UNAUTHENTICATED |
| 403 | FORBIDDEN |
| 404 | AOI_NOT_FOUND |
| 413 | REQUEST_TOO_LARGE |
| 422 | INVALID_REQUEST |
| 429 | RATE_LIMIT_EXCEEDED |
| 500 | INTERNAL_ERROR |
Errors use the shared error envelope. Field validation also checks the geospatial and account rules explained in the guides above. Required nested fields apply when their parent object is present.
Link to this operationDownload OpenAPI JSON. Stripe webhooks and private worker/operator operations are managed by HyperSR and are outside the customer integration contract.