Clean, filter and dedupe Apify Dataset rows, then run an AI prompt on each row with no API key of your own. $1 per 1,000 rows kept, plus $1 per 1,000 AI rows, plus model usage on your Apify account.
Dataset to read. Leave this empty when you paste rows in Inline rows. The run needs read access.
Type:string
Inline rows
data
Optional
Up to 1,000 JSON objects to clean in this run. Leave this empty when datasetId is set. For more rows, write them to a Dataset first.
Type:object[]
Max. items:1000
Filter mode
filterMode
Optional
AND keeps a row only when every filter matches. OR keeps a row when any filter matches. No filters keeps every row that transforms cleanly.
Type:string
Default:AND
Options:
ANDOR
Filters
filters
Optional
Up to 40 filters, each {"op", "field", ...}. equals, notEquals, contains, notContains, startsWith, endsWith: "value" to compare. gt, gte, lt, lte: "value" as a number, an ISO 8601 date such as "2026-10-01" or "2026-10-01T08:00:00Z", or a relative bound "now", "now-7d", "now-24h" or "now-30m" counted from the run start. A date without a time is midnight UTC. between: "value" [low, high] using those same numbers or dates. in, notIn: "value" list. regex: "pattern", a Python regular expression. empty, notEmpty, true, false: no value. Add "caseSensitive": false to compare text in any letter case. Comparisons are exact: the number 10 is not the string "10" until a cast transform runs. Missing fields are empty, and they are not equal to null. A dotted name such as price.value or metadata.title reads the value nested inside objects; a top-level field with that exact name wins. Filters read the names after the transforms. A name only renames moved is read under its new name; a name a drop or keep removed, or a renamed field another transform changed, fails the run before a row is read.
Type:object[]
Max. items:40
Transforms
transforms
Optional
Up to 40 transforms, applied in order before filters, each {"op", "field", ...}. rename: "field", "to". drop: "field" or "fields". keep: "fields". trim, lowercase, uppercase: "field" or "fields", or one "field" and "to" to write the result to a new field. cast: "field", "to" string, number, integer or boolean (a blank cell becomes null). default: "field", "value". regexReplace: "field", "pattern", "replacement" (empty removes the matches). regexExtract: "field", "pattern", optional "to", optional "group" (a group number or name; by default the first group that matched). template: "field", "template" with {{otherField}}. A transform that cannot be applied drops that row and records a code in RUN_SUMMARY. It does not write a guessed value. A dotted "field" such as metadata.title changes the nested value where it is, and rename moves it to the top-level name in "to". A field that does not exist yet, and every "to", is a top-level name. llm: "prompt" with {{field}} placeholders, and "to" for a text answer or "outputFields" for typed answers; optional "model" (an OpenRouter id, default openai/gpt-4o-mini), "maxTokens" (1-2048, default 256), "temperature" (0-2, default 0), "system" and "onError" (keep or drop). The model runs through apify/openrouter with no API key of your own; its usage is billed to your Apify account. One llm transform per run. Filters that read fields the llm step and later transforms do not change run before the model, so rows they remove cost no model call.
Type:object[]
Max. items:40
Remove duplicates by
dedupeBy
Optional
Field names that identify a row, such as email or url. After the transforms and filters, only the first row for each value is written; later rows are counted in rowsDuplicate and charged as read, not kept. Values compare exactly, so lowercase an email first. Rows whose key fields are all empty are kept. A dotted name such as metadata.title reads a nested value. Up to 10 fields.
Type:string[]
Max. items:10
Maximum kept rows
maxItems
Optional
Stop after this many kept rows. 1 to 10000000. Omit it to process the whole source. Filtered, duplicate, skipped and failed rows do not count toward this cap.
Type:integer
Minimum:1
Maximum:10000000
Parallel model calls
llmConcurrency
Optional
How many rows are sent to the model at once, 1-32. Default 8. Lower it if the model provider throttles.
Type:integer
Minimum:1
Maximum:32
Maximum model calls
maxLlmRows
Optional
The run sends at most this many rows to the model, 1-1000000. Default 1000. Rows past it get llmError LLM_LIMIT and are kept or dropped by onError. Identical prompts answered from the cache do not count.
Type:integer
Minimum:1
Maximum:1000000
Maximum model usage (USD)
maxLlmCostUsd
Optional
Stop sending rows to the model once the usage.cost that apify/openrouter reports adds up to this amount, 0.01-1000. Default 1. Calls already in flight finish, so the total can pass it by a few calls.
Type:number
Minimum:0.01
Maximum:1000
Reuse answers to identical prompts
llmCache
Optional
Default true. Rows whose filled-in prompt is identical share one model call. They still get the answer and are charged as AI rows.
Type:boolean
Output dataset name
outputDatasetName
Optional
Optional. Empty writes the run's default dataset, which a webhook can pass on as datasetId. A name stores rows in that dataset instead. Leave it empty if the run cannot create datasets.
Type:string
Max. length:63
Default:
Integration payload
payload
Optional
Added by an Apify Actor-to-Actor integration. It is accepted so the run can start and is not read. Set datasetId to {{resource.defaultDatasetId}} in the integration input.