Community n8n node package (@apify/n8n-nodes-apify) that integrates the Apify platform with n8n workflows. Provides nodes to run Apify Actors and tasks, fetch dataset / key-value-store data, and trigger workflows when Actor or task runs finish.
nodes/Apify/— main node source.Apify.node.ts/Apify.node.json— main Apify action node. AI Agent tool surfacing comes fromusableAsTool: true, not from the codex — see the note below onsubcategories.ApifyTrigger.node.ts/ApifyTrigger.node.json— trigger node (Actor / task run finished).Apify.properties.ts,Apify.methods.ts,properties.json— generated/maintained UI properties and methods.resources/— per-resource handlers:actors/,actor-tasks/,actor-runs/,datasets/,key-value-stores/, plusrouter.ts,executeActor.ts,genericFunctions.ts,hooks.ts, and resource locators.helpers/— sharedconsts.ts,hooks.ts,methods.ts.__tests__/— Jest specs (excluded fromtsconfig).apify-light.svg/apify-dark.svg— themed node icons.
credentials/—ApifyApi.credentials.ts(API key) andApifyOAuth2Api.credentials.ts(OAuth2, n8n cloud only).docs/— README screenshots.nodes.config.js—@n8n/node-cliconfig (package name, credentials, OpenAPI tags/excludes, name overrides).gulpfile.js,tsconfig.json,eslint.config.mjs,tslint.json,.eslintrc.prepublish.js,.prettierrc.js,jest.config.js— build / lint / format / test config..github/workflows/ci.yml— lint, type-check, build, test on push / PR tomaster..github/workflows/publish.yml— release-triggered build, version bump, npm publish.
- Language: TypeScript 5.5 (CommonJS, target ES2019, strict mode).
- Runtime: Node.js —
package.jsonrequires>=22.0.0; CI runs on24.x. - Package manager: npm
10.8.2. - n8n: peer dep
n8n-workflowis unpinned (*); build/dev tooling via@n8n/node-cli. - Test: Jest + ts-jest, with
nockfor HTTP mocking. - Lint/format: ESLint 9 (via
n8n-node lint), Prettier 3.
npm install— install deps.npm run build— build vian8n-node build(output indist/).npm run build:watch—tsc --watchonly.npm run dev— start n8n dev server with this node linked (n8n-node dev).npm run lint/npm run lint:fix—n8n-node lint.npx tsc --noEmit— type check (mirrors CI).npm test— Jest (setsWEBHOOK_URL=https://localhost:5678).npm run format— Prettier onnodesandcredentials.npm run merge:api— merge OpenAPI specs viaopenapi-merge-cli.
For trigger development on self-hosted n8n, export a public WEBHOOK_URL before npm run dev (see README).
- Default branch:
master. PRs targetmaster; CI must pass (lint, type-check, build, test). - Conventional Commits (
feat:,fix:,chore:,ci:,chore(release):);[skip ci]suffix for release version-bump commits. - Releases: publish a GitHub Release with tag
vX.Y.Z; thepublish.ymlworkflow extracts the version, runsnpm version, commitschore(release): set version to X.Y.Z [skip ci]to the target branch, and publishes@apify/n8n-nodes-apify@X.Y.Zto npm with--provenance --access public(skips if version already exists). - Two credential types:
apifyApi(API key, all installs) andapifyOAuth2Api(n8n cloud only).apifyApiexposes an n8n credentialtestthatGETs/v2/users/meagainstAPIFY_API_URL— socredentials/imports fromnodes/Apify/helpers/consts; keep base URLs inconsts.tsrather than hard-coding them. - Tests live in
nodes/Apify/__tests__/matching**/?(*.)+(spec).ts; excluded from the TypeScript build viatsconfig.json. Specs drive nodes through theexecuteWorkflowharness (__tests__/utils/executeWorkflow.ts) with workflow fixtures in__tests__/workflows/; to test a non-default parameter, spread the fixture and override that node'sparameters.
- Node engine mismatch is intentional/known:
package.jsonengines =>=22.0.0, README says 22.x, but CI (ci.yml,publish.yml) runs Node24.x. Don't "fix" one without checking the others. package.json#mainisindex.js(empty stub); n8n loads compiled artifacts fromdist/listed under then8nfield — always runnpm run buildbefore linking/testing in n8n.- Many node properties are generated from an OpenAPI spec via
nodes.config.js+npm run merge:api. When changing operation surface, update the spec /tagslist innodes.config.jsrather than hand-editing generated property files. Exception: hand-written operations such asresources/actors/scrape-single-url/keep their ownproperties.ts— edit those directly. - Never put
"AI"in a codexsubcategoriesblock. n8n's node creator drops every action for a node whosecodex.subcategories.AIcontainsToolswithoutRoot Nodes(generateNodeActionsinuseActionsGeneration.ts), so the node is inserted straight onto the canvas with its default resource/operation instead of showing the action list. It also buys nothing:subcategorizeItemsonly readssubcategories[X]whencategoriesalso containsX, and neither node lists"AI"as a category. AI Agent tool placement comes for free fromusableAsTool: true— n8n's backend clones the node intoapifyTooland stamps the AI codex itself (tool-generation/utils.js#setToolCodex).subcategories.Toolsis safe and is preserved onto that generated tool; it only picks the group inside the AI › Tools panel. fix: prevent duplicate actor runs with multiple input items(commit4a3836e) fixed a real regression — be cautious about changing input-iteration behavior inresources/executeActor.tsand related actor/task run handlers.- HTTP requests in
resources/genericFunctions.ts#apiRequestcarry a defaulttimeout(DEFAULT_REQUEST_TIMEOUT_MS, 60s; overridable per request viarequestOptions.timeout); dataset item downloads pass the longerDATASET_REQUEST_TIMEOUT_MS(10m). Network errors (no HTTP status, e.g. socket timeouts) are retried only for idempotentGETrequests (retryNetworkErrors: method === 'GET') — never widen this to POST, which could create duplicate Actor runs. All timeout constants live inhelpers/consts.ts. Actors → Scrape single URL(resources/actors/scrape-single-url/) always returns page metadata plus only the selectedoutputFormatfield (markdowndefault /html/text). The Web Content Scraper actor returnstext/html/markdownregardless of thesaveHtml/saveMarkdowninput flags, soexecute.tsstrips all three and re-adds the chosen one — keep that stripping when adding fields, or the lean AI-agent-friendly output contract breaks. AnincludeMetadatatoggle existed briefly and was reverted in1970964— don't re-add it.maxTotalChargeUsd("Maximum Cost per Run (USD)", defaultnull,minValue: 0) is a hand-written property on all four run-starting operations —actors/run-actor,actors/run-actor-and-get-dataset,actor-tasks/run-task,actor-tasks/run-task-and-get-dataset. It is sent as a query param only when non-null and> 0;0/empty deliberately means "no limit". Keep that guard and add the property to any new run-starting operation (Apify.node.spec.tsasserts every such operation exposes it).pollRunStatusis bounded: it caps polling at the run's owntimeoutSecsplusWAIT_FOR_FINISH_BUFFER_MS(5m grace), falling back toWAIT_FOR_FINISH_MAX_DURATION_MS(24h) when the run has no timeout, and throws once exceeded. Don't reintroduce unboundedwhile (true)polling.- The test harness's
getNodeParametermock mirrors n8n by falling back to the caller-supplied default (node.parameters[name] ?? fallbackValue) — don't "simplify" it back to a bare lookup, or handlers relying on defaults will seeundefinedin tests only. - Do not bump version manually; the release workflow owns
package.json/package-lock.jsonversion updates. - Don't commit
dist/; it is built in CI/release and listed inpackage.json#filesonly for publish. Apify.node.ts#executecarries an intentional// eslint-disable ... require-continue-on-fail—continueOnFailis handled insideexecuteAndLinkItems(resources/genericFunctions.ts), not inexecute. Don't remove the disable.