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.

referencesstandby-mode.md

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

Actor Standby mode reference

When to use Standby mode

Use Standby when the Actor must handle interactive, real-time HTTP requests — API endpoints, webhook receivers, real-time data lookups, MCP servers, or scraping APIs serving on-demand single-URL requests.

Configuration

actor.json

Set usesStandbyMode: true in .actor/actor.json:

{
    "actorSpecification": 1,
    "name": "my-api-actor",
    "title": "My API Actor",
    "version": "0.0",
    "usesStandbyMode": true,
    "webServerSchema": "./openapi.json",
    "meta": {
        "generatedBy": "<FILL-IN-TOOL-AND-MODEL>"
    },
    "dockerfile": "../Dockerfile"
}

OpenAPI Schema (webServerSchema)

Define an OpenAPI v3 schema describing the Actor's HTTP endpoints. This can be a file path (e.g., "./openapi.json") or an inline object in actor.json. Ensure that the schema conforms to the OpenAPI spec.

Why: The schema is rendered as Swagger UI in the Standby tab of Apify Console and on the Actor's Apify Store page. This lets users browse endpoint documentation and try out the API directly from the browser.

The presence of webServerSchema also counts as a quality metric for Actor publication.

Environment variables

Variable Description
ACTOR_WEB_SERVER_PORT Port the HTTP server must listen on. Access via SDK: JS Actor.config.get('containerPort'), Python Actor.configuration.web_server_port. Both default to 4321 locally
ACTOR_STANDBY_URL The public Standby URL (stable across runs, format: https://<username>--<actor-name>.apify.actor)
APIFY_META_ORIGIN Set to STANDBY when the Actor was launched in Standby mode

Readiness probe

The platform sends GET / requests with the header x-apify-container-server-readiness-probe to check server readiness. You MUST respond with HTTP 200. Keep the response lightweight.

Authentication

Callers authenticate to Standby URLs via:

  • Bearer token (recommended): Authorization: Bearer <APIFY_TOKEN>
  • Query parameter (fallback): ?token=<APIFY_TOKEN>

Input handling

Standby Actors receive per-request input via HTTP query parameters or request body — NOT via INPUT.json. The traditional input schema (input_schema.json) is used for Actor initialization/configuration only, not per-request data.

Complete examples

JavaScript / TypeScript (Express)

import { Actor, log } from 'apify';
import express from 'express';

await Actor.init();

const app = express();
app.use(express.json());
const port = Actor.config.get('containerPort');

// Readiness probe
app.get('/', (req, res) => {
    if (req.headers['x-apify-container-server-readiness-probe']) {
        return res.send('OK');
    }
    res.json({ status: 'Actor is running in Standby mode' });
});

// Example endpoint
app.get('/search', (req, res) => {
    const { query } = req.query;
    log.info('Handling search request', { query });
    // ... handle request ...
    res.json({ results: [] });
});

app.listen(port, () => log.info(`Listening on port ${port}`));

Python (FastAPI)

from apify import Actor
import uvicorn
from fastapi import FastAPI, Request

app = FastAPI()

@app.get('/')
async def root(request: Request):
    if 'x-apify-container-server-readiness-probe' in request.headers:
        return {'status': 'OK'}
    return {'status': 'Actor is running in Standby mode'}

@app.get('/search')
async def search(query: str):
    Actor.log.info('Handling search request', extra={'query': query})
    # ... handle request ...
    return {'results': []}

async def main():
    async with Actor:
        port = Actor.configuration.web_server_port
        server = uvicorn.Server(uvicorn.Config(app, host='0.0.0.0', port=port))
        await server.serve()

if __name__ == '__main__':
    import asyncio
    asyncio.run(main())

Rules

  • NEVER disable standby mode (usesStandbyMode: false) in .actor/actor.json without explicit user permission
  • NEVER call Actor.exit() after handling a request — the server must stay alive
  • ALWAYS listen on the SDK-provided port, not a hardcoded value
  • ALWAYS implement the readiness probe at GET /
  • Standby Actor READMEs must document: available endpoints, HTTP methods, request/response schemas, authentication (Authorization: Bearer <token>), and example calls.

Testing

Start the server with apify run, as for any Actor. It sets up the Apify environment and serves on port 4321, but it does not simulate the Standby load balancer or send the readiness probe, so exercise both yourself:

  1. Start apify run in the background and retry the probe below until it answers.
  2. Probe readiness: curl -H "x-apify-container-server-readiness-probe: true" http://localhost:4321/ returns HTTP 200.
  3. Call each endpoint with curl or httpie and check the responses.

Standby vs. container web server

Do not confuse these:

  • Container web server (ACTOR_WEB_SERVER_URL): per-run unique URL, no load balancing, no auto-scaling. Useful for live view UIs during a run.
  • Standby mode (ACTOR_STANDBY_URL): stable hostname, load-balanced across runs, auto-scaled based on traffic. Use this for production APIs.

Further reading

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.