Chapter 01 · Apify Actor Development
Subchapter 1.8
references/standby-mode.mdMarkdown5 KBView on GitHub
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.
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"
}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.
| Variable | Description |
|---|---|
ACTOR_WEB_SERVER_PORT | Port the HTTP server must listen on. Access via SDK: JS Actor.config.get('containerPort'), Python Actor.config.container_port |
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 |
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.
Callers authenticate to Standby URLs via:
Authorization: Bearer <APIFY_TOKEN>?token=<APIFY_TOKEN>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.
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}`));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.config.container_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())usesStandbyMode: false) in .actor/actor.json without explicit user permissionActor.exit() after handling a request — the server must stay aliveGET /Authorization: Bearer <token>), and example calls.apify run does not simulate Standby mode. To test locally:
node src/main.js or python src/main.py)curl -H "x-apify-container-server-readiness-probe: true" http://localhost:<port>/Do not confuse these:
ACTOR_WEB_SERVER_URL): per-run unique URL, no load balancing, no auto-scaling. Useful for live view UIs during a run.ACTOR_STANDBY_URL): stable hostname, load-balanced across runs, auto-scaled based on traffic. Use this for production APIs.