Skill 01 · Apify Actor Development
Subchapter 1.5
references/output-schemas.mdMarkdown7 KBView on GitHub
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.
.actor/actor.json. Note any existing schema files, and any inline storages.dataset or storages.keyValueStore objects (they migrate into files in step 5)..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.pushData( in JS/TS, push_data( in Python.setValue( in JS/TS, set_value( in Python. Ignore the default INPUT key.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.
{
"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.
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.
{
"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.
"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.
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:
fields.properties.type, nullable: true, description, and an anonymized example.required: [] and additionalProperties: true sit on the top-level fields object and on every nested object.output_schema.json property has "type": "string".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.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.