This workflow follows the Google Sheets → HTTP Request recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Twitter/X Scraper",
"nodes": [
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
20,
460
],
"id": "979d95f8-9197-4b89-adbf-a67ce9a8a7a9",
"name": "When clicking \u2018Test workflow\u2019"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "bc47f40e-0850-4762-9efb-999608a2c096",
"name": "counter",
"value": "={{ $json.count }}",
"type": "number"
},
{
"id": "92034c06-3976-4099-a7a5-28a252204472",
"name": "cursor",
"value": "={{ $json.cursor }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
400,
460
],
"id": "244050aa-04c4-4ad7-9d16-f8a34fff8332",
"name": "Counter"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "b2f9c3c3-32f4-48b4-b72f-76f03c35b750",
"name": "count",
"value": 1,
"type": "number"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
200,
460
],
"id": "395c383f-f6f3-4533-acf8-2eec2305e996",
"name": "Set Count"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "928894d0-2876-4996-9b1e-3d365b903771",
"leftValue": "={{ $('Counter').item.json.counter }}",
"rightValue": 3,
"operator": {
"type": "number",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1300,
460
],
"id": "8eab7011-986e-4089-a50c-78c20b952cfe",
"name": "If"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "a252eb06-03cd-4e3f-84b0-2a46082606ca",
"name": "counter",
"value": "={{ $('Counter').item.json.counter }}",
"type": "number"
},
{
"id": "43222ff8-c354-4afa-b050-7d55fe30c865",
"name": "cursor",
"value": "={{ $('Get Tweets').item.json.next_cursor }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
620,
780
],
"id": "9becc5d4-0b3f-4673-9bda-1f95addce714",
"name": "Set Increase"
},
{
"parameters": {
"jsCode": "// This code should be placed in an n8n Function node\n\n// Get the input items\nconst items = $input.all();\n\n// Process each item in the array\nreturn items.map(item => {\n // Create a new value for the count field\n let newCount = 1;\n \n // If there's an existing counter value, use it as a base\n if (item.json && item.json.counter !== undefined) {\n newCount = item.json.counter + 1;\n }\n \n // Ensure json property exists\n if (!item.json) {\n item.json = {};\n }\n \n // Set the value to the new field name \"count\"\n item.json.count = newCount;\n \n // Return the modified item\n return item;\n});"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
800,
780
],
"id": "bc04ea46-4d94-4648-bbee-9a5cceb5ae36",
"name": "Increase Count"
},
{
"parameters": {
"assignments": {
"assignments": [
{
"id": "5be1f203-28ea-4635-b42d-01f2a5bb367f",
"name": "count",
"value": "={{ $json.count }}",
"type": "string"
},
{
"id": "b68f8c17-c045-4b5b-8f8b-367ec72b72a3",
"name": "cursor",
"value": "={{ $('Set Increase').item.json.cursor }}",
"type": "string"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
980,
780
],
"id": "495b42f4-4b73-4adc-b1f4-0a4b8c22cf2d",
"name": "Set Count and Cursor"
},
{
"parameters": {
"operation": "append",
"documentId": {
"__rl": true,
"value": "1_GeFBfE6vFYFv0C-e_HmpKMB6kDnq676IeidTaWFg0Y",
"mode": "list",
"cachedResultName": "Twitter Data",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_GeFBfE6vFYFv0C-e_HmpKMB6kDnq676IeidTaWFg0Y/edit?usp=drivesdk"
},
"sheetName": {
"__rl": true,
"value": "gid=0",
"mode": "list",
"cachedResultName": "Sheet1",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1_GeFBfE6vFYFv0C-e_HmpKMB6kDnq676IeidTaWFg0Y/edit#gid=0"
},
"columns": {
"mappingMode": "defineBelow",
"value": {
"Tweet ID": "={{ $json.tweetId }}",
"URL": "={{ $json.url }}",
"Content": "={{ $json.content }}",
"Likes": "={{ $json.likeCount }}",
"Retweets": "={{ $json.retweetCount }}",
"Replies": "={{ $json.replyCount }}",
"Quotes": "={{ $json.quoteCount }}",
"Views": "={{ $json.viewCount }}",
"Date": "={{ $json.createdAt }}"
},
"matchingColumns": [],
"schema": [
{
"id": "Tweet ID",
"displayName": "Tweet ID",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "URL",
"displayName": "URL",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Content",
"displayName": "Content",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Likes",
"displayName": "Likes",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Retweets",
"displayName": "Retweets",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Replies",
"displayName": "Replies",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Quotes",
"displayName": "Quotes",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Views",
"displayName": "Views",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
},
{
"id": "Date",
"displayName": "Date",
"required": false,
"defaultMatch": false,
"display": true,
"type": "string",
"canBeUsedToMatch": true
}
],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {}
},
"type": "n8n-nodes-base.googleSheets",
"typeVersion": 4.5,
"position": [
940,
460
],
"id": "ce9f712b-c9bf-46e8-a833-de65500d0688",
"name": "Add to Sheet",
"credentials": {
"googleSheetsOAuth2Api": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "// This approach handles both single tweets and collections\n// It focuses on properly formatting the output for n8n\n// First, let's log what we're working with for debugging\nconsole.log(\"Input item structure:\", JSON.stringify($input.item, null, 2));\n\n// Function to format the date in a more human-readable way\nfunction formatDate(dateString) {\n if (!dateString) return '';\n \n try {\n const date = new Date(dateString);\n // Format: \"March 13, 2025 at 19:25\"\n return date.toLocaleString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit'\n });\n } catch (error) {\n console.log(\"Error formatting date:\", error);\n return dateString; // Return original if parsing fails\n }\n}\n\n// Check if this is a Twitter Search result with multiple tweets\nif ($input.item.json.tweets && Array.isArray($input.item.json.tweets) && $input.item.json.tweets.length > 0) {\n // This is a collection of tweets\n // In n8n, to output multiple items, we need to use an array of objects with a json property\n const items = $input.item.json.tweets.map(tweet => {\n return {\n json: {\n tweetId: tweet.id || '',\n url: tweet.url || '',\n content: tweet.text || '',\n likeCount: tweet.likeCount || 0,\n retweetCount: tweet.retweetCount || 0,\n replyCount: tweet.replyCount || 0,\n quoteCount: tweet.quoteCount || 0,\n viewCount: tweet.viewCount || 0,\n createdAt: formatDate(tweet.createdAt)\n }\n };\n });\n \n console.log(`Processing ${items.length} tweets`);\n \n // Return all items\n return items;\n} else {\n // This is a single tweet, just extract and return its data\n const tweetData = {\n tweetId: $input.item.json.id || '',\n url: $input.item.json.url || '',\n content: $input.item.json.text || '',\n likeCount: $input.item.json.likeCount || 0,\n retweetCount: $input.item.json.retweetCount || 0,\n replyCount: $input.item.json.replyCount || 0,\n quoteCount: $input.item.json.quoteCount || 0,\n viewCount: $input.item.json.viewCount || 0,\n createdAt: formatDate($input.item.json.createdAt)\n };\n \n console.log(\"Processing single tweet\");\n \n // Return as a single item\n return {\n json: tweetData\n };\n}"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
760,
460
],
"id": "916933bb-bffd-4360-be34-ec96f785c807",
"name": "Extract Info"
},
{
"parameters": {},
"type": "n8n-nodes-base.noOp",
"typeVersion": 1,
"position": [
1520,
460
],
"id": "dfbe6f01-8724-4dd1-9d4e-50a2b1b4a4f7",
"name": "No Operation, do nothing"
},
{
"parameters": {},
"type": "n8n-nodes-base.limit",
"typeVersion": 1,
"position": [
460,
780
],
"id": "372b41c3-15ed-42e6-9dbc-f4f2780f38e7",
"name": "Limit"
},
{
"parameters": {
"url": "https://api.twitterapi.io/twitter/tweet/advanced_search",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "query",
"value": "OpenAI"
},
{
"name": "queryType",
"value": "Top"
},
{
"name": "cursor",
"value": "={{ $json.cursor }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
580,
460
],
"id": "e77121c6-b7d2-4d2b-bdee-dfcbd665c84e",
"name": "Get Tweets",
"credentials": {
"httpHeaderAuth": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"content": "# Increasing Count & Cursor\n",
"height": 260,
"width": 1360,
"color": 5
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-200,
720
],
"id": "e3f4a99c-3271-485e-b96f-45a0e8afec2f",
"name": "Sticky Note"
},
{
"parameters": {
"content": "# Checking Count\n",
"height": 260,
"width": 500
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
1220,
380
],
"id": "9871378e-88d7-4417-aec5-68c266db1bc3",
"name": "Sticky Note2"
},
{
"parameters": {
"content": "# Scraping X",
"height": 260,
"width": 1360,
"color": 4
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-200,
380
],
"id": "0a948a34-476f-4a79-996e-97033b285b0b",
"name": "Sticky Note1"
},
{
"parameters": {
"content": "# Nate Herk | AI Automation",
"height": 80,
"width": 500,
"color": 6
},
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
420,
220
],
"id": "58f84c2a-b87c-4183-a855-67cd6e041822",
"name": "Sticky Note3"
}
],
"connections": {
"When clicking \u2018Test workflow\u2019": {
"main": [
[
{
"node": "Set Count",
"type": "main",
"index": 0
}
]
]
},
"Counter": {
"main": [
[
{
"node": "Get Tweets",
"type": "main",
"index": 0
}
]
]
},
"Set Count": {
"main": [
[
{
"node": "Counter",
"type": "main",
"index": 0
}
]
]
},
"If": {
"main": [
[
{
"node": "No Operation, do nothing",
"type": "main",
"index": 0
}
],
[
{
"node": "Limit",
"type": "main",
"index": 0
}
]
]
},
"Set Increase": {
"main": [
[
{
"node": "Increase Count",
"type": "main",
"index": 0
}
]
]
},
"Increase Count": {
"main": [
[
{
"node": "Set Count and Cursor",
"type": "main",
"index": 0
}
]
]
},
"Set Count and Cursor": {
"main": [
[
{
"node": "Counter",
"type": "main",
"index": 0
}
]
]
},
"Add to Sheet": {
"main": [
[
{
"node": "If",
"type": "main",
"index": 0
}
]
]
},
"Extract Info": {
"main": [
[
{
"node": "Add to Sheet",
"type": "main",
"index": 0
}
]
]
},
"Limit": {
"main": [
[
{
"node": "Set Increase",
"type": "main",
"index": 0
}
]
]
},
"Get Tweets": {
"main": [
[
{
"node": "Extract Info",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"versionId": "ae372884-4333-4aec-93d7-bba986ca3786",
"meta": {
"templateCredsSetupCompleted": true
},
"id": "NaxibodNsCmkKho0",
"tags": []
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
googleSheetsOAuth2ApihttpHeaderAuth
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
How this works
This workflow effortlessly extracts Twitter/X data into Google Sheets, saving you hours of manual scrolling and copying for research, competitor analysis, or trend tracking. It's ideal for marketers, journalists, or analysts needing quick insights from public tweets without coding expertise. The key step involves an HTTP request to fetch tweets in batches, incrementing a counter to handle pagination until the desired limit is reached, seamlessly appending results to your sheet for easy review.
Use this when you need to scrape a specific volume of recent tweets on a topic, such as monitoring brand mentions during a campaign launch. Avoid it for real-time alerts or private account data, as it relies on event-driven pulls rather than continuous monitoring. Common variations include swapping Google Sheets for CSV exports or adding filters in the code node to target tweets by keyword or user.
About this workflow
Twitter/X Scraper. Uses manualTrigger, googleSheets, noOp, limit. Event-driven trigger; 16 nodes.
Source: https://github.com/Zie619/n8n-workflows — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Disclaimer: this workflow only works on self-hosted instances due to the file system usage.
More workflow: https://aitool.wiki/
> ⚠️ Disclaimer: This workflow uses Community Nodes and requires a self-hosted n8n instance.
This n8n workflow automates the process of scraping job listings from both LinkedIn and Indeed platforms simultaneously, combining results, and exporting data to Google Sheets for comprehensive job ma
This workflow watches a Slack channel for shared LinkedIn profile screenshots or PDFs, uses easybits Extractor to convert the file into structured candidate fields, deduplicates against a Google Sheet