How to Build & Publish Your First Apify Actor
Quick Answer
Build an Apify Actor in four steps: initialize with apify create, write Crawlee scraper code, test locally with apify run, deploy with apify push. After the push, add .actor/INPUT_SCHEMA.json and a strong README.md so others can run it from the Console; use Publication when you want it in the Apify Store.
Actors are how you turn a script into a hosted, schedulable, API-addressable tool on Apify. See also What are Apify Actors?.
Prerequisites
- Node.js or Python (match your template).
- Apify CLI installed and logged in (
apify login). - An Apify account.
Step 1: Initialize with apify create
Scaffold a project (example: JavaScript Crawlee starter):
npm install -g apify-cli
apify create my-actor
# Choose a template such as the Crawlee cheerio or playwright starter
cd my-actor
Python equivalent:
apify create my-actor -t python-start
cd my-actor
You get a Dockerfile, .actor folder hooks, and a src/ entrypoint ready for Actor.init() / Actor.exit() patterns.
Step 2: Write your Crawlee scraper code
Read input with Actor.getInput(), crawl with CheerioCrawler or PlaywrightCrawler, and persist rows with Actor.pushData().
Minimal Node example (src/main.js)
import { Actor } from 'apify';
import { CheerioCrawler } from 'crawlee';
await Actor.init();
const input = await Actor.getInput();
const startUrls = input?.startUrls ?? [{ url: 'https://news.ycombinator.com' }];
const crawler = new CheerioCrawler({
async requestHandler({ request, $ }) {
const title = $('.titleline > span > a').first().text().trim();
const href = $('.titleline > span > a').first().attr('href');
await Actor.pushData({
url: request.url,
title,
link: href?.startsWith('http') ? href : new URL(href ?? '', request.url).href,
});
},
});
await crawler.run(startUrls);
await Actor.exit();
Example input JSON (Console or API)
{
"startUrls": [{ "url": "https://news.ycombinator.com" }]
}
Example output row (dataset)
{
"url": "https://news.ycombinator.com",
"title": "Show HN: Example",
"link": "https://example.com"
}
If you started from the Crawlee Python tutorial, port the same extraction logic into the generated src/main.py and keep asyncio entry patterns from the template.
Step 3: Test locally with apify run
apify run
- Logs stream to the terminal.
- Data lands under
storage/datasets/default(and related storage folders) so you can diff output before paying for cloud runs. - Fix selectors, proxy settings, and max concurrency here, which iterates faster than push-only workflows.
Step 4: Deploy with apify push
apify push
This uploads your Actor to the platform. Open the Console → your Actor → run with the same input JSON to validate cloud behavior (memory, timeouts, proxies).
Step 5: Add INPUT_SCHEMA.json (Console UI)
Public and team-friendly Actors should declare .actor/INPUT_SCHEMA.json so the Console renders a form instead of raw JSON.
{
"title": "Hacker News Scraper Input",
"type": "object",
"schemaVersion": 1,
"properties": {
"startUrls": {
"title": "Start URLs",
"type": "array",
"description": "URLs to scrape. Defaults to Hacker News homepage.",
"editor": "requestListSources",
"prefill": [{ "url": "https://news.ycombinator.com" }]
}
}
}
Redeploy with apify push after changes.
Step 6: Write the Actor README.md
The README is the Store listing body. Explain what the Actor does, input fields, sample output, and limits (rate limits, required proxies).
# Hacker News Scraper
Scrapes front-page story titles and links from Hacker News.
## Input
- **startUrls** — Pages to crawl (default: https://news.ycombinator.com).
## Output
Dataset items include `url`, `title`, and `link` fields (see run preview in Console for live samples).
You can start from the community README template and adapt.
Step 7: Publish to the Apify Store
- Console → your Actor → Publication → Display information.
- Complete icon, name, short description, and categories.
- Click Publish to Store when eligible.
Self-serve publication still expects working runs, clear docs, and honest pricing metadata. Official reference: Publishing Actors.
Optional: monetize
From the Monetization tab you can enable pay per result, pay per event, or rental fees. Revenue share details are covered in Monetize Actors and Apify pricing.
Lifecycle summary
apify create → implement Crawlee + SDK → apify run → apify push → INPUT_SCHEMA + README → Publish (optional) → Monetize (optional).
Create an Apify account and push your first Actor →
Use JavaScript/TypeScript with Crawlee for Node, or Python with the Apify SDK for Python. Both paths support local runs via apify run and cloud deploys via apify push.
Run apify run in the project directory. Outputs are written to local storage folders under storage/ so you can inspect datasets without consuming cloud compute.
Yes. Configure monetization in the Actor settings: common models include pay per result, pay per event, and monthly rental. Revenue share rules are described in Apify’s publisher documentation.
A minimal scraper with schema and README can go live in under an hour. Production-hardening (retries, proxy strategy, validation, and docs) takes longer depending on site complexity.
You need runnable code, a clear README, and INPUT_SCHEMA for a good Console experience. Store publication also asks for metadata such as icon, name, description, and categories; follow the Console publication checklist.
apify push deploys the Actor to your Apify account. The Store is an extra publication step in the Console so the tool is discoverable publicly and optionally monetized.
Common mistakes and fixes
Actor fails with 'Memory limit exceeded'.
Increase the Actor's memory allocation in the run configuration. Profile your crawler to find memory leaks. Closing browser contexts after each page is the most common fix.
'apify push' fails with authentication error.
Run 'apify login' first and authenticate with your API token. Verify the Actor name in .actor/actor.json matches your Apify username/actor-name format.
Dataset is empty after a successful run.
Ensure you are pushing results with 'await Actor.pushData(item)' inside your crawler's handler. Check the run log for handler errors that might be silently suppressed.



