agentsclimarketplace

Apify actor builder

Skill thirdwatch-dev/scraping-skills/skills/apify-actor-builder

Web scraping skills for Claude & coding agents — anti-bot bypass, build-vs-buy, and ready-made scrapers for jobs, e-commerce, reviews, social, leads, real estate, travel, food & SEO. npx skills add thirdwatch-dev/scraping-skills

Install
npx -y skills add thirdwatch-dev/scraping-skills --skill apify-actor-builder

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when you want to package a web scraper as a deployable, monetizable Apify Actor in Python — set up the directory structure, choose a runtime pattern (HTTP / impit / Playwright / Camoufox), write the input/output/dataset schemas, deploy with the apify CLI, and configure pay-per-event pricing. Triggers on "build an Apify actor", "deploy/publish a scraper to Apify", "monetize a scraper", "pay-per-event pricing", "Apify input schema", "Apify output schema", "actor.json".

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

6.9 KB, as published. Nobody here has run it

Apify Actor Builder

How to turn a working scraper into a hosted, schedulable, billable Apify Actor in Python. This skill is about packaging and deployment — the scraping logic itself lives in anti-bot-scraping and web-scraping-playbook.

When to build an Actor (vs. a one-off script)

A plain script is fine for a single extraction you run by hand. Build an Actor when you need:

  • Recurring runs — scheduling, retries, and run history without your own cron/infra.
  • Managed hosting — proxies, memory, logging, and a dataset store handled for you.
  • Monetization — list it on the Apify Store and bill users pay-per-result.

If it's one-off, public, and unprotected, write an HTTP script instead.

Directory structure

An Actor is self-contained. No shared utilities across actors.

my-scraper/
├── .actor/
│   ├── actor.json          # name, version, memory, timeout, dockerfile, schemas
│   └── input_schema.json   # the form users fill out
├── my_actor/
│   ├── __init__.py
│   ├── __main__.py         # entry: asyncio.run(main())
│   └── main.py             # core logic
├── Dockerfile
├── requirements.txt
└── README.md

__main__.py is just the entrypoint:

import asyncio
from .main import main

asyncio.run(main())

Choose a runtime pattern (cheapest first)

Always climb the ladder HTTP → impit → Camoufox → Playwright. Each step costs more compute and proxy.

PatternBase imageMemoryUse when
A — Pure HTTPapify/actor-python:3.14256MBAPI, RSS, embedded JSON, or server-rendered HTML. httpx + beautifulsoup4. Preferred, cheapest.
B — HTTP + impit TLSapify/actor-python:3.14256MBSite blocks on TLS/JA3 fingerprint. impit.AsyncClient(browser='chrome') spoofs Chrome at the HTTP level — no browser.
C — Playwright + Crawleeapify/actor-python-playwright:3.14-1.58.02048MBYou need a real browser and the Crawlee crawler framework (queues, sessions).
D — Camoufox stealthapify/actor-python-playwright:3.122048MBDataDome / Cloudflare Turnstile / Akamai / PerimeterX. camoufox (hardened Firefox) with humanize=True.

Memory must be a power of 2 (256 / 512 / 1024 / 2048 / 4096). Proxy traffic is 70–90% of the cost of a browser-pattern Actor — block images/fonts/analytics to cut bytes.

Minimal main.py

from apify import Actor

async def main() -> None:
    async with Actor:
        actor_input = await Actor.get_input() or {}
        queries = actor_input.get("queries", [])
        seen = set()                                   # in-memory dedup

        for q in queries:
            Actor.log.info(f"Scraping: {q}")
            for item in await scrape(q):               # your logic
                key = item["url"]
                if key in seen:
                    continue
                seen.add(key)
                await Actor.push_data(item)            # NOT context.push_data()

Conventions that matter:

  • actor_input = await Actor.get_input() or {} — input may be None.
  • Always await Actor.push_data(result) — never context.push_data(). It survives Crawlee's 60s handler timeout.
  • Logging via Actor.log.info() / .warning() / .error().
  • Dedup with an in-memory set() on a stable id/URL.
  • Rate-limit: await asyncio.sleep(1.5–3) between pages; exponential backoff on 429.

The THREE schemas (easy to confuse — a common failure)

Apify has three distinct schemas. Getting one right does not satisfy the others.

1. Input schema.actor/input_schema.json, referenced by "input": "./input_schema.json" in actor.json. The form users fill out.

2. Dataset schema.actor/dataset_schema.json, referenced by "storages": {"dataset": "./dataset_schema.json"}. Controls how rows display in the Console. Its fields must be empty ({}) or the build fails with a cryptic "must be string".

3. Output schema — a top-level output block inside actor.json (not a separate file). This is what the "Actor quality → Add an output schema" warning wants; without it the Actor Quality Score caps at ~77/100. Minimal template:

"output": {
  "actorOutputSchemaVersion": 1,
  "title": "My Scraper",
  "properties": {
    "results": {
      "type": "string",
      "title": "Results",
      "template": "{{links.apiDefaultDatasetUrl}}/items"
    }
  }
}

Docs: https://docs.apify.com/platform/actors/development/actor-definition/output-schema

Deploy

cd my-scraper && apify push           # test account
cd my-scraper && apify push --force   # production

Cost rules:

  • Try HTTP → impit → Camoufox → Playwright, cheapest first.
  • Test with 1–2 inputs before any full run.
  • Cost ≠ cost-per-result. A $0.003 run returning 60 rows is $0.00005/row.

Monetize with Pay-Per-Event (PPE)

PPE bills the user per result. Apify keeps 20%, you keep 80%. The developer pays all compute + proxy; the user pays only the PPE rate.

Tiered PPE has exactly four tiers — FREE, BRONZE, SILVER, GOLD. Never add PLATINUM or DIAMOND (some older actor.json files carry six tiers from prior work — those are bugs; fix them when you touch them).

Charge-event caveat: only call Actor.charge('result') if the live PPE schema declares a custom result event. With the default apify-default-dataset-item event, each row is billed automatically and explicit charge() calls spam WARN logs — accumulated WARNs can trip Apify's QA and flag the actor "Under maintenance". Check the deployed schema before adding or removing charge() calls.

Reference implementations

Thirdwatch runs 70+ of these Actors on the Apify Store — useful as working examples of each pattern:

For the scraping logic underneath an Actor — choosing a data source and beating anti-bot — see the web-scraping-playbook and anti-bot-scraping skills.


Maintained by Thirdwatch. 70+ ready-made scrapers on the Apify Store.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.