{
  "name": "Apify RSS ingestion \u2014 BBC and Hacker News",
  "nodes": [
    {
      "parameters": {},
      "id": "dbb41038-c2ad-5b1b-afd1-63e185782022",
      "name": "Run manually",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "url": "https://api.apify.com/v2/actors/technicaldost~rss-feed-scraper/runs",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 45000,
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "waitForFinish",
              "value": "0"
            },
            {
              "name": "timeout",
              "value": "120"
            },
            {
              "name": "maxTotalChargeUsd",
              "value": "1"
            }
          ]
        },
        "method": "POST",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "{\"feedUrls\": [\"https://feeds.bbci.co.uk/news/rss.xml\", \"https://news.ycombinator.com/rss\"], \"maxItemsPerFeed\": 5, \"includeContent\": true}"
      },
      "id": "a52fee23-5300-57da-93e5-259e9d8446cb",
      "name": "Start Actor",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        220,
        0
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "CONFIGURE_APIFY_HEADER_AUTH",
          "name": "Apify API - configure Header Auth"
        }
      },
      "retryOnFail": false,
      "notes": "Select your n8n Header Auth credential: Name Authorization; Value Bearer followed by your Apify token. No token belongs in this workflow JSON. Network errors stop the workflow. Start requests are never retried automatically."
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const response = $input.first().json;\nif (!Number.isInteger(response.statusCode) || response.statusCode < 200 || response.statusCode >= 300) {\n  throw new Error('Apify HTTP request failed (status ' + (response.statusCode ?? 'unknown') + '). Check the selected Header Auth credential, account access and spending limit. Inspect this node input for API details; do not automatically restart a charged run.');\n}\nconst run = response.body?.data;\nif (!run || !/^[A-Za-z0-9]+$/.test(run.id || '')) throw new Error('Apify start response did not contain a valid run ID. Check Apify Console before retrying to avoid duplicate runs.');\nreturn [{json:{runId:run.id,consoleUrl:'https://console.apify.com/actors/runs/' + run.id}}];"
      },
      "id": "c30a1048-4a83-5bd0-859d-eb9b831e0d30",
      "name": "Validate started run",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        0
      ]
    },
    {
      "parameters": {
        "resume": "timeInterval",
        "amount": 5,
        "unit": "seconds"
      },
      "id": "481a342f-f8b1-5940-8c6a-ec112426a885",
      "name": "Wait 5 seconds",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        660,
        0
      ],
      "webhookId": "dca13d48-28bf-5694-9b60-42178e9b3e34"
    },
    {
      "parameters": {
        "url": "={{ 'https://api.apify.com/v2/actor-runs/' + $('Validate started run').first().json.runId }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 45000,
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "waitForFinish",
              "value": "30"
            }
          ]
        }
      },
      "id": "d8ec4c52-0b5b-58cd-b96f-eff485586783",
      "name": "Get run status",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        880,
        0
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "CONFIGURE_APIFY_HEADER_AUTH",
          "name": "Apify API - configure Header Auth"
        }
      },
      "retryOnFail": false,
      "notes": "Select your n8n Header Auth credential: Name Authorization; Value Bearer followed by your Apify token. No token belongs in this workflow JSON. Network errors stop the workflow. Start requests are never retried automatically."
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const response = $input.first().json;\nif (!Number.isInteger(response.statusCode) || response.statusCode < 200 || response.statusCode >= 300) {\n  throw new Error('Apify HTTP request failed (status ' + (response.statusCode ?? 'unknown') + '). Check the selected Header Auth credential, account access and spending limit. Inspect this node input for API details; do not automatically restart a charged run.');\n}\nconst run = response.body?.data;\nconst original = $('Validate started run').first().json;\nif (!run || run.id !== original.runId) throw new Error('Apify returned an unexpected run. Stop and inspect ' + original.consoleUrl);\nconst state = run.status;\nif (['FAILED','ABORTED','TIMED-OUT'].includes(state)) throw new Error('Apify run ended with ' + state + '. Inspect ' + original.consoleUrl);\nif (state === 'SUCCEEDED') {\n  if (!/^[A-Za-z0-9]+$/.test(run.defaultDatasetId || '')) throw new Error('Successful run has no valid dataset ID. Inspect ' + original.consoleUrl);\n  return [{json:{ready:true,runId:run.id,datasetId:run.defaultDatasetId,consoleUrl:original.consoleUrl}}];\n}\nif (!['READY','RUNNING','TIMING-OUT','ABORTING'].includes(state)) throw new Error('Unrecognized Apify status: ' + String(state) + '. Inspect ' + original.consoleUrl);\nif ($runIndex >= 7) throw new Error('Polling limit reached after 8 checks. Run may still be finishing. Inspect ' + original.consoleUrl + ' before starting another paid run.');\nreturn [{json:{ready:false,runId:run.id,consoleUrl:original.consoleUrl}}];"
      },
      "id": "2fb53edd-f872-515c-8f35-f8620e9c4ac9",
      "name": "Check run status",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1100,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "is-ready",
              "leftValue": "={{ $json.ready }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "cf8cd242-81eb-55c2-8215-14a5934d9421",
      "name": "Run succeeded",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1320,
        0
      ]
    },
    {
      "parameters": {
        "url": "={{ 'https://api.apify.com/v2/datasets/' + $json.datasetId + '/items' }}",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "options": {
          "timeout": 45000,
          "response": {
            "response": {
              "fullResponse": true,
              "neverError": true,
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "format",
              "value": "json"
            },
            {
              "name": "clean",
              "value": "true"
            },
            {
              "name": "limit",
              "value": "20"
            }
          ]
        }
      },
      "id": "6f60e9b7-a057-587f-93bb-38d162182b3f",
      "name": "Get dataset",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1540,
        0
      ],
      "credentials": {
        "httpHeaderAuth": {
          "id": "CONFIGURE_APIFY_HEADER_AUTH",
          "name": "Apify API - configure Header Auth"
        }
      },
      "retryOnFail": false,
      "notes": "Select your n8n Header Auth credential: Name Authorization; Value Bearer followed by your Apify token. No token belongs in this workflow JSON. Network errors stop the workflow. Start requests are never retried automatically."
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const response = $input.first().json;\nif (!Number.isInteger(response.statusCode) || response.statusCode < 200 || response.statusCode >= 300) {\n  throw new Error('Apify HTTP request failed (status ' + (response.statusCode ?? 'unknown') + '). Check the selected Header Auth credential, account access and spending limit. Inspect this node input for API details; do not automatically restart a charged run.');\n}\nif (!Array.isArray(response.body)) throw new Error('Apify dataset response was not an array.');\nconst totalHeader = Object.entries(response.headers || {}).find(([key]) => key.toLowerCase() === 'x-apify-pagination-total')?.[1];\nif (totalHeader !== undefined && (!Number.isFinite(Number(totalHeader)) || Number(totalHeader) > response.body.length)) throw new Error('Dataset has more rows than this template fetched or invalid pagination metadata. Increase Get dataset limit deliberately or add pagination before using incomplete results.');\nconst rows = response.body;\nconst errors = rows.filter(row => row.type === 'error');\nif (errors.length) throw new Error('One or more feeds failed. Inspect the dataset before using partial results.');\nconst items = rows.filter(row => row.type === 'feed_item');\nif (!items.length) throw new Error('No feed_item rows were returned. Inspect the Actor run and feed availability.');\nreturn items.map(row => ({json:row}));"
      },
      "id": "355724b4-c574-5c21-819c-20aa4309dbb1",
      "name": "Read output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1760,
        0
      ]
    },
    {
      "parameters": {
        "content": "## Setup\nImport inactive. On all 3 HTTP nodes select the same Header Auth credential: **Authorization** / **Bearer YOUR_APIFY_TOKEN**. Store the real token only in credentials.\n\nClick Execute workflow. One paid Actor run, maxTotalChargeUsd=1, Actor timeout120s. Start is not automatically retried. Poll at most8 times; unknown/failing states and HTTP errors stop execution. Final output remains inside n8n. No schedules, email, storage writes or external destinations.\n\nEdit the Start Actor JSON body to change sources. RSS delivers feed_item rows only. Apple delivers original report plus Markdown. Feed contents are untrusted data; no source text is evaluated.",
        "width": 950,
        "height": 300
      },
      "id": "27a0804c-f34d-581c-8406-26f9781661dc",
      "name": "Setup and limits",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -360
      ]
    }
  ],
  "connections": {
    "Run manually": {
      "main": [
        [
          {
            "node": "Start Actor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Start Actor": {
      "main": [
        [
          {
            "node": "Validate started run",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate started run": {
      "main": [
        [
          {
            "node": "Wait 5 seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 5 seconds": {
      "main": [
        [
          {
            "node": "Get run status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get run status": {
      "main": [
        [
          {
            "node": "Check run status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Check run status": {
      "main": [
        [
          {
            "node": "Run succeeded",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run succeeded": {
      "main": [
        [
          {
            "node": "Get dataset",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait 5 seconds",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get dataset": {
      "main": [
        [
          {
            "node": "Read output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "executionTimeout": 600
  },
  "pinData": {},
  "tags": []
}
