Job Management

Get job status

Retrieve the status and metadata of a detection job.

GET /api/v1/jobs/{job_id}

Retrieve the current status of a detection job. Poll this endpoint until status becomes completed or failed.

Request

curl https://api.spcsft.com/api/v1/jobs/351e635d-7c25-4ae8-a2a5-60c01a6f434c \
  -H "Authorization: Bearer sk_live_xxxxx"

Path parameters

ParameterTypeDescription
job_idstring (UUID)Returned from the detection endpoint.

Response

Completed job

{
  "job_id": "351e635d-7c25-4ae8-a2a5-60c01a6f434c",
  "endpoint_id": "newbuilding",
  "satellite_id": "sentinel-1",
  "status": "completed",
  "created_at": "2026-03-12T15:00:00Z",
  "completed_at": "2026-03-12T15:32:10Z",
  "result_path": "/api/v1/jobs/351e635d-7c25-4ae8-a2a5-60c01a6f434c/result.geojson",
  "error_code": null,
  "error_message": null,
  "error": null,
  "area_sqkm": 80.7,
  "coverage": {
    "method": "measured",
    "requested_area_sqkm": 103.4,
    "ratio": 0.78,
    "polygon": "POLYGON((139.7 35.6, ...))"
  },
  "scene_selection": {
    "first_month": "2024-01",
    "last_month": "2026-02"
  },
  "warnings": [
    {
      "code": "LOW_AOI_COVERAGE",
      "message": "Scenes cover only 78% of the requested area."
    }
  ]
}

Failed job

{
  "job_id": "351e635d-7c25-4ae8-a2a5-60c01a6f434c",
  "endpoint_id": "timeseries",
  "satellite_id": "sentinel-1",
  "status": "failed",
  "created_at": "2026-03-12T15:00:00Z",
  "completed_at": "2026-03-12T15:31:15Z",
  "result_path": null,
  "error_code": "VALIDATION_ERROR",
  "error_message": "Polygon area (52.6 km²) exceeds 50 km² limit for endpoint 'timeseries'",
  "error": "VALIDATION_ERROR",
  "area_sqkm": null,
  "coverage": null,
  "scene_selection": null,
  "warnings": []
}

Response fields

FieldTypeDescription
job_idstringJob UUID.
endpoint_idstring | nullDetection type (ship, oilslick, newbuilding, disappearbuilding, timeseries, idlefarm). null for jobs submitted before this field existed.
satellite_idstring | nullSatellite used for the analysis.
statusstringpending / processing / completed / failed
created_atstringISO 8601 timestamp.
completed_atstring | nullISO 8601 timestamp. null while pending/processing.
result_pathstring | nullGeoJSON download path. null unless completed.
error_codestring | nullMachine-readable error code. See Errors.
error_messagestring | nullHuman-readable error detail. Fixed message for 5xx errors.
errorstring | nullDeprecated. Same as error_code. Use error_code instead.
area_sqkmnumber | nullArea in km² that credits were charged on. For newbuilding / disappearbuilding / timeseries it is the actually analysed area, with no-data excluded.
coverageobject | nullHow much of your requested polygon was actually analysed. See Coverage.
scene_selectionobject | nullWhen the imagery used was captured. null when you supplied scene_id, or for jobs submitted before this field existed.
warningsarrayNon-fatal notices about the selected imagery. Empty when the request was satisfied as asked.

Coverage

The polygon you request and the area actually analysed are rarely identical: the selected scene may not cover the whole AOI, and portions without usable data are excluded. coverage quantifies that difference, so you can tell "nothing detected" apart from "not analysed".

FieldTypeDescription
coverage.method"measured" | "estimated"How the value was obtained — see below.
coverage.requested_area_sqkmnumberArea of the polygon you submitted (km²).
coverage.rationumberFraction of the requested area that was analysed, 0.01.0, rounded down to two decimals.
coverage.polygonstring | nullWKT of the analysed extent, in the same format as the request polygon. In job responses it is present only for measured coverage; a preview carries the predicted extent instead.
methodDerived fromEndpoints
measuredThe actual data boundary after excluding no-data pixelsnewbuilding, disappearbuilding, timeseries
estimatedThe intersection of your polygon with the selected scene footprintship, oilslick (polygon input)

coverage is absent for inputs where the analysed fraction cannot be determined, such as scene_id input. Treat a missing coverage as unknown — do not display it as 100%.

coverage.polygon is simplified to fit within the request size limit, so you can resubmit it — or the missing remainder — directly as a new request's polygon. When no-data splits the analysed area it is a MULTIPOLYGON; split it into individual polygons before resubmitting.

Scene selection

When you submit a polygon, the API picks the Sentinel-1 imagery for you. scene_selection tells you when the imagery used was captured, so you can confirm it matches the period you asked for.

FieldTypeDescription
scene_selection.first_monthstring | nullObservation month of the earliest scene used (YYYY-MM).
scene_selection.last_monthstring | nullObservation month of the latest scene used (YYYY-MM).

Compare first_month / last_month against the date_start / date_end you requested. A gap means suitable imagery was not available across the whole period — warnings explains why.

Observation dates are reported to month precision only. Scene identifiers and exact acquisition timestamps are not returned.

Warnings

A job with warnings still succeeds: status is completed and error_code is null. Warnings tell you whether to trust the result as-is.

{
  "status": "completed",
  "error_code": null,
  "scene_selection": {
    "first_month": "2021-01",
    "last_month": "2021-12"
  },
  "warnings": [
    {
      "code": "PERIOD_PARTIALLY_COVERED",
      "message": "Observations cover only 30% of the requested period (2021-01-01 to 2023-12-31)."
    }
  ]
}

Each entry has a machine-readable code and a human-readable message. See Warnings for the full list.

Status values

StatusDescription
pendingQueued, not yet started.
processingCurrently running.
completedDone. Result available for 30 days.
failedFailed. See error for details. No credits charged.

Polling guidance

Poll about once per minute. See How it works for per-endpoint timing recommendations.

Next steps

On this page