Detect & Classify

Learn how to add Detect & Classify (D&C) to your tasking workflow. D&C applies automated detection models to your SAR imagery and delivers structured results identifying objects of interest within the scene.

What is Detect & Classify?

When you task a satellite image through the ICEYE API, you receive SAR data products (GRD, SLC, etc.). D&C goes a step further: it runs detection models on the acquired imagery and returns GeoJSON files containing the locations and classifications of detected objects.

The legacy types DC_AIRCRAFT, DC_VESSELS, and DC_VEHICLES are no longer supported — use DC-ATR instead. See the deprecation notice for migration details.

Available detection models

The following derivative product type is available:

Derivative product type Description

DC-ATR

Automated Target Recognition (ATR): detection and classification of objects of interest (aircraft, vessels, vehicles). Requires D&C to be enabled on your contract. Contact your ICEYE representative for any changes needed.

How it works

D&C is not a separate API. It extends the existing tasking workflow with two additions:

  1. You include a derivativeProductTypes parameter when creating a task

  2. After the task is DONE, you retrieve the detection results from a dedicated derivatives endpoint

The rest of the workflow (authentication, task creation, monitoring) stays the same.

Prerequisites

Before using D&C, make sure you have:

  • A contract with D&C enabled — derivative product types must be enabled in your contract. Contact your ICEYE representative.

  • Valid authentication credentials — see Authentication.

  • A supported imaging mode — D&C is available only for specific imaging modes. Your contract defines which imaging modes are enabled for D&C.

Part 1: Creating a task with D&C

Creating a D&C task is identical to creating a regular task, with one addition: the derivativeProductTypes field in the request body.

The derivativeProductTypes parameter

This parameter accepts an array of derivative product type strings. Add it alongside your usual task parameters:

"derivativeProductTypes": ["DC-ATR"]
D&C uses the GRD or GRD-COG product as input, so you must include it explicitly in the productTypes parameter. Having it as a default product on your contract is not sufficient.

For the full list of validation rules and error codes, see Create task — Derivative product types.

Full request example

  • cURL

  • Python

  • JavaScript

curl --location "${API_BASE_URL}/api/tasking/v2/tasks" \
  --header "Content-Type: application/json" \
  --header "Accept: application/json, application/problem+json" \
  --header "Authorization: Bearer ${API_ACCESS_TOKEN}" \
  --data '{
    "acquisitionWindow": {
      "start": "2026-03-01T00:00:00Z",
      "end": "2026-03-02T00:00:00Z"
    },
    "contractID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "imagingMode": "SPOTLIGHT_EXTENDED_DWELL",
    "pointOfInterest": {
      "lat": 60.1699,
      "lon": 24.9384
    },
    "productTypes": ["GRD"],
    "derivativeProductTypes": ["DC-ATR"]
  }'
import requests

task_payload = {
    "acquisitionWindow": {
        "start": "2026-03-01T00:00:00Z",
        "end": "2026-03-02T00:00:00Z"
    },
    "contractID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
    "imagingMode": "SPOTLIGHT_EXTENDED_DWELL",
    "pointOfInterest": {
        "lat": 60.1699,
        "lon": 24.9384
    },
    "productTypes": ["GRD"],
    "derivativeProductTypes": ["DC-ATR"]
}

response = requests.post(
    f"{API_BASE_URL}/api/tasking/v2/tasks",
    headers={
        "Content-Type": "application/json",
        "Accept": "application/json, application/problem+json",
        "Authorization": f"Bearer {access_token}"
    },
    json=task_payload
)

task = response.json()
print(f"Task created: {task['id']}, status: {task['status']}")
const taskPayload = {
  acquisitionWindow: {
    start: "2026-03-01T00:00:00Z",
    end: "2026-03-02T00:00:00Z"
  },
  contractID: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  imagingMode: "SPOTLIGHT_EXTENDED_DWELL",
  pointOfInterest: {
    lat: 60.1699,
    lon: 24.9384
  },
  productTypes: ["GRD"],
  derivativeProductTypes: ["DC-ATR"]
};

const response = await fetch(`${API_BASE_URL}/api/tasking/v2/tasks`, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Accept": "application/json, application/problem+json",
    "Authorization": `Bearer ${accessToken}`
  },
  body: JSON.stringify(taskPayload)
});

const task = await response.json();
console.log(`Task created: ${task.id}, status: ${task.status}`);

Response

On success, you receive a 201 status code. The response confirms which derivative product types were requested:

{
  "id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "pointOfInterest": {
    "lat": 60.1699,
    "lon": 24.9384
  },
  "acquisitionWindow": {
    "start": "2026-03-01T00:00:00Z",
    "end": "2026-03-02T00:00:00Z"
  },
  "createdAt": "2026-02-18T10:00:00Z",
  "updatedAt": "2026-02-18T10:00:00Z",
  "imagingMode": "SPOTLIGHT_EXTENDED_DWELL",
  "contractID": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "status": "RECEIVED",
  "exclusivity": "PRIVATE",
  "priority": "COMMERCIAL",
  "sla": "SLA_8H",
  "eula": "STANDARD",
  "incidenceAngle": {
    "min": 20,
    "max": 35
  },
  "lookSide": "ANY",
  "passDirection": "ANY",
  "productTypes": ["GRD"],
  "derivativeProductTypes": ["DC-ATR"]
}

The derivativeProductTypes array in the response mirrors what you requested. If a derivative type is not supported by your contract, the API returns an error.

Part 2: Monitoring the task

D&C runs automatically in the background as part of task processing. Monitoring it is no different from any other task: you poll the task status, and you never poll the derivatives endpoint to check whether results are ready.

When the derivatives are ready depends on whether D&C is an SLA product under your contract. If it is, they are ready once the task reaches FULFILLED. If it is not, they are ready once the task reaches DONE. Because DONE never comes before FULFILLED, waiting for DONE guarantees your derivatives are ready either way — so unless you want to act on the earlier FULFILLED signal, simply wait for DONE.

Polling for status

  • cURL

  • Python

  • JavaScript

curl --location "${API_BASE_URL}/api/tasking/v2/tasks/${taskID}" \
  --header "Accept: application/json, application/problem+json" \
  --header "Authorization: Bearer ${API_ACCESS_TOKEN}"
import time

while True:
    response = requests.get(
        f"{API_BASE_URL}/api/tasking/v2/tasks/{task_id}",
        headers={"Authorization": f"Bearer {access_token}"}
    )
    task = response.json()
    print(f"Status: {task['status']}")

    # Waiting for DONE is always safe. SLA-backed D&C is ready earlier, at
    # FULFILLED -- add "FULFILLED" here only to act on that earlier signal.
    if task["status"] in ["DONE", "FAILED", "REJECTED", "CANCELED"]:
        break

    time.sleep(30)
async function waitForCompletion(taskId) {
  // Waiting for DONE is always safe. SLA-backed D&C is ready earlier, at
  // FULFILLED -- add "FULFILLED" here only to act on that earlier signal.
  const terminalStates = ["DONE", "FAILED", "REJECTED", "CANCELED"];

  while (true) {
    const response = await fetch(
      `${API_BASE_URL}/api/tasking/v2/tasks/${taskId}`,
      { headers: { "Authorization": `Bearer ${accessToken}` } }
    );
    const task = await response.json();
    console.log(`Status: ${task.status}`);

    if (terminalStates.includes(task.status)) return task;

    await new Promise(resolve => setTimeout(resolve, 30000));
  }
}

Once the task reaches DONE, all requested products have finished processing. At that point, move on to Part 3 to retrieve your results.

Part 3: Retrieving D&C results

Once your derivatives are ready (see Part 2 for the readiness rule), retrieve them from the derivatives endpoint with a single request — there is no need to poll.

Request

  • cURL

  • Python

  • JavaScript

curl --location "${API_BASE_URL}/api/tasking/v2/tasks/${taskID}/derivatives" \
  --header "Accept: application/json, application/problem+json" \
  --header "Authorization: Bearer ${API_ACCESS_TOKEN}"
response = requests.get(
    f"{API_BASE_URL}/api/tasking/v2/tasks/{task_id}/derivatives",
    headers={"Authorization": f"Bearer {access_token}"}
)

if response.status_code == 404:
    print("No derivatives available yet (task not DONE?)")
elif response.status_code == 200:
    print("All derivatives ready")
    derivatives = response.json()
    for derivative in derivatives["data"]:
        print(f"Type: {derivative['derivativeProductType']}")
        for asset in derivative.get("assets", []):
            print(f"  Download: {asset['href']}")
const response = await fetch(
  `${API_BASE_URL}/api/tasking/v2/tasks/${taskId}/derivatives`,
  { headers: { "Authorization": `Bearer ${accessToken}` } }
);

if (response.status === 404) {
  console.log("No derivatives available yet (task not DONE?)");
} else if (response.status === 200) {
  console.log("All derivatives ready");
  const derivatives = await response.json();
  for (const derivative of derivatives.data) {
    console.log(`Type: ${derivative.derivativeProductType}`);
    for (const asset of derivative.assets || []) {
      console.log(`  Download: ${asset.href}`);
    }
  }
}

Understanding the response status codes

A request made once your derivatives are ready returns 200 with your results:

200 (All derivatives ready)

All requested derivative products have been processed and are available for download.

404 (Not found)

No derivative products are available yet. You’ll see this if you query before your derivatives are ready — wait for the task to reach DONE (or FULFILLED for SLA-backed D&C) and request again.

For the full list of error responses, see Task derivatives.

Response body

The response contains a data array with one entry per derivative product. Each derivative entry contains:

  • id — unique identifier for the derivative processing request

  • imageReference — UUID reference to the source image

  • imageID — alternative string identifier for the source image (optional)

  • derivativeProductType — type of derivative product (DC-ATR)

  • assets — array of downloadable files with signed URLs

{
  "data": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "imageReference": "c2ac555e-27ac-4c6a-bf87-717fa81539e3",
      "derivativeProductType": "DC-ATR",
      "assets": [
        {
          "type": "geojson",
          "href": "https://XXXXXXXXXXXX.cloudfront.net/XXXX/detections.geojson?Expires=..."
        }
      ]
    }
  ]
}
Download URLs are signed and expire after 1 hour. Do not store them for long-term access, instead request a fresh URL from the API when you need to download again.

Part 4: Downloading D&C results

The assets array contains signed URLs pointing to GeoJSON files. Download them like any other file:

  • cURL

  • Python

  • JavaScript

curl --location "${DERIVATIVE_ASSET_URL}" \
  --output aircraft-detections.geojson
for derivative in derivatives["data"]:
    for asset in derivative.get("assets", []):
        download_response = requests.get(asset["href"])
        filename = f"{derivative['derivativeProductType'].lower()}.geojson"
        with open(filename, "wb") as f:
            f.write(download_response.content)
        print(f"Downloaded {filename}")
for (const derivative of derivatives.data) {
  for (const asset of derivative.assets || []) {
    const downloadResponse = await fetch(asset.href);
    const content = await downloadResponse.text();
    const filename = `${derivative.derivativeProductType.toLowerCase()}.geojson`;
    // Save or process the GeoJSON content
    console.log(`Downloaded ${filename}: ${content.length} bytes`);
  }
}

The internal structure of the D&C GeoJSON files — the specific fields and what they mean — is shared on an individual basis. Reach out to your ICEYE representative for the detailed schema.

Complete workflow summary

Here is the end-to-end D&C workflow:

1. Create task (with derivativeProductTypes and GRD or GRD-COG in productTypes)
       |
       v
2. Poll task status (GET /tasks/{id}) until DONE
       |
       v
3. Download SAR products (GET /tasks/{id}/products)
       |
       v
4. Retrieve derivatives (GET /tasks/{id}/derivatives)
       |
       '-- 200: results ready (task is DONE), returns GeoJSON assets
              |
              v
       Download GeoJSON files from asset URLs
API call Frequency Trigger Stop condition

POST Create Task

Once

User requests D&C

After task created

GET Task Status

Periodic

After task created

Status is DONE

GET Task Products

Once

Status is DONE

After download

GET Task Derivatives

Once

Status is DONE

After download

You never poll the derivatives endpoint — you wait on the task status, then retrieve derivatives with a single request. See Part 2 for the readiness rule.