JavaScript

JavaScript reference

Full reference for the SateAIs JavaScript/TypeScript SDK — client, analysis methods, jobs, types, and errors.

Complete reference for the @sateais/sdk package. For setup and authentication, see the JavaScript SDK guide.

Client

import { Client } from "@sateais/sdk";

const client = new Client(options?: ClientOptions);

ClientOptions

OptionTypeDefaultDescription
apiKeystringSATEAIS_API_KEYAPI key. Falls back to the environment variable.
baseUrlstringhttps://api.spcsft.com/api/v1API base URL. Trailing slashes are removed.
timeoutMsnumber30_000Per-request timeout in milliseconds. Set to 0 to disable the timeout.
fetchtypeof fetchglobal fetchCustom fetch implementation.
apiClientApiClient—Inject a custom transport implementing the ApiClient interface (submitAnalysis, previewAnalysis, getJob, getJobResult). Intended for testing — when set, all HTTP options above are ignored.

Properties

PropertyTypeDescription
analyzeAnalyzeResourceDetection job submission methods.
previewPreviewResourceThe same methods as analyze, previewed instead of submitted.
jobsJobsResourceJob status and result methods.

Analysis methods

All client.analyze.* methods submit a detection job and resolve to a JobCreateResponse with status "pending". Use jobs.wait() to await the result.

analyze.ship / analyze.oilslick

client.analyze.ship(params: SceneAnalyzeParams): Promise<JobCreateResponse>

analyze.oilslick(...) has the same signature. SceneAnalyzeParams is a union — pass either a scene_id or a polygon + date:

type SceneAnalyzeParams = SceneIdInput | PolygonDateInput;

interface SceneIdInput {
  satellite_id?: "sentinel-1";
  scene_id: string;
}

interface PolygonDateInput {
  satellite_id?: "sentinel-1";
  polygon: string;                 // WKT, EPSG:4326
  date: string;                    // "YYYY-MM-DD"
  date_direction?: "before" | "after" | "nearest"; // default "nearest"
  orbit_direction?: "ascending" | "descending";
}
// By scene ID
const job = await client.analyze.ship({ scene_id: "S1A_IW_GRDH_..." });

// By polygon + date (auto scene selection)
const job = await client.analyze.oilslick({
  polygon: "POLYGON((55.5 26.7, 56.1 26.7, 56.1 27.0, 55.5 27.0, 55.5 26.7))",
  date: "2026-03-10",
  date_direction: "nearest",
});

analyze.newbuilding / analyze.disappearbuilding / analyze.timeseries

client.analyze.newbuilding(params: PolygonPeriodParams): Promise<JobCreateResponse>

analyze.disappearbuilding(...) and analyze.timeseries(...) share this signature.

interface PolygonPeriodParams {
  satellite_id?: "sentinel-1";
  polygon: string;       // WKT, EPSG:4326
  date_start: string;    // "YYYY-MM-DD"
  date_end: string;      // "YYYY-MM-DD"
  orbit_direction?: "ascending" | "descending";
}
const job = await client.analyze.timeseries({
  polygon: "POLYGON((139.7 35.6, 139.8 35.6, 139.8 35.7, 139.7 35.7, 139.7 35.6))",
  date_start: "2026-01-01",
  date_end: "2026-05-01",
});

Per-endpoint area caps apply: newbuilding / disappearbuilding ≤ 30,000 km², timeseries ≤ 50 km². Exceeding a cap throws ValidationError before any credits are consumed. See Polygons & AOI.

Preview methods

client.preview.* has the same five methods as client.analyze.*, taking the same parameter types. Each resolves to a PreviewResponse instead of a JobCreateResponse: no job is created and no credits are consumed.

client.preview.ship(params: SceneAnalyzeParams): Promise<PreviewResponse>
client.preview.oilslick(params: SceneAnalyzeParams): Promise<PreviewResponse>
client.preview.newbuilding(params: PolygonPeriodParams): Promise<PreviewResponse>
client.preview.disappearbuilding(params: PolygonPeriodParams): Promise<PreviewResponse>
client.preview.timeseries(params: PolygonPeriodParams): Promise<PreviewResponse>
const preview = await client.preview.timeseries({
  polygon: "POLYGON((139.7 35.6, 139.8 35.6, 139.8 35.7, 139.7 35.7, 139.7 35.6))",
  date_start: "2026-01-01",
  date_end: "2026-05-01",
});
console.log(preview.credits.estimated, preview.credits.sufficient);

Parameter validation is identical to submission — an invalid parameter combination throws ValidationError before the request goes out. The one deliberate difference is the balance: a short balance comes back as credits.sufficient false, never as InsufficientCreditsError, since seeing the shortfall is the point of previewing. See Preview a request for the underlying contract.

Job methods

jobs.status

client.jobs.status(jobId: string): Promise<JobStatusResponse>

Fetches the current state of a job (a single poll).

const job = await client.jobs.status("351e635d-7c25-4ae8-a2a5-60c01a6f434c");
console.log(job.status);

jobs.result

client.jobs.result(jobId: string): Promise<GeoJSONResponse>

Returns the GeoJSON FeatureCollection for a completed job. Throws NotFoundError if the job is not complete or the result is past its 30-day retention window.

const geojson = await client.jobs.result("351e635d-7c25-4ae8-a2a5-60c01a6f434c");
console.log(geojson.type, geojson.features.length);

jobs.wait

client.jobs.wait(jobId: string, options?: WaitOptions): Promise<GeoJSONResponse>

Polls until the job reaches a terminal state, then resolves to the result GeoJSON.

interface WaitOptions {
  intervalMs?: number;   // default 60_000
  timeoutMs?: number;    // default Infinity (no limit)
  onPoll?: (job: JobStatusResponse) => void;
}
OptionTypeDefaultDescription
intervalMsnumber60_000Milliseconds between status polls.
timeoutMsnumberInfinityMaximum time to wait before throwing JobTimeoutError.
onPoll(job: JobStatusResponse) => void—Called with the latest status after each poll.

Rejects with JobTimeoutError if timeoutMs elapses first, or JobFailedError if the job reaches any status other than pending, processing, or completed — this includes failed as well as unrecognized statuses, so polling never loops forever.

const result = await client.jobs.wait(job.job_id, {
  intervalMs: 30_000,
  timeoutMs: 3_600_000,
  onPoll: (j) => console.log(j.status),
});

Tip

intervalMs defaults to 60 s. Runtime varies by endpoint — see How it works.

Types

JobCreateResponse

Returned by all analyze.* methods.

interface JobCreateResponse {
  job_id: string;
  status: JobStatus;            // "pending" | "processing" | "completed" | "failed"
  created_at: string;          // ISO 8601
  completed_at: string | null;
  result_path: string | null;
  error: string | null;        // null while pending
}

JobStatusResponse

Returned by jobs.status().

interface JobStatusResponse {
  job_id: string;
  status: JobStatus;
  created_at: string;
  completed_at: string | null;
  result_path: string | null;
  error_code: string | null;     // set when failed
  error_message: string | null;  // set when failed
  error: string | null;          // @deprecated — mirrors error_code
}

GeoJSONResponse

Returned by jobs.result() and jobs.wait() — a standard GeoJSON FeatureCollection in EPSG:4326.

interface GeoJSONResponse {
  type: "FeatureCollection";
  features: GeoJSONFeature[];
  crs?: { type: string; properties: Record<string, unknown> };
}

PreviewResponse

Returned by all preview.* methods.

interface PreviewResponse {
  endpoint_id: string;              // detection type the preview was run for
  area_sqkm: number | null;         // area expected to be analysed (km²)
  coverage: Coverage | null;        // null = cannot be predicted, NOT "fully covered"
  credits: PreviewCredits;
  warnings: SceneWarning[];         // LOW_AOI_COVERAGE, CREDITS_NOT_ESTIMABLE
}

interface PreviewCredits {
  estimated: number | null;         // null = not determined before submission, NOT free
  balance: number;                  // current credit balance
  sufficient: boolean | null;       // null when estimated is null
}

interface Coverage {
  method: CoverageMethod;           // always "estimated" in a preview
  requested_area_sqkm: number | null;
  ratio: number | null;             // fraction of the AOI analysed (0.0–1.0)
  polygon: string | null;           // WKT of the area analysed, reusable as a request polygon
}

type CoverageMethod = "measured" | "estimated";

interface SceneWarning {
  code: string;
  message: string;
}

credits.estimated is an upper bound: it is based on the area you requested, while the actual charge excludes portions without usable data. Actual consumption never exceeds it. method is "measured" only on a finished job, where the analysed area is known exactly — see Warnings for the warning codes.

Errors

All errors inherit from SateaisError.

ErrorMaps toDescription
SateaisError—Base class for every SDK error.
SateaisApiError4xx / 5xxBase for API error responses. Has code and status.
AuthenticationError401 / 403Missing, invalid, or unauthorized API key.
ValidationError400Invalid request — bad parameter or polygon over the area cap.
InsufficientCreditsError402Not enough credits for the job.
NotFoundError404 / 410Job not found, or result past retention.
RateLimitError429Request or concurrency limit exceeded.
ResponseParseError—A successful (2xx) response body could not be parsed as JSON. Has status. Extends SateaisError, not SateaisApiError.
JobFailedError—A job did not complete successfully. Has jobId, errorCode, errorMessage.
JobTimeoutError—jobs.wait() exceeded timeoutMs. Has jobId.
import { SateaisApiError } from "@sateais/sdk";

try {
  await client.analyze.ship({ scene_id: "bad-id" });
} catch (err) {
  if (err instanceof SateaisApiError) {
    console.error(err.status, err.code, err.message);
  }
}

Next steps

On this page