---
title: "Move Your Apify Actor to Scrape.ly: Step by Step | Scrape.ly"
description: "Move an Apify Actor to Scrape.ly: CLI push, SDK and apify-client changes, base URL and token, proxies, schedules, webhooks and Store publishing."
url: https://scrape.ly/blog/migrate-from-apify
markdown: https://scrape.ly/blog/migrate-from-apify.md
---

[Comparisons](https://scrape.ly/blog/category/comparisons.md)

# How to Move Your Apify Actor to Scrape.ly (Step by Step)

Move an Apify Actor and your apify-client code to Scrape.ly in an afternoon: the exact CLI commands, package and import changes, base URL and token, proxies, schedules, webhooks and integrations, and how to publish in the Store and keep 80% of event fees.

[Scrape.ly Team The team behind Scrape.ly](https://scrape.ly/blog/author/scrapely-team.md)

- Published

  Oct 23, 2026

- Reading time

  9 min read

![Pixel-art robot with an orange >\_ on its terminal head carries a cardboard box away from a switched-off arcade cabinet bearing the Apify logo towards a lit cabinet bearing the Scrape.ly logo](https://scrape.ly/media/migrate-from-apify.png?w=1080)

To move an Apify Actor to Scrape.ly, install the Scrape.ly CLI, log in with a Scrape.ly token and run `scrapely push` in the Actor's folder. The build accepts your `apify/actor-*` base image and your `apify` imports, so most Apify Actors run unchanged, and your `apify-client` scripts keep working after you change the import, the base URL and the token.

This guide walks through every step with the exact commands and code from the Scrape.ly docs: pushing the Actor, the few code paths to check, switching your API client, recreating schedules, webhooks and integrations, and publishing in the Scrape.ly Store, where creators keep 80% of event fees and the run's platform usage is billed to the user. For pricing, proxies and Store terms side by side, see the full [Scrape.ly vs Apify comparison](https://scrape.ly/compare/apify.md).

## What changes and what stays the same

Scrape.ly speaks the same Actor model as Apify. The JavaScript SDK is API-compatible with the Apify SDK, and parts of it are derived from it under Apache-2.0. The Python SDK mirrors the Apify Python SDK on Crawlee for Python. Here is the whole move at a glance:

|                    | On Apify                                                    | On Scrape.ly                                                                      |
| ------------------ | ----------------------------------------------------------- | --------------------------------------------------------------------------------- |
| Actor code         | `import { Actor } from 'apify'` / `from apify import Actor` | Unchanged. On the platform, `apify` imports resolve to the Scrape.ly SDK          |
| Actor config       | `.actor/actor.json`                                         | Unchanged. The CLI reads `.actor/actor.json` first                                |
| Dockerfile         | `FROM apify/actor-node…` or `apify/actor-python…`           | Unchanged to run. Swapped for the matching `scrapely/actor-*` image at build time |
| CLI                | `apify login`, `apify push`, `apify call`                   | `scrapely login`, `scrapely push`, `scrapely call` (package `scrapely-cli`)       |
| Node.js API client | `apify-client`                                              | `scrapely-client`; `ApifyClient` still works as an alias                          |
| Python API client  | `apify-client`, imported as `apify_client`                  | `scrapely-client-python`, imported as `scrapely_client`                           |
| API base URL       | Apify's API                                                 | `https://api.scrape.ly`                                                           |
| Token              | Apify token                                                 | A Scrape.ly token. Apify tokens don't work                                        |
| Proxy input        | `useApifyProxy`, `apifyProxyGroups`                         | Both names work; `RESIDENTIAL` and `DATACENTER` groups                            |
| Environment        | `APIFY_TOKEN`, `APIFY_PROXY_*` and friends                  | Still set in every run, next to the `SCRAPELY_*` names                            |

The rest of this guide is the detail behind each row.

## Before you start

- **Create a Scrape.ly account.** The free plan includes $5 of usage every month, no credit card.
- **Have Node.js 20.17 or newer** for the CLI.
- **List what each Actor depends on:** proxy groups, environment variables and secrets, schedules, webhooks and integrations. You'll recreate the last three on Scrape.ly.
- **Keep your Apify setup running** until the first Scrape.ly runs match. Nothing below touches your Apify account.

## Step 1: Install the CLI and log in

```shell
npm install -g scrapely-cli
export SCRAPELY_API_URL=https://api.scrape.ly
export SCRAPELY_WEB_URL=https://my.scrape.ly
scrapely login
```

Set `SCRAPELY_API_URL` before your first login: without it, the CLI's built-in default is not the production API. `scrapely login` offers three methods. **Browser** opens the dashboard and asks you to approve the CLI, **console** lets you paste an API key from **Settings → API & Integrations**, and **manual** asks for a key directly. Check the result with `scrapely info`.

For CI, skip the login and set an API key in the environment instead (more on that in Step 5). The [CLI reference](https://scrape.ly/docs/cli.md) has every command.

## Step 2: Push your Apify Actor unchanged

In the folder of the Actor you want to move:

```shell
scrapely push
```

That's the whole migration for most Actors. `push` reads your existing `.actor/actor.json`, uploads the project (skipping `node_modules`, `.git`, `storage` and anything in `.gitignore` or `.actorignore`), creates the Actor on Scrape.ly on the first push, writes the new `actorId` into the config, and builds it. If the build fails, it prints the build log and exits with code 1.

What the build does for an Apify project:

- **Node.js** (with **Transform actors** on, the default under **Settings → Preferences**): swaps `apify/actor-node*` base images for the matching `scrapely/actor-node*` image, links the image's SDK into `node_modules`, and adds an `apify` shim when your project doesn't install the real `apify` package.
- **Python:** swaps `apify/actor-python*` for the matching `scrapely/actor-python*` image. Every Python build also uninstalls `apify`, pins `crawlee>=1.8,<2` and runs `pip check`, so an `apify` line in `requirements.txt` is corrected rather than breaking the build. The base images ship an `apify` module that re-exports `scrapely`, so `from apify import Actor` resolves to the Scrape.ly SDK.
- **Both, whatever that setting says:** calls to Apify's LLM gateway (`https://openrouter.apify.actor`) are pointed at the Scrape.ly LLM gateway, so a run token is never sent to Apify.

Then run it on the platform with a small input and print the results:

```shell
scrapely call -i '{"startUrls":[{"url":"https://crawlee.dev"}]}' -o
```

`call` streams the run log and exits with code 1 if the run doesn't end as `SUCCEEDED`. To dig into a run, use `scrapely runs log <runId>` and `scrapely datasets get-items <datasetId> --format json --limit 20`.

If your Actor's source lives on GitHub, you can also link the repository as the Actor's source and get a new build on every push to the linked branch.

## Step 3: Check the code paths that differ

Most Actors need nothing here. These are the cases worth a look before you switch traffic over:

- **Proxy groups.** `RESIDENTIAL` and `DATACENTER` are available, and the Apify input names (`useApifyProxy`, `apifyProxyGroups`, `apifyProxyCountry`) keep working; the run receives each proxy object under both names. Other groups, such as `GOOGLE_SERP`, are refused with a clear error, never rerouted or billed. For Google results, call a Store Actor such as the [Google Search Scraper](https://scrape.ly/store/querystone/google-search-scraper.md) instead.
- **Environment variables and secrets.** A value that starts with `@` in `environmentVariables` comes from your local secrets for `scrapely run` (`scrapely secrets add target-api-key`), but local secrets never reach the platform. Set real values on the Actor version under **Source → Environment Variables** with **Secret** checked. Each `scrapely push` replaces the version it uploads, including those variables, so set them again after a push or script it with `scrapely api`.
- **Local runs.** `scrapely run` uses your local Node.js or Python, so install the SDK locally: `npm install scrapely` for Node.js, or `pip install scrapely-python` in your virtual environment for Python. Never `pip install scrapely`: that PyPI name belongs to an unrelated library.
- **`requirements.txt`** **.** Leave out `scrapely-python`, `scrapely` and `apify`. The SDK is preinstalled in the base images.
- **Pay-per-event charging.** If your pricing uses `apify-default-dataset-item`, the SDKs recognize it next to `scrapely-default-dataset-item`, so `Actor.pushData()` charging keeps working.

## Step 4: Point your apify-client code at Scrape.ly

The API clients are drop-in replacements for `apify-client`. Class and method names match, and `ApifyClient` is exported as an alias, so most scripts need three edits: the import, the base URL and the token.

**Node.js.** Install `scrapely-client`:

```shell
npm install scrapely-client
```

```javascript
// Before: import { ApifyClient } from 'apify-client';
import { ApifyClient } from 'scrapely-client';

const client = new ApifyClient({
  token: process.env.SCRAPELY_TOKEN, // a Scrape.ly token
  baseUrl: 'https://api.scrape.ly',
});

const run = await client.actor('username/my-actor').call(
  { startUrls: [{ url: 'https://example.com' }] },
  { maxTotalChargeUsd: 2 }, // stop the run if it would cost more than $2
);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

Always set `baseUrl`: the current release defaults to a non-production URL, and the client doesn't read the token or URL from environment variables. If you build public storage URLs, set `publicBaseUrl` too. New code can use the `ScrapelyClient` name; the alias is there so you don't have to touch every file.

**Python.** Install `scrapely-client-python` (Python 3.11 or newer):

```shell
pip install scrapely-client-python
```

```python
# Before: from apify_client import ApifyClient
import os
from decimal import Decimal
from scrapely_client import ApifyClient

client = ApifyClient(os.environ["SCRAPELY_TOKEN"], api_url="https://api.scrape.ly")

run = client.actor("username/my-actor").call(
    run_input={"startUrls": [{"url": "https://example.com"}]},
    max_total_charge_usd=Decimal("2"),
)
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)
```

`ApifyClientAsync` and `ApifyApiError` are aliases too. Pass `api_url` every time, for the same reason as `baseUrl` above.

Two more notes from the [JavaScript client docs](https://scrape.ly/docs/client-js.md) and the [Python client docs](https://scrape.ly/docs/client-python.md): endpoints Scrape.ly doesn't implement return an error, and `task(id).publish()` and `.unpublish()` are Scrape.ly additions. `call()` returns the run for every final status without throwing, so keep checking `run.status`.

## Step 5: Recreate schedules, webhooks and integrations

Schedules and webhooks belong to your account, not your code, so they don't move with `push`. Create them in the dashboard, or from the same client you just switched.

**Schedules** run an Actor or a task on a cron expression, evaluated in the timezone you set:

```javascript
await client.schedules().create({
  name: 'nightly',
  cronExpression: '0 3 * * *',
  timezone: 'UTC',
  actions: [{ type: 'RUN_ACTOR', actorId: 'ACTOR_ID' }],
});
```

**Webhooks** send a POST to your URL when a run changes state:

```javascript
await client.webhooks().create({
  eventTypes: ['ACTOR.RUN.SUCCEEDED', 'ACTOR.RUN.FAILED'],
  condition: { actorId: 'ACTOR_ID' },
  requestUrl: 'https://example.com/hooks/scrapely',
});
```

Run events cover created, succeeded, failed, aborted, timed out and resurrected, and build events cover your own Actors. The payload carries the run's ID, status, timing and default dataset ID. To authenticate deliveries, put a secret of your own in `headersTemplate` or in the URL's query string.

**Integrations.** Instead of separate integration setups, Scrape.ly uses automations: pick a delivery under **Then** to send each run's results to Slack, Discord, email, Google Sheets, Amazon S3 or R2, or a GitHub repository. An automation can also combine several Actors, filter for new items and add an AI summary before it delivers. For code and pipelines there's a GitHub Action, an n8n community node, and LangChain, LangGraph and LlamaIndex integrations.

**CI.** Deploy from a pipeline with an API key in the environment, never `scrapely login`:

```shell
export SCRAPELY_API_URL=https://api.scrape.ly
export SCRAPELY_TOKEN=<your API key>
scrapely push --wait-for-finish 600
```

A scoped key with `actors:build` and `actors:read` is enough for deploys.

## Step 6: Publish your Apify Actor in the Scrape.ly Store

If you sell Actors on Apify, you can list the same Actor in the [Scrape.ly Store](https://scrape.ly/store.md). One change is required: the Dockerfile's `FROM` line must use a `scrapely/actor-node` image (Node.js 20, 22, 24 or 26) or a `scrapely/actor-python` image (Python 3.11 or later). Browser variants such as `scrapely/actor-node-playwright-chrome` count, and a project without a Dockerfile always passes. The `apify/actor-*` images build and run, but they don't pass the publish check.

The other publish checks are the usual ones: a title of 3 to 100 characters, a description of 20 to 500, a README with at least 300 characters, 1 to 3 categories, a successful build, an input schema, and a verified identity. Then set pay-per-event pricing in the pricing wizard. To charge per result, make `scrapely-default-dataset-item` the primary event, or charge your own events with `Actor.charge()`.

The economics favor creators. On Scrape.ly you earn 80% of what users pay in event fees with purchased credits, and the run's platform usage (compute, storage, proxy) is billed to the user, not deducted from your share. On Apify, the [pay-per-event docs](https://docs.apify.com/platform/actors/publishing/monetize/pay-per-event) give the formula as `profit = (0.8 * revenue) - platform costs`. Payouts on Scrape.ly are on request: crypto or PayPal from $25, bank transfer from $500. Every step, from KYC to payouts, is in the [publishing docs](https://scrape.ly/docs/publishing.md).

## Step 7: Connect your AI agents

Once your Actors run on Scrape.ly, agents can run them too. The [hosted MCP server](https://scrape.ly/mcp.md) at `https://api.scrape.ly/mcp` signs in with OAuth or a token and gives Claude, Cursor, VS Code and ChatGPT the `search-actors`, `fetch-actor-details` and `call-actor` tools, plus runs and storage. In Claude Code:

```shell
claude mcp add --transport http scrapely https://api.scrape.ly/mcp
```

Give unattended agents a scoped API key with a daily charge limit, and pass `maxTotalChargeUsd` on each run.

## Using Store Actors instead of your own

If your Apify bill is mostly Store Actors rather than your own code, there's nothing to push. Find the equivalent in the Scrape.ly Store, which has 568 ready-made Actors with a pay-per-event price on each page, run it once on the free plan, and compare the cost with your Apify invoice. Proxy-heavy jobs are where the gap shows: Scrape.ly charges $4 per GB of residential traffic on the free plan and $2 on Business, against Apify's $8 per GB on Free and Starter and $7 on Business. Our [Apify pricing breakdown](https://scrape.ly/blog/apify-pricing.md) has the full numbers, and [Apify alternatives](https://scrape.ly/blog/apify-alternatives.md) compares the wider field.

## What to watch out for

- **Tokens.** Every Apify token in your scripts, CI secrets and webhooks needs a Scrape.ly replacement. Search your repos and CI settings for `APIFY_TOKEN` before you switch.
- **Base URL.** Put `baseUrl: 'https://api.scrape.ly'` (or `api_url=` in Python) in every client constructor, once, next to the token.
- **Secrets.** Script your version environment variables with `scrapely api` so each `push` sets them for you.
- **A test run per Actor.** Run each migrated Actor once with a small input and a `maxTotalChargeUsd` cap, and compare the dataset with the last Apify run before you move schedules.
- **Publishing.** Change the `FROM` line to a `scrapely/actor-*` image before you submit to the Store.
- **Proxy inputs.** Keep `RESIDENTIAL` or `DATACENTER` in proxy inputs; for Google results, call the Google Search Scraper.

## FAQ

### Can I run my Apify Actor on Scrape.ly without changes?

In most cases, yes. Builds accept `apify/actor-*` base images, `apify` imports resolve to the Scrape.ly SDK, and the CLI reads `.actor/actor.json`. Check proxy groups and environment variables, and switch to a `scrapely/actor-*` base image if you want to publish.

### Do Apify tokens work on Scrape.ly?

No. Create a Scrape.ly token under **Settings → API & Integrations** and use it with the CLI, the API clients and the MCP server.

### What do I change in my apify-client code?

Three things: install `scrapely-client` (or `scrapely-client-python`) and change the import, set `baseUrl` (or `api_url`) to `https://api.scrape.ly`, and use a Scrape.ly token. `ApifyClient` still works as a class name.

### Do my Apify proxy settings still work?

Yes for residential and datacenter traffic. The input names `useApifyProxy` and `apifyProxyGroups` are accepted, and `APIFY_PROXY_*` environment variables are set in every run. Groups Scrape.ly doesn't offer, such as `GOOGLE_SERP`, are refused.

### Can I sell my Apify Actor in the Scrape.ly Store?

Yes, after switching the Dockerfile to a `scrapely/actor-*` base image and passing the publish checks. You keep 80% of event fees paid with purchased credits, and users pay the run's platform usage.

### How long does a migration take?

For a typical Actor, one `scrapely push` and a test run. Budget extra time for environment variables, schedules, webhooks and swapping tokens in your scripts and CI.

About the author

[Scrape.ly Team](https://scrape.ly/blog/author/scrapely-team.md) The team behind Scrape.ly

Scrape.ly is the web data platform from TeraShift GmbH in Switzerland, powered by Evomi: ready-made scrapers in the Scrape.ly Store, a cloud runtime, an API with JavaScript and Python SDKs, proxies, a hosted anti-bot browser and an MCP server for AI agents.

- [Website](https://scrape.ly/index.md)

Keep reading

## More in Comparisons

- [![Pixel-art robot with an orange >\_ on its terminal head walks up to a lit arcade cabinet bearing the Scrape.ly logo and rests its hand on it, thinking of the Scrape.ly mark, while a dark cabinet with the blue Octoparse octopus logo stands switched off](https://scrape.ly/media/octoparse-alternatives.png?w=750)](https://scrape.ly/blog/octoparse-alternatives.md)
  ### [6 Best Octoparse Alternatives in 2026 (No Install Needed)](https://scrape.ly/blog/octoparse-alternatives.md)
  [The 6 best Octoparse alternatives for 2026, compared on what you actually pay for: cloud runs on the free plan, ready-made scrapers, per-result prices and automation. Plus the steps to move your Octoparse jobs.](https://scrape.ly/blog/octoparse-alternatives.md)
- [![Pixel-art robot with an orange >\_ on its terminal head stands between two matching arcade cabinets, one with the cyan Crawl4AI logo and one with the orange Firecrawl flame, thinking of the Scrape.ly mark next to a crate bearing the Scrape.ly logo](https://scrape.ly/media/crawl4ai-vs-firecrawl.png?w=750)](https://scrape.ly/blog/crawl4ai-vs-firecrawl.md)
  ### [Crawl4AI vs Firecrawl (and the Hosted Option That Beats Both)](https://scrape.ly/blog/crawl4ai-vs-firecrawl.md)
  [Crawl4AI vs Firecrawl: a free library you run yourself against a hosted API billed in credits. We compare setup, price per page, free tiers, extraction and MCP with worked costs, and show the hosted crawler that's cheaper than Firecrawl without the servers Crawl4AI needs.](https://scrape.ly/blog/crawl4ai-vs-firecrawl.md)
- [![Pixel-art robot with an orange >\_ on its terminal head holds the end of a long receipt with a dollar sign that prints out of an arcade cabinet bearing the orange Firecrawl flame logo](https://scrape.ly/media/firecrawl-pricing.png?w=750)](https://scrape.ly/blog/firecrawl-pricing.md)
  ### [Firecrawl Pricing in 2026: Plans, Credits and What 10,000 Pages Cost](https://scrape.ly/blog/firecrawl-pricing.md)
  [Firecrawl pricing explained: every plan, what each endpoint and option costs in credits, three worked examples with the arithmetic, the extra charges to plan for, and how a real Scrape.ly crawl compares.](https://scrape.ly/blog/firecrawl-pricing.md)

## The internet is full of data. Start owning yours.

Scrape.ly gives you the infrastructure, the tooling, and the team to extract, process, and operationalize web data at any scale. Setup takes minutes. The advantage lasts forever.

[Get Started Free](https://my.scrape.ly/register) [Chat with Support](mailto:support@scrape.ly?subject=Question%20about%20Scrape.ly\&body=Hi%2C%20I%20have%20a%20question%20about%20Scrape.ly.)

No credit card. No setup calls.
