Skip to content
JavaScript SDK

Lifecycle

Start, exit, fail, and initialize an Actor, with exit codes and local runs.

Every Actor initializes the SDK, does its work, and shuts down so storage writes and charges are flushed before the process ends. Actor.main() handles all three steps; the manual methods give you control over each one.

Lifecycle

Actor.main()

Actor.main(userFunc, options?) calls Actor.init(options), awaits your callback, shuts down events, storage, and charging, flushes standard output, and calls process.exit().

.ts
import { Actor } from 'scrapely';

await Actor.main(async () => {
    const input = await Actor.getInputOrThrow<{ url: string }>();
    await Actor.pushData({ url: input.url, ok: true });
});
OutcomeWhat happensExit code
Callback resolvesShutdown (waits up to timeoutSecs, default 30)options.exitCode, default 0
Callback throwsLogs [Scrapely] Actor failed: <message> (plus the error's cause), then shuts downoptions.exitCode, default 1

options accepts every InitOptions field plus timeoutSecs and exitCode. main() always exits the process, even if you pass exit: false. The first argument must be a function, otherwise main() throws before initializing.

The platform marks the run SUCCEEDED on exit code 0 and FAILED on any other code, unless the run was aborted or timed out. Because exitCode applies to both outcomes, setting exitCode: 0 also makes a failed callback exit with 0.

Manual lifecycle

Use init() and exit() when your code must control when the process ends:

.ts
await Actor.init();
try {
    const input = await Actor.getInputOrThrow<Record<string, unknown>>();
    await Actor.pushData({ received: input });
    await Actor.exit('Completed');
} catch (error) {
    await Actor.fail(error instanceof Error ? error.message : String(error));
}

These methods throw Actor.<name>() requires Actor.init() to be called first. Hint: Use Actor.main() which calls init() for you. when called before init(): getInput, getInputOrThrow, pushData, getValue, setValue, openDataset, openKeyValueStore, openRequestQueue, useState, setStatusMessage, charge, reboot, exit, and fail.

Actor.exit() and Actor.fail()

.ts
Actor.exit(messageOrOptions?: string | ExitOptions, options?: ExitOptions): Promise<void>
Actor.fail(messageOrOptions?: string | ExitOptions, options?: ExitOptions): Promise<void>
ExitOptions fieldTypeDefaultMeaning
statusMessagestring—Printed to the log as [Scrapely] <message>
timeoutSecsnumber30How long shutdown may wait for flushes
exitCodenumber0 for exit(), 1 for fail()Process exit code
exitbooleantrueWhether to call process.exit() after shutdown
.ts
await Actor.exit('Finished');
await Actor.exit({ statusMessage: 'Finished', exit: false });
await Actor.fail('Validation failed');
await Actor.fail({ statusMessage: 'Upstream down', exitCode: 2 });

fail() logs [Scrapely] Actor failed: <message> and calls exit() with exitCode defaulting to 1.

Exit codes

EXIT_CODES is exported for code that sets exit codes explicitly. main() and fail() don't use it; they use 0 and 1.

ConstantValue
EXIT_CODES.SUCCESS0
EXIT_CODES.ERROR_USER_FUNCTION_THREW91
EXIT_CODES.ERROR_UNKNOWN92

Initialization

.ts
interface InitOptions {
    configuration?: ConfigurationOptions;
    storage?: ApiStorageConfig | StorageClient;
    gracefulShutdown?: boolean;
    gracefulShutdownDelayMillis?: number;
}
OptionDefaultBehavior
configuration—Overrides SDK and Crawlee configuration values (see Configuration)
storagefrom environmentAPI credentials { apiUrl, token, runId }, or any Crawlee StorageClient
gracefulShutdowntrueOn aborting, calls Actor.exit(); on migrating, calls Actor.reboot()
gracefulShutdownDelayMillis0Delay before that automatic exit() or reboot()

Calling init() again after it succeeded does nothing. If init() fails, it undoes the partial setup, so you can retry.

What init() does

  1. Installs handlers for uncaughtException and unhandledRejection. On either, the SDK logs the error, flushes pending charges for up to 5 seconds, and exits with code 1 (a hard exit after 10 seconds at most).
  2. Picks storage:
    • the storage option, if you passed one;
    • otherwise, API-backed storage from SCRAPELY_API_URL, SCRAPELY_TOKEN, and ACTOR_RUN_ID;
    • otherwise, Crawlee local storage (see Local runs).
  3. Connects the platform events WebSocket when the events URL, token, and run ID are all set.
  4. Starts the event manager, and registers the graceful shutdown handlers unless gracefulShutdown is false.
  5. Loads pricing and starts the charging manager.
  6. Handles SIGINT and SIGTERM: flushes storage and charges for up to 5 seconds, then exits with code 0.

On the platform (SCRAPELY_IS_AT_HOME=1), missing credentials are an error:

.text
Missing required configuration for secure storage. Required: apiBaseUrl (SCRAPELY_API_URL), token (SCRAPELY_TOKEN/APIFY_TOKEN), actorRunId (ACTOR_RUN_ID). Containers must be started by the scrapely-server, or pass storage in Actor.init().

Explicit storage credentials

On a platform run you don't need this. Pass storage only to point the SDK at a specific run's storage:

.ts
await Actor.init({
    storage: {
        apiUrl: process.env.SCRAPELY_API_URL!,
        token: process.env.SCRAPELY_TOKEN!,
        runId: process.env.ACTOR_RUN_ID!,
    },
});

Any other value that isn't API credentials or a StorageClient throws Actor.init() storage option must be ApiStorageConfig or StorageClient.

Local runs

Without platform credentials and outside the platform, init() logs [Scrapely] Local run detected — using Crawlee local storage and keeps working:

FeatureLocal behavior
StorageCrawlee storage in ./storage
InputACTOR_INPUT, or the INPUT record of the local default key-value store
EventsNo platform events; Crawlee still emits persistState on its interval
BrowsersLaunched on your machine, even if the Actor uses the Scrape.ly browser on the platform
ProxyNo platform proxy unless you set SCRAPELY_PROXY_*; your own proxyUrls work
ChargingEvery event is priced at 1; nothing is billed
addWebhook()Logs a warning and returns undefined

scrapely run sets this up for you. It purges the default local storages (keeping INPUT.json) unless you pass --no-purge, passes -i/-f input as ACTOR_INPUT, and starts npm start or main. See CLI: Actor runtime.

Platform-only calls such as metamorph(), reboot(), and setStatusMessage() need run credentials; see each method for its local behavior.