All skills
apify avatar

/apify-actor-development

@c009920 official
by apifyapify/agent-skills2.4k stars
259

Create, modify, debug, and deploy Apify Actors, and write their input and output schemas. Use when building an Actor from scratch, changing or troubleshooting Actor code, generating or updating .actor schema files, or pushing an Actor to the Apify platform. To wrap an existing non-Actor project, use apify-actorization instead.

Use this Skill: https://skilld.dev/gh/apify/agent-skills/apify-actor-development

This session only. Nothing lands on disk.

referencesoutput-schemas.md

≈1.9k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Output schemas

Generate or update dataset_schema.json, output_schema.json, and key_value_store_schema.json in .actor/, then wire them into actor.json. These files tell Apify Console how to display run results. Work on the Actor named in the request, or the one in the current directory.

1. Discover what the code outputs

  1. Read .actor/actor.json. Note any existing schema files, and any inline storages.dataset or storages.keyValueStore objects (they migrate into files in step 5).
  2. Search the repository for other .actor/*_schema.json files. Match their description style, field naming, example format, view size, and JSON formatting. A template's dataset_schema.json with "fields": {} is a placeholder to replace, not a style to match.
  3. Find every dataset write: pushData( in JS/TS, push_data( in Python.
  4. Find every key-value store write: setValue( in JS/TS, set_value( in Python. Ignore the default INPUT key.
  5. Find the output type: a TypeScript interface, or a Python TypedDict, dataclass, or Pydantic model. When one exists it is the canonical field list; derive the schema from it and cross-check against the code that fills the values.

Done when you can list every output field with its type, source location, and nullability, and every key-value store key pattern. Present that list to the user.

2. Write dataset_schema.json

{
    "actorSpecification": 1,
    "fields": {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object",
        "properties": {},
        "required": [],
        "additionalProperties": true
    },
    "views": {
        "overview": {
            "title": "Overview",
            "description": "Most important fields at a glance",
            "transformation": { "fields": [] },
            "display": { "component": "table", "properties": {} }
        }
    }
}

fields.properties is the superset: every field the Actor can output. Views select a subset of it for display.

Rules for every field:

Rule Detail
"type" Always present. AJV rejects nullable without type on the same field. A TypeScript union such as Foo | string becomes "type": ["object", "string"].
"nullable": true On every field. Websites and APIs drop values without warning. nullable is an Apify extension to draft-07.
"description" and "example" On every field. Examples are anonymized: "exampleuser", "Example Channel", ISO dates like "2025-01-15T12:00:00.000Z", URLs in the platform's format with fake IDs of the right length.
"required": [] and "additionalProperties": true On the top-level fields object and on every nested object inside properties. The nested level is the one most often missed.

Two patterns cover the rest:

"title": {
    "type": "string",
    "description": "Title of the scraped item",
    "nullable": true,
    "example": "Example Item Title"
},
"authorInfo": {
    "type": "object",
    "description": "Information about the author",
    "properties": {
        "name": { "type": "string", "nullable": true },
        "url": { "type": "string", "nullable": true }
    },
    "required": [],
    "additionalProperties": true,
    "nullable": true,
    "example": { "name": "Example Author", "url": "https://example.com/author" }
}

Arrays add "items": { "type": "string" }; enums add "enum": [...].

Views. transformation.fields lists the 8 to 12 fields a user wants at a glance, in column order. display.properties gives each one a label and a format from text, number, date, link, boolean, image, array, object. Transformation also accepts unwind, flatten, omit, limit, and desc.

3. Write key_value_store_schema.json (only when step 1 found store writes)

Group the writes by key pattern. A fixed key such as "REPORT" becomes a collection with key; a dynamic key such as `screenshot-${id}` becomes one with keyPrefix. Each collection uses exactly one of the two.

{
    "actorKeyValueStoreSchemaVersion": 1,
    "title": "Scraped Files",
    "description": "Downloaded files and screenshots",
    "collections": {
        "report": {
            "title": "Report",
            "description": "Final analysis report",
            "key": "REPORT",
            "contentTypes": ["application/json"]
        },
        "screenshots": {
            "title": "Screenshots",
            "description": "Page screenshots captured during scraping",
            "keyPrefix": "screenshot-",
            "contentTypes": ["image/png", "image/jpeg"]
        }
    }
}

contentTypes restricts MIME types. jsonSchema (draft-07) validates application/json records.

4. Write output_schema.json

{
    "actorOutputSchemaVersion": 1,
    "title": "Output of the <Actor name>",
    "description": "One sentence describing the output data",
    "properties": {
        "dataset": {
            "type": "string",
            "title": "Results",
            "description": "Dataset containing all scraped data",
            "template": "{{links.apiDefaultDatasetUrl}}/items"
        }
    }
}

Every property has "type": "string"; the Apify meta-validator accepts no other type here. When step 3 produced a store schema, add a second property with template {{links.apiDefaultKeyValueStoreUrl}}/keys.

Available template variables: links.apiDefaultDatasetUrl, links.apiDefaultKeyValueStoreUrl, links.publicRunUrl, links.consoleRunUrl, links.apiRunUrl, links.containerRunUrl, run.defaultDatasetId, run.defaultKeyValueStoreId.

5. Wire actor.json

"outputSchema": "./output_schema.json",
"storages": {
    "dataset": "./dataset_schema.json",
    "keyValueStore": "./key_value_store_schema.json"
}

Include keyValueStore only when step 3 ran. Move any inline schema objects into the files and replace them with these path strings. When actor.json still has the deprecated input or output key, rename it to inputSchema or outputSchema.

6. Validate

Run apify validate-schema. It checks the structure of the input, dataset, and key-value store schemas, not the field rules below, and CLI 1.10 skips a schema referenced as outputSchema, so check output_schema.json against the list by hand. The work is done when every line holds:

  • Every output field found in step 1 is in fields.properties.
  • Every field has type, nullable: true, description, and an anonymized example.
  • required: [] and additionalProperties: true sit on the top-level fields object and on every nested object.
  • Field names match the keys the code writes (camelCase or snake_case as the code has them).
  • The overview view lists 8 to 12 fields with correct formats.
  • Every output_schema.json property has "type": "string".
  • If a store schema exists, its collections cover every setValue / set_value call, each with key or keyPrefix but not both.
  • actor.json references every schema file through inputSchema, outputSchema, and storages.
  • apify validate-schema passes and reports the dataset and (if present) key-value store schemas, not only the input schema.
  • Style matches the other schemas in the repository, and fields derive from the existing type definition when one exists.

Then show the user the schemas and report the files written, the field count, the overview fields, and any field whose type or nullability needs the user's confirmation.

Source: SKILL.md on GitHub

2 warnings1d5 checks · Risk SAFE
  • Gen Agent Trust Hub1d

    This skill provides secure guidelines for developing Apify Actors, emphasizing credential protection, sanitization of external data, and the use of trusted package managers.

  • Socket1d

    No alerts

  • Snyk1d

    Risk: MEDIUM · 1 issue

  • Runlayer7mo

    1/8 files flagged

  • ZeroLeaks5mo

    1 finding · Score: 82/100

Signed by skilld at c009920. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated yesterday
  • Python
  • TypeScript
  • apify
  • actors
  • web-scraping
  • automation
  • nodejs
  • docker
  • serverless
  • data-processing

README badge

README badge for apify/agent-skills/apify-actor-development

Develops, debugs, and deploys Apify Actors—serverless cloud programs for web scraping, automation, and data processing using the Apify CLI and SDK. Covers Actor creation across JavaScript, TypeScript, and Python, local testing with `apify run`, schema configuration, and deployment to the Apify platform.

Generated from the current SKILL.md.

What programming languages does this skill support?
JavaScript, TypeScript, and Python. The skill prompts you to choose your language preference and uses the appropriate `apify create` template (`project_empty` for JS, `ts_empty` for TS, `python-empty` for Python).
Do I need to install the Apify CLI before using this skill?
Yes. The skill requires `apify` CLI to be installed and authenticated. Install via `npm install -g apify-cli` and authenticate with `apify login` or set the `APIFY_TOKEN` environment variable.
Does local testing with `apify run` upload results to Apify Console?
No. Local testing stores data only in your `storage/` directory. You must deploy with `apify push` and run on the platform to see results in Apify Console.
What should I use for web scraping - CheerioCrawler or PlaywrightCrawler?
Use CheerioCrawler for static HTML (10x faster) and PlaywrightCrawler only for JavaScript-heavy sites that require a browser.
How are sensitive credentials handled in Actors?
Use the `apify/log` package, which automatically censors API keys and tokens. Never pass `APIFY_TOKEN` as command-line arguments or log raw crawled content that may contain credentials.

Generated from the current SKILL.md. These answers refresh after source changes.