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
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | SATEAIS_API_KEY | API key. Falls back to the environment variable. |
baseUrl | string | https://api.spcsft.com/api/v1 | API base URL. Trailing slashes are removed. |
timeoutMs | number | 30_000 | Per-request timeout in milliseconds. Set to 0 to disable the timeout. |
fetch | typeof fetch | global fetch | Custom fetch implementation. |
apiClient | ApiClient | — | Inject a custom transport implementing the ApiClient interface (submitAnalysis, previewAnalysis, getJob, getJobResult). Intended for testing — when set, all HTTP options above are ignored. |
Properties
| Property | Type | Description |
|---|---|---|
analyze | AnalyzeResource | Detection job submission methods. |
preview | PreviewResource | The same methods as analyze, previewed instead of submitted. |
jobs | JobsResource | Job 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;
}| Option | Type | Default | Description |
|---|---|---|---|
intervalMs | number | 60_000 | Milliseconds between status polls. |
timeoutMs | number | Infinity | Maximum 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.
| Error | Maps to | Description |
|---|---|---|
SateaisError | — | Base class for every SDK error. |
SateaisApiError | 4xx / 5xx | Base for API error responses. Has code and status. |
AuthenticationError | 401 / 403 | Missing, invalid, or unauthorized API key. |
ValidationError | 400 | Invalid request — bad parameter or polygon over the area cap. |
InsufficientCreditsError | 402 | Not enough credits for the job. |
NotFoundError | 404 / 410 | Job not found, or result past retention. |
RateLimitError | 429 | Request 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);
}
}