---
title: "Input & output · Scrape.ly Docs"
description: "Read Actor input and write results to datasets and key-value stores."
url: https://scrape.ly/docs/sdk-node-input-output
markdown: https://scrape.ly/docs/sdk-node-input-output.md
---

# Input & output

> Read Actor input and write results to datasets and key-value stores.

An Actor reads its input once at start, writes results to its default dataset, and keeps files and state in its default key-value store. This page covers the `Actor` shortcuts for those three jobs.

## Input

### `Actor.getInput<T>()`

`getInput()` returns `Promise<T | null>` and requires `Actor.init()`. It looks for input in this order:

1. If `ACTOR_METAMORPH_COUNT` is greater than `0`, the `INPUT-METAMORPH-{count}` record of the default key-value store.
2. `ACTOR_INPUT`, decoded as base64 JSON, then as plain JSON.
3. Only when `ACTOR_INPUT` is unset: the default key-value store record named by `ACTOR_INPUT_KEY` (normally `INPUT`).

On the platform, `ACTOR_INPUT` is always set when the run has input, and the same input is stored in the `INPUT` record. Locally, `scrapely run` sets `ACTOR_INPUT` from `-i`, `-f`, or `storage/key_value_stores/default/INPUT.json`.

```ts
type Input = { startUrls: string[]; maxPages?: number };

const input = await Actor.getInput<Input>();
if (input === null) throw new Error('No input was provided');
```

Secret input fields (`isSecret` in the input schema) arrive already decrypted: the platform decrypts them before the container starts, and the run's own token reads the `INPUT` record decrypted. See [Secret input fields](https://scrape.ly/docs/security.md#secret-input-fields). The SDK only decrypts values itself if `APIFY_INPUT_SECRETS_PRIVATE_KEY_FILE` and `APIFY_INPUT_SECRETS_PRIVATE_KEY_PASSPHRASE` are set, which platform runs don't do.

### `Actor.getInputOrThrow<T>()`

```ts
const input = await Actor.getInputOrThrow<Input>();
```

Throws `Input does not exist` when `getInput()` returns `null` or `undefined`. It doesn't validate the shape; check required fields yourself before doing expensive work.

### `Actor.input`

`Actor.input` is a synchronous getter that parses `ACTOR_INPUT` (base64 JSON, then plain JSON) on first access and caches the result. It returns `undefined` when `ACTOR_INPUT` is unset or invalid, doesn't read the key-value store, and doesn't need `init()`. Prefer `getInput()`.

## Output and key-value records

### `Actor.pushData()`

`pushData(item)` writes one object or an array of objects to the default dataset. Each object must serialize to JSON smaller than 9 MB.

```ts
await Actor.pushData({ url: 'https://example.com', title: 'Example' });
await Actor.pushData([
    { page: 1, value: 'a' },
    { page: 2, value: 'b' },
]);
```

Pass an event name to charge a pay-per-event event once per item:

```ts
const result = await Actor.pushData(items, 'page-scraped');
if (result.eventChargeLimitReached) {
    await Actor.exit('Charge limit reached');
}
```

With an event name, `pushData()` returns a `ChargeResult`. It charges before writing and only writes as many items as the run's remaining budget (`maxTotalChargeUsd`) covers, so items beyond the cap are dropped, not written for free. On an Actor that isn't priced per event, the items are written and nothing is charged. See [Charging](https://scrape.ly/docs/sdk-node-actors.md#charging).

If the write fails after the charge was accepted and the SDK knows nothing was stored (the API returned an error, or the connection was never made), `pushData()` [voids the charge](https://scrape.ly/docs/api-runs.md#void-a-charge) and rethrows. A timeout after sending keeps the charge, since the items may have been stored. Voids that can't be confirmed are retried before the next push and at exit.

On a pay-per-event Actor whose pricing includes the default dataset item event, every write to the default dataset is charged automatically, including writes made through `Dataset` or a crawler's `pushData`. In that case `pushData()` also returns a `ChargeResult`.

`pushData()` throws `Cannot charge for synthetic event '<name>' manually` if you pass `apify-default-dataset-item` or `scrapely-default-dataset-item` as the event name.

### `Actor.getValue()` and `Actor.setValue()`

These read and write records of the default key-value store:

```ts
const state = await Actor.getValue<{ cursor: string }>('STATE');
await Actor.setValue('STATE', { cursor: 'next' });
await Actor.setValue('TEMPORARY', null); // deletes the record
```

`getValue()` returns `null` when the record doesn't exist. `setValue()` stores objects as JSON. Its options (`RecordOptions`):

| Option | Meaning |
| --- | --- |
| `contentType` | MIME type of the record; when set, the value must be a string or `Buffer` |
| `doNotRetryTimeouts` | Don't retry the write after a timeout |
| `timeoutSecs` | Custom timeout for the write, in seconds |

```ts
await Actor.setValue('PAGE_HTML', '<h1>Hello</h1>', {
    contentType: 'text/html',
});
```

To work with other stores or list keys, open the store with `Actor.openKeyValueStore()` (see [Storages & Crawlee](https://scrape.ly/docs/sdk-node-storages.md#key-value-store)). For how callers download datasets and records, see [Results](https://scrape.ly/docs/results.md).

---

- Previous: [Lifecycle](https://scrape.ly/docs/sdk-node-lifecycle.md)
- Next: [Storages & Crawlee](https://scrape.ly/docs/sdk-node-storages.md)
- All docs: [Docs index](https://scrape.ly/docs.md), [llms.txt](https://scrape.ly/llms.txt)
