This workflow corresponds to n8n.io template #13509 — we link there as the canonical source.
This workflow follows the Datatable → Gmail 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 →
{
"id": "rbbgcf1n8aIwsPQCyOyMm",
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Fetch Threads posts and send an HTML analytics report via email",
"tags": [],
"nodes": [
{
"id": "5a30db15-c360-4034-a14b-81e384388abb",
"name": "Split Out",
"type": "n8n-nodes-base.splitOut",
"position": [
4768,
1760
],
"parameters": {
"options": {},
"fieldToSplitOut": "data"
},
"typeVersion": 1
},
{
"id": "85c6ba79-41ef-4b4c-b7d8-7e5694c09abd",
"name": "Fetch Threads Posts",
"type": "n8n-nodes-base.httpRequest",
"position": [
4544,
1760
],
"parameters": {
"url": "https://graph.threads.net/v1.0/me/threads",
"options": {},
"sendQuery": true,
"sendHeaders": true,
"queryParameters": {
"parameters": [
{
"name": "fields",
"value": "id,text,timestamp,permalink"
},
{
"name": "locale",
"value": "zh_TW"
},
{
"name": "limit",
"value": "100"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $json.Threads_Token }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "d48cd4f8-319c-4796-9ce5-0549bc41ceae",
"name": "Fetch Post Insights",
"type": "n8n-nodes-base.httpRequest",
"position": [
4992,
1760
],
"parameters": {
"url": "=https://graph.threads.net/v1.0/{{$json[\"id\"]}}/insights",
"options": {},
"sendQuery": true,
"sendHeaders": true,
"queryParameters": {
"parameters": [
{
"name": "metric",
"value": "views,likes,replies,reposts,quotes"
},
{
"name": "locale",
"value": "zh_TW"
}
]
},
"headerParameters": {
"parameters": [
{
"name": "Authorization",
"value": "=Bearer {{ $('Set Token').item.json.Threads_Token }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "2f3e2e37-ef99-417f-9624-28a28202d9d1",
"name": "Email Analytics Report",
"type": "n8n-nodes-base.gmail",
"position": [
5664,
1760
],
"parameters": {
"sendTo": "YOUR_EMAIL_ADDRESS",
"message": "={{ $json.html }}",
"options": {},
"subject": "=Threads Analytics Report (Latest 100 Posts)"
},
"typeVersion": 2.1
},
{
"id": "8ae6c331-71a1-49d3-899f-e4bf19bc4338",
"name": "Build HTML Report",
"type": "n8n-nodes-base.code",
"position": [
5440,
1760
],
"parameters": {
"jsCode": "if (items.length === 0) {\n return [{ json: { hasItems: false, html: '<p>No posts found.</p>' } }];\n}\n\nconst safe = s => (s || '').toString().replace(/</g,'<').replace(/>/g,'>');\nconst toLocal = ts => ts ? new Date(ts).toLocaleString() : '';\n\n/** Totals **/\nconst totals = items.reduce((acc, i) => {\n const j = i.json;\n acc.posts += 1;\n acc.likes += Number(j.likes || 0);\n acc.replies += Number(j.replies || 0);\n acc.reposts += Number(j.reposts || 0);\n acc.quotes += Number(j.quotes || 0);\n acc.views += Number(j.views || 0);\n return acc;\n}, {posts:0, likes:0, replies:0, reposts:0, quotes:0, views:0});\n\n/** Table rows **/\nconst rows = items\n .sort((a,b) => new Date(b.json.timestamp) - new Date(a.json.timestamp))\n .map(i => {\n const j = i.json;\n return `\n <tr>\n <td>${toLocal(j.timestamp)}</td>\n <td style=\"max-width:520px;white-space:pre-wrap\">${safe(j.text).slice(0,200)}${j.text?.length>200?'\u2026':''}</td>\n <td style=\"text-align:right\">${Number(j.likes||0)}</td>\n <td style=\"text-align:right\">${Number(j.replies||0)}</td>\n <td style=\"text-align:right\">${Number(j.reposts||0)}</td>\n <td style=\"text-align:right\">${Number(j.quotes||0)}</td>\n <td style=\"text-align:right\">${Number(j.views||0)}</td>\n <td><a href=\"${j.permalink}\">Link</a></td>\n </tr>`;\n }).join('');\n\n/** HTML **/\nconst html = `\n <div style=\"font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif\">\n <h2 style=\"margin:0 0 8px\">Threads Analytics Report (${items.length} posts)</h2>\n <p style=\"margin:0 0 16px\">\n \ud83d\udc4d ${totals.likes} \ud83d\udcac ${totals.replies} \ud83d\udd01 ${totals.reposts} \ud83d\udde8\ufe0f ${totals.quotes} \ud83d\udc41\ufe0f ${totals.views}\n </p>\n <table border=\"1\" cellspacing=\"0\" cellpadding=\"8\" style=\"border-collapse:collapse;width:100%;font-size:14px\">\n <thead style=\"background:#f6f7f9\">\n <tr>\n <th style=\"text-align:left\">Time</th>\n <th style=\"text-align:left\">Content</th>\n <th>\ud83d\udc4d</th>\n <th>\ud83d\udcac</th>\n <th>\ud83d\udd01</th>\n <th>\ud83d\udde8\ufe0f</th>\n <th>\ud83d\udc41\ufe0f</th>\n <th>Link</th>\n </tr>\n </thead>\n <tbody>${rows}</tbody>\n </table>\n </div>\n`;\n\nreturn [{ json: { hasItems: true, html } }];"
},
"typeVersion": 2
},
{
"id": "440e1a86-b7e6-4494-a6c1-b904def2f0e8",
"name": "Merge Posts and Insights",
"type": "n8n-nodes-base.code",
"position": [
5216,
1760
],
"parameters": {
"jsCode": "const posts = $items('Split Out');\n\nconst OUT = items.map((ins, i) => {\n const post = (posts[i] || {}).json || {};\n\n const arr = ins.json?.data || [];\n const m = Object.fromEntries(arr.map(x => [x.name, x.values?.[0]?.value]));\n\n const ts = post.timestamp || post.created_time || post.created_at || '';\n\n return {\n json: {\n id: post.id,\n text: (post.text || '').trim(),\n timestamp: ts,\n permalink: post.permalink,\n likes: m.likes ?? 0,\n replies: m.replies ?? 0,\n reposts: m.reposts ?? 0,\n quotes: m.quotes ?? 0,\n views: m.views ?? m.impressions ?? 0\n }\n };\n});\n\nreturn OUT;"
},
"typeVersion": 2
},
{
"id": "d2ef1dab-12ba-4143-9ad0-589579586c8f",
"name": "Get Threads Token",
"type": "n8n-nodes-base.dataTable",
"position": [
4096,
1760
],
"parameters": {
"filters": {
"conditions": [
{
"keyName": "Platform",
"keyValue": "Threads"
}
]
},
"operation": "get",
"dataTableId": {
"__rl": true,
"mode": "list",
"value": "YOUR_DATA_TABLE_ID",
"cachedResultUrl": "",
"cachedResultName": "Token_Management"
}
},
"typeVersion": 1
},
{
"id": "910115ef-8311-40ae-a7cc-96e8f36805bd",
"name": "Set Token",
"type": "n8n-nodes-base.set",
"position": [
4320,
1760
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "f49e5cf9-57be-41bb-a798-bec884720c2f",
"name": "Threads_Token",
"type": "string",
"value": "={{ $json.Token }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "7435fc9d-0816-404c-87c2-43fca1141cc9",
"name": "Manual Trigger",
"type": "n8n-nodes-base.manualTrigger",
"position": [
3872,
1664
],
"parameters": {},
"typeVersion": 1
},
{
"id": "15a85b84-fb71-43fa-82c7-447c4417ea81",
"name": "Schedule Trigger (Every 2 Days)",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
3872,
1856
],
"parameters": {
"rule": {
"interval": [
{
"daysInterval": 2,
"triggerAtHour": 23
}
]
}
},
"typeVersion": 1.2
},
{
"id": "8cc6eee6-b33f-4662-a17d-5ab44f2ba2de",
"name": "Sticky Note - Main",
"type": "n8n-nodes-base.stickyNote",
"position": [
3600,
1184
],
"parameters": {
"width": 680,
"height": 512,
"content": "## How it works\n\nThis workflow pulls up to 100 of your latest Threads posts along with their engagement metrics \u2014 views, likes, replies, reposts and quotes \u2014 directly from the Threads Graph API.\n\nFor each post, a separate insights API call retrieves engagement data. The results are merged into a unified dataset, then a Code node builds a clean HTML table sorted by publish time (newest first), with a totals summary at the top. The report is delivered as an inline HTML email via Gmail.\n\nSupports both manual execution and automatic scheduling (every 2 days by default).\n\n## Setup steps\n\n1. Get a long-lived Threads access token from Meta for Developers \u2192 Threads API \u2192 Access Tokens. Enter it in the **Get Threads Token** node (Data Table, Platform = \"Threads\").\n2. Open **Email Analytics Report**, connect your Google account, and update the recipient email address.\n3. (Optional) Adjust the interval in **Schedule Trigger** \u2014 default is every 2 days at 11 pm.\n\n_Note: The Threads API allows up to 500 requests/day. Fetching 100 posts uses approximately 100 insight API calls._"
},
"typeVersion": 1
},
{
"id": "6037d286-0bed-4636-ab2a-a01963c12d21",
"name": "Sticky Note - Triggers",
"type": "n8n-nodes-base.stickyNote",
"position": [
3680,
2016
],
"parameters": {
"color": 7,
"width": 356,
"height": 136,
"content": "## Triggers\n\nRun manually on demand or on a schedule. Default: every 2 days at 11 pm. Adjust the interval in the Schedule Trigger node."
},
"typeVersion": 1
},
{
"id": "5b3e71b3-fdac-407e-8c04-64867971de35",
"name": "Sticky Note - Token setup",
"type": "n8n-nodes-base.stickyNote",
"position": [
4064,
1904
],
"parameters": {
"color": 7,
"width": 188,
"height": 264,
"content": "## Token setup\n\nReads the Threads long-lived access token from an n8n Data Table and passes it to the downstream fetch nodes."
},
"typeVersion": 1
},
{
"id": "713c0945-958b-4f11-9b7b-96093fb2f70a",
"name": "Sticky Note - Fetch",
"type": "n8n-nodes-base.stickyNote",
"position": [
4480,
1920
],
"parameters": {
"color": 7,
"width": 636,
"height": 136,
"content": "## Fetch posts & insights\n\nCalls the Threads API to retrieve up to 100 posts (id, text, timestamp, permalink), splits them into individual items, then fetches engagement metrics for each post."
},
"typeVersion": 1
},
{
"id": "386a6cb7-046f-4c72-9cec-00b2933918ff",
"name": "Sticky Note - Build and send",
"type": "n8n-nodes-base.stickyNote",
"position": [
5200,
1920
],
"parameters": {
"color": 7,
"width": 622,
"height": 168,
"content": "## Build & send report\n\nMerges post data with insights, builds a styled HTML table, and sends it as an inline email to the address configured in the Email Analytics Report node."
},
"typeVersion": 1
},
{
"id": "c02321e2-cddb-4aeb-8e55-837b45126f7e",
"name": "Sticky Note - More Templates",
"type": "n8n-nodes-base.stickyNote",
"position": [
5824,
1440
],
"parameters": {
"color": 7,
"width": 412,
"height": 262,
"content": "## More Threads automation\n\nWant to auto-publish Threads posts from a Notion CMS on a schedule?\n\nCheck out the **Content Automation Bundle** \u2014 includes Threads Publisher, LinkedIn Publisher, Facebook Publisher, AI Content Rewriter and more.\n\n\ud83d\udc49 [jasonchuang0818.gumroad.com/l/n8n-content-automation-bundle](https://jasonchuang0818.gumroad.com/l/n8n-content-automation-bundle)"
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"availableInMCP": false,
"executionOrder": "v1"
},
"versionId": "48cf3b59-632f-4ba1-b7be-e55713c423e9",
"connections": {
"Set Token": {
"main": [
[
{
"node": "Fetch Threads Posts",
"type": "main",
"index": 0
}
]
]
},
"Split Out": {
"main": [
[
{
"node": "Fetch Post Insights",
"type": "main",
"index": 0
}
]
]
},
"Manual Trigger": {
"main": [
[
{
"node": "Get Threads Token",
"type": "main",
"index": 0
}
]
]
},
"Build HTML Report": {
"main": [
[
{
"node": "Email Analytics Report",
"type": "main",
"index": 0
}
]
]
},
"Get Threads Token": {
"main": [
[
{
"node": "Set Token",
"type": "main",
"index": 0
}
]
]
},
"Fetch Post Insights": {
"main": [
[
{
"node": "Merge Posts and Insights",
"type": "main",
"index": 0
}
]
]
},
"Fetch Threads Posts": {
"main": [
[
{
"node": "Split Out",
"type": "main",
"index": 0
}
]
]
},
"Merge Posts and Insights": {
"main": [
[
{
"node": "Build HTML Report",
"type": "main",
"index": 0
}
]
]
},
"Schedule Trigger (Every 2 Days)": {
"main": [
[
{
"node": "Get Threads Token",
"type": "main",
"index": 0
}
]
]
}
}
}
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Fetch up to 100 of your latest Threads posts along with their insights (views, likes, replies, reposts, quotes), build a formatted HTML summary table, and send it to your inbox automatically.
Source: https://n8n.io/workflows/13509/ — 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.
This workflow automates the collection of public procurement data from TenderNed (the official Dutch tender platform). It: Fetches the latest tender publications from the TenderNed API Retrieves detai
This workflow automates the management of Zoom OAuth tokens and the creation of new Zoom users through the Zoom API.
Transform your event registration process with this comprehensive automation that eliminates manual certificate creation and ensures only verified attendees receive credentials.
How It Works This sub-workflow uploads files to Dropbox and returns a direct download link:
Transform your order fulfillment process with complete invoice automation. This workflow automatically generates professional PDF invoices from Jotform orders and delivers them to customers while keep