Skip to content

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.

Published
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
In this article
  1. What changes and what stays the same
  2. Before you start
  3. Step 1: Install the CLI and log in
  4. Step 2: Push your Apify Actor unchanged
  5. Step 3: Check the code paths that differ
  6. Step 4: Point your apify-client code at Scrape.ly
  7. Step 5: Recreate schedules, webhooks and integrations
  8. Step 6: Publish your Apify Actor in the Scrape.ly Store
  9. Step 7: Connect your AI agents
  10. Using Store Actors instead of your own
  11. What to watch out for
  12. FAQ
  13. Can I run my Apify Actor on Scrape.ly without changes?
  14. Do Apify tokens work on Scrape.ly?
  15. What do I change in my apify-client code?
  16. Do my Apify proxy settings still work?
  17. Can I sell my Apify Actor in the Scrape.ly Store?
  18. How long does a migration take?

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.

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 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 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 and the Python client docs: 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. 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 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.

Step 7: Connect your AI agents

Once your Actors run on Scrape.ly, agents can run them too. The hosted MCP server 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 has the full numbers, and Apify alternatives 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.

  • apify
  • apify actor
  • migration
  • apify-client
  • sdk
  • cli

About the author

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.

//Keep reading\\

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.

No credit card. No setup calls.