Building an Apify Actor with TypeScript 2026: Complete Tutorial
Build an Apify Actor from scratch with TypeScript, Crawlee, and the Apify SDK. Scaffold with apify create, choose Cheerio or Playwright, define input, run locally, then push to the cloud. Start building with a free Apify account.
Prerequisites
- Node.js 18+
- Apify account
- Apify CLI:
npm install -g apify-cli
Step 1: Create the project
apify create my-typescript-actor
Select Crawlee + Playwright (TypeScript) or Crawlee + Cheerio (TypeScript). Cheerio is faster for static HTML; Playwright handles JavaScript-heavy pages.
This creates:
my-typescript-actor/
├── .actor/
│ ├── actor.json
│ └── input_schema.json
├── src/
│ ├── main.ts
│ └── routes.ts # Playwright only
├── package.json
├── tsconfig.json
└── Dockerfile
Step 2: Actor structure
main.ts — entry point. Initialize Apify, read input, run crawler, exit:
import { Actor } from 'apify';
import { CheerioCrawler } from 'crawlee';
await Actor.init();
interface Input {
startUrls: { url: string }[];
maxRequestsPerCrawl?: number;
}
const input = (await Actor.getInput<Input>()) ?? {};
const { startUrls = [{ url: 'https://example.com' }], maxRequestsPerCrawl = 50 } = input;
const proxyConfiguration = await Actor.createProxyConfiguration();
const crawler = new CheerioCrawler({
proxyConfiguration,
maxRequestsPerCrawl,
async requestHandler({ $, request }) {
const title = $('title').text();
await Actor.pushData({ url: request.loadedUrl, title });
},
});
await crawler.run(startUrls);
await Actor.exit();
INPUT_SCHEMA.json (or input_schema.json) — defines the input form. See the input schema guide for details:
{
"title": "My Actor Input",
"type": "object",
"schemaVersion": 1,
"properties": {
"startUrls": {
"title": "Start URLs",
"type": "array",
"editor": "requestListSources",
"prefill": [{ "url": "https://example.com" }]
},
"maxRequestsPerCrawl": {
"title": "Max requests",
"type": "integer",
"default": 50,
"minimum": 1,
"maximum": 1000
}
},
"required": ["startUrls"]
}
Step 3: CheerioCrawler vs PlaywrightCrawler
| Crawler | Use when | Speed | JS support |
|---|---|---|---|
| CheerioCrawler | Static HTML, fast | Fast | No |
| PlaywrightCrawler | SPAs, login, dynamic content | Slower | Yes |
CheerioCrawler example:
import { CheerioCrawler } from 'crawlee';
const crawler = new CheerioCrawler({
async requestHandler({ $, request }) {
const links = $('a[href]').map((_, el) => $(el).attr('href')).get();
await Actor.pushData({ url: request.url, links });
},
});
PlaywrightCrawler example:
import { PlaywrightCrawler } from 'crawlee';
const crawler = new PlaywrightCrawler({
async requestHandler({ page, request }) {
await page.waitForSelector('.product-title');
const title = await page.locator('.product-title').first().textContent();
await Actor.pushData({ url: request.url, title });
},
});
Step 4: Storing results
Use Actor.pushData() to append rows to the default dataset. Data appears in the Storage tab after the run.
await Actor.pushData({ url: request.url, title, price });
For key-value output (single JSON):
await Actor.setValue('OUTPUT', { summary: { total: 42 } });
Step 5: Local testing
cd my-typescript-actor
apify login # if not already
apify run
Input comes from .actor/actor.json defaultInput or --input:
apify run --input '{"startUrls":[{"url":"https://crawlee.dev"}],"maxRequestsPerCrawl":5}'
Output is written to storage/datasets/default/. Check storage/datasets/default/*.json for scraped items.
Step 6: Push to Apify
apify push
This builds the Docker image and uploads the Actor. Run it from the Apify Console, schedule it, or call the API.
Summary
- apify create — scaffold a TypeScript Actor
- main.ts —
Actor.init(),getInput(), crawler,Actor.exit() - INPUT_SCHEMA — define startUrls, limits, proxy
- CheerioCrawler for static HTML, PlaywrightCrawler for JS sites
- Actor.pushData() — store results in the dataset
- apify run — test locally
- apify push — deploy to Apify Cloud
For testing, input schema design, and proxy setup, see the related guides above.
Create your first Actor with apify create, run it locally, then push. Deploy to Apify →
Yes. Apify CLI templates include TypeScript (Crawlee + Playwright, Crawlee + Cheerio). Use apify create and select the TypeScript option.
Run apify create, pick a TypeScript template, edit src/main.ts and .actor/input_schema.json, then apify run and apify push.
TypeScript adds type safety and better IDE support. Both work. Apify and Crawlee support both with full type definitions.
Actor.pushData() writes to the default dataset. Access it via the Storage tab in Console or the Apify API after the run finishes.




