---
title: "JavaScript SDK · Scrape.ly Docs"
description: "Build Actors in Node.js: get the SDK, imports, and a minimal Actor."
url: https://scrape.ly/docs/sdk-node
markdown: https://scrape.ly/docs/sdk-node.md
---

# JavaScript SDK

> Build Actors in Node.js: get the SDK, imports, and a minimal Actor.

The JavaScript SDK (`scrapely`) is the runtime library for JavaScript and TypeScript Actors on Scrape.ly. It combines the `Actor` lifecycle and platform APIs with Crawlee crawlers, storages, the platform proxy, the opt-in [Scrape.ly browser](https://scrape.ly/docs/browser.md), logging, and pay-per-event charging. It is API-compatible with the Apify SDK, and parts of it are derived from the Apify SDK (Apache-2.0), so most Apify Actors run unchanged.

The source code is on GitHub: [scrapely-app/scrapely](https://github.com/scrapely-app/scrapely).

## Get the SDK

### On the platform

You don't install the SDK for platform runs. Every Node.js Actor builds on a `scrapely/actor-node*` base image that already contains `scrapely` and its API client (`scrapely-client`) under `/opt/scrapely`.

| Image | Contents |
| --- | --- |
| `scrapely/actor-node` | Node.js only, for HTTP crawlers and plain scripts |
| `scrapely/actor-node-playwright` | Playwright |
| `scrapely/actor-node-playwright-chrome` | Playwright with Chrome |
| `scrapely/actor-node-playwright-firefox` | Playwright with Firefox |
| `scrapely/actor-node-playwright-webkit` | Playwright with WebKit |
| `scrapely/actor-node-playwright-camoufox` | Playwright with Camoufox |
| `scrapely/actor-node-puppeteer-chrome` | Puppeteer with Chrome |

Each image is tagged by Node.js major version: `20`, `22`, `24`, and `26`. Browser images ship browsers that your Actor launches in its container. If you turn on the [Scrape.ly browser](https://scrape.ly/docs/browser.md) for the Actor, the build skips browser downloads and launches connect to the hosted browser instead (see [Browsers and remote CDP](https://scrape.ly/docs/sdk-node-environment.md#browsers-and-remote-cdp)).

If your Actor has no Dockerfile, the build generates one that starts from `scrapely/actor-node:20`, runs `npm ci --omit=dev` (or `npm install --omit=dev` without a lockfile), and starts `node main.js` in the package root. Add a Dockerfile to choose another image or entrypoint.

With **Transform actors** enabled (dashboard **Settings → Preferences**, on by default), the build also:

- links the base image's SDK into your `node_modules` after `npm install`, so `scrapely` resolves to it even if your `package.json` asks for another version;
- adds an `apify` shim when your project doesn't install the real `apify` package;
- swaps `apify/actor-node*` base images for the matching `scrapely/actor-node*` image;
- points calls to Apify's LLM gateway (`https://openrouter.apify.actor`) at the [Scrape.ly LLM gateway](https://scrape.ly/docs/llm-gateway.md#actors-built-for-apify).

> [!NOTE]
> `scrapely init` adds `"scrapely": "*"` to `dependencies` so local runs can resolve the SDK. With **Transform actors** on, platform builds use the SDK version in the base image, whatever version your `package.json` or lockfile names.

### On your machine

`scrapely run` starts your entrypoint with your local Node.js, so the SDK has to be in your local `node_modules`. The SDK is public on npm, and no npm login is needed. In a project created with `scrapely init`, `npm install` is enough. Otherwise, install it yourself:

```bash
npm install scrapely
```

Versions 1.2.3 and older of `scrapely` are an empty placeholder. If an old lockfile pins one of them, run `npm install scrapely@latest`. To test against the platform itself, push your code and run it with `scrapely call`; see [Errors & deployment](https://scrape.ly/docs/sdk-node-deploy.md#testing-and-deployment).

Locally, the SDK uses Crawlee storage in `./storage`, launches browsers on your machine, and has no platform proxy or charging. See [Local runs](https://scrape.ly/docs/sdk-node-lifecycle.md#local-runs).

## Imports

Examples in these docs import from `scrapely`. On the platform, `apify` imports resolve to the same module.

```ts
import {
    Actor,
    PlaywrightCrawler,
    Dataset,
    KeyValueStore,
    RequestQueue,
} from 'scrapely';
```

TypeScript types ship with the package. The root package exports:

- `Actor`, `ACTOR_PERMISSION_LEVEL`, `EXIT_CODES`, and the Actor option types
- `Configuration` and `PlatformEventManager`
- `ApifyClient` from `scrapely-client`
- `ChargingManager`, `ChargeUncertainError`, `ChargeRejectedError`, and charging helpers
- `ProxyConfiguration` and `createProxyConfiguration` for the platform proxy or your own proxies
- `BrowserConfiguration`, `createBrowserConfiguration`, and `setCdpUrl` for the [Scrape.ly browser](https://scrape.ly/docs/browser.md)
- `ScrapelyRequestList`, `utils`, and `enqueueLinks`
- Crawlee crawlers, storages, logging, and the `playwrightUtils` / `puppeteerUtils` namespaces

See the [API reference](https://scrape.ly/docs/sdk-node-reference.md) for the full list.

Importing the package has side effects:

- It maps `ACTOR_LOG_LEVEL` and `ACTOR_LOG_FORMAT` to Crawlee's log settings.
- It patches Crawlee's browser pool, and the Playwright and Puppeteer launchers, so browsers connect to the [Scrape.ly browser](https://scrape.ly/docs/browser.md) when the Actor has it turned on. With the setting off (the default), launches are left alone.

Import the SDK before other Crawlee modules so the log settings apply.

## How an Actor runs

A platform run is a container with a run context. Scrape.ly injects the run ID, a scoped token, the API URL, default storage IDs, input, timeout, and the events WebSocket URL as environment variables (see [Environment](https://scrape.ly/docs/sdk-node-environment.md#variables-the-platform-injects)). The SDK reads them when you call `Actor.init()`.

1. `Actor.init()` connects API-backed storage, the platform events WebSocket, and charging.
2. The Actor reads input with `Actor.getInput()` or `Actor.getInputOrThrow()`.
3. Crawler code processes requests and writes to datasets, key-value stores, and request queues.
4. Platform events (`persistState`, `migrating`, `aborting`, `systemInfo`) arrive while the Actor works.
5. `Actor.main()` or `Actor.exit()` closes events, flushes storage and charges, and ends the process.

Always `await` writes. The process can end before an un-awaited dataset or key-value store write reaches the API.

## Minimal Actor

```ts
import { Actor } from 'scrapely';

await Actor.main(async () => {
    const { url } = await Actor.getInputOrThrow<{ url: string }>();
    const response = await fetch(url);
    await Actor.pushData({ url, status: response.status });
});
```

## Where to go next

| Topic | Page |
| --- | --- |
| `init`, `main`, `exit`, `fail`, local runs, signals | [Lifecycle](https://scrape.ly/docs/sdk-node-lifecycle.md) |
| `getInput`, `pushData`, `getValue`, `setValue` | [Input & output](https://scrape.ly/docs/sdk-node-input-output.md) |
| Datasets, key-value stores, queues, Crawlee | [Storages & Crawlee](https://scrape.ly/docs/sdk-node-storages.md) |
| Proxy, events, `useState`, status messages | [Proxy & events](https://scrape.ly/docs/sdk-node-proxy-events.md) |
| `call`, `start`, `abort`, `metamorph`, webhooks, charging | [Actors & webhooks](https://scrape.ly/docs/sdk-node-actors.md) |
| `getEnv`, run metadata, Scrape.ly browser, API client | [Environment](https://scrape.ly/docs/sdk-node-environment.md) |
| Standby mode and HTTP-serving Actors | [Standby](https://scrape.ly/docs/sdk-node-standby.md) |
| Errors, testing, deployment | [Errors & deployment](https://scrape.ly/docs/sdk-node-deploy.md) |
| Every exported symbol and signature | [API reference](https://scrape.ly/docs/sdk-node-reference.md) |

---

- Previous: [Security](https://scrape.ly/docs/security.md)
- Next: [Lifecycle](https://scrape.ly/docs/sdk-node-lifecycle.md)
- All docs: [Docs index](https://scrape.ly/docs.md), [llms.txt](https://scrape.ly/llms.txt)
