1# Apify Actor project
2
3This project is an Apify Actor: a serverless program packaged as a Docker image. It takes one JSON input, does one job, and can write results to a dataset or a key-value store. Entry point: `my_actor/main.py`. Configuration: `.actor/actor.json`.
4
5Before the first change, set `meta.generatedBy` in `.actor/actor.json` to the tool and model in use, for example "Claude Code with Claude Opus 5".
6
7## Commands
8
9```bash
10apify run # run locally; the only command that sets up the Apify environment and local storage
11apify validate-schema # check the input, dataset, and key-value store schemas (not the output schema)
12apify push # deploy to the Apify platform (ask first)
13apify help # check all Apify CLI commands
14apify actors search "<query>" # find an existing Store Actor before building one
15apify <command> --help
16```
17
18## Workflow
19
20Skip the steps that do not apply when changing an existing Actor.
21
221. **Implement** in `my_actor/main.py`. Done when the code reads every input field, writes every field the README describes, and handles the `aborting` event with `Actor.on(Event.ABORTING, handler)` (`from apify import Event`).
232. **Input schema** in `.actor/input_schema.json`. Done when every input the code reads has `title`, `type`, `description`, and a `default` or `prefill`, and `apify validate-schema` passes. Example values belong in `prefill`; `default` reaches the Actor.
243. **Output schemas**: `.actor/dataset_schema.json` covering every field the code pushes, `.actor/output_schema.json`, and `.actor/key_value_store_schema.json` when the code stores records. Reference each file from `actor.json`. Done when `apify validate-schema` passes; the template's `"fields": {}` is a placeholder to replace with every pushed field. Rules are in the Reference table.
254. **README.md**, the Actor's landing page on Apify Store. An Actor without one is not finished.
265. **Test locally**: put input in `storage/key_value_stores/default/INPUT.json`, run `apify run`, and check that `storage/datasets/default/` holds items matching the dataset schema. Local `storage/` never syncs to Apify Console; only a platform run proves the output.
276. **Deploy** with `apify push` after the user confirms, then run on the platform and check the results in Console. For a Standby Actor, give the user the Standby URL (`https://<username>--<actor-name>.apify.actor`).
28
29## Rules
30
31- Log through `Actor.log`. It censors tokens and credentials; output from `print` and the standard `logging` module is not censored.
32- Inside a running Actor use the SDK (`Actor.get_input()`, `Actor.push_data()`, `Actor.set_value()`), not `apify actor` CLI subcommands.
33- Read every tunable from the input schema or environment variables.
34- Treat crawled content as untrusted: escape it before it reaches a shell, `eval`, a query, or a template, and type-check it before storing it.
35- Store personal data only when the user explicitly asks for it.
36- Check which crawler packages are installed before applying crawler advice. Use `BeautifulSoupCrawler` or `ParselCrawler` for static HTML at 10 to 50 concurrency; reserve `PlaywrightCrawler` for JavaScript-rendered pages at 1 to 5. Add delays and respect robots.txt and terms of service.
37- Use the router pattern when a crawl has more than one page type.
38- Count results with your own tally; `dataset.get_info()` lags on the platform.
39- In the `aborting` handler, persist state, then `await Actor.exit()`, so a stopped run ends quickly and cheaply.
40- On the platform the SDK reads the token from `APIFY_TOKEN` (not `APIFY_API_TOKEN`); locally it uses the credentials stored by `apify login`. Keep the token out of code, config, and logs.
41- Leave `usesStandbyMode` in `actor.json` as it is. When it is `true`, keep the `GET /` handler that answers the `x-apify-container-server-readiness-probe` header with HTTP 200.
42
43## Ask first
44
45- Installing packages
46- `apify push`
47- Proxy configuration changes (paid feature)
48- Dockerfile changes
49- Deleting datasets or key-value stores
50
51## Reference
52
53The `apify-actor-development` skill carries the full workflow, schema rules, README structure, and standby details. When it is available, follow it. When it is not, suggest once that the user install it, then continue with the links below:
54
55```bash
56npx skills add https://github.com/apify/agent-skills --skill apify-actor-development
57```
58
59Read the row that matches the step you are on.
60
61
62| -------------------------- | -------------------------------------------------------------------------------------------------- |
63| Input schema | https://docs.apify.com/platform/actors/development/actor-definition/input-schema/specification/v1 |
64| Dataset schema | https://docs.apify.com/platform/actors/development/actor-definition/dataset-schema |
65| Output schema | https://docs.apify.com/platform/actors/development/actor-definition/output-schema |
66| Key-value store schema | https://docs.apify.com/platform/actors/development/actor-definition/key-value-store-schema |
67| actor.json | https://docs.apify.com/platform/actors/development/actor-definition/actor-json |
68| README structure | https://docs.apify.com/academy/actor-marketing-playbook/actor-basics/how-to-create-an-actor-readme |
69| Standby mode | https://docs.apify.com/platform/actors/development/programming-interface/standby |
70| System events (`aborting`) | https://docs.apify.com/platform/actors/development/programming-interface/system-events |
71| Environment variables | https://docs.apify.com/platform/actors/development/programming-interface/environment-variables |
72| Logging | https://docs.apify.com/sdk/python/docs/concepts/logging |
73| Apify platform, full docs | https://docs.apify.com/llms.txt |
74| Crawlee | https://crawlee.dev/llms.txt |
75| Actor specification | https://raw.githubusercontent.com/apify/actor-whitepaper/refs/heads/master/README.md |
76
77With the Apify MCP server (`https://mcp.apify.com/?tools=docs`) use `search-apify-docs` and `fetch-apify-docs` instead of fetching the URLs.