n8n-nodes-apivault-website-traffic-database 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,60 +1,11 @@
1
- # n8n-nodes-apivault-website-traffic-database
1
+ # Websites by Traffic Volume 2026 — n8n node
2
2
 
3
- An [n8n](https://n8n.io) community node for **Websites by Traffic Volume 2026**, powered by the [`apivault_labs/website-traffic-database` Apify Actor](https://apify.com/apivault_labs/website-traffic-database).
3
+ Calls the hosted [Apify Actor](https://apify.com/apivault_labs/website-traffic-database). Requires your own Apify credential; Apify run charges apply.
4
4
 
5
- The reverse of SimilarWeb: instead of one domain's stats, get every website in a traffic range. Filter 40M+ sites by monthly visits, category, country, rank, traffic channel, engagement, growth, or the keywords they rank for. A cheaper SimilarWeb/Semrush alternative for bulk site discovery. $5/1K.
5
+ Install `n8n-nodes-apivault-website-traffic-database` through Settings → Community nodes → Install in self-hosted n8n.
6
6
 
7
- The node is a thin connector: collection, analysis, retries and billing run in the hosted Actor. It contains no private scraper implementation or embedded credentials.
7
+ ## Upgrade compatibility
8
8
 
9
- ## Installation
9
+ Existing node names, credential type and input parameter names are retained. Existing version 1 workflows keep their previous charge behavior unless an event cap is selected. New version 2 nodes default to a $0.05 event charge cap and 1,000 downloaded rows; platform usage can be charged separately. Select Read Existing Run and provide the Run ID to resume without starting a paid duplicate. Full output includes run metadata, SUMMARY and ERRORS when available. Dataset rows retain their original fields and paired input item. Actor build is latest.
10
10
 
11
- 1. Open **Settings → Community Nodes** in your n8n instance.
12
- 2. Select **Install**.
13
- 3. Enter `n8n-nodes-apivault-website-traffic-database` and confirm.
14
-
15
- ## Credentials
16
-
17
- Create an **Apify API** credential in n8n and paste your personal token from [Apify Console → Integrations](https://console.apify.com/account/integrations). The token is sent to Apify as a bearer credential and is never bundled with this package.
18
-
19
- ## Usage
20
-
21
- Add **Websites by Traffic Volume 2026** to a workflow, fill the public Actor inputs below, and execute the node. Every Dataset result becomes one n8n item, so it can flow into Sheets, databases, CRMs, alerts or your own code. The node respects n8n's **Continue On Fail** behavior.
22
-
23
- | Input | Type | Description |
24
- |---|---|---|
25
- | `workflow` | `string` | Choose one way to find websites. Auto-detect preserves existing integrations and uses this priority: domain lookup, similar sites, global rank, then traffic filters. |
26
- | `minVisits` | `integer` | Return sites with at least this many visits in the most recent month (e.g. 1000 = 1K+ visits/month). Used only in traffic mode. If you use ‘Top sites by Global Rank’ in section ②, |
27
- | `maxVisits` | `integer` | Return sites with at most this many visits in the most recent month. Leave empty for no upper limit. |
28
- | `topGlobalRank` | `integer` | Enter 1000 to get the world's top 1000 sites — global ranks 1 through 1000, ordered from rank 1 (rank 1 = the most visited site in the world). Just fill this one field: the monthly |
29
- | `category` | `string` | Keep only sites whose category contains this text (e.g. 'finance', 'health', 'games'). Category is known for a subset of sites, so this narrows results. Leave empty for all. |
30
- | `country` | `string` | Keep only sites whose #1 visitor country matches this code (e.g. 'US', 'DE', 'GB'). Leave empty for all countries. |
31
- | `minSearchShare` | `integer` | Keep only sites where organic search is at least this % of traffic (e.g. 50 = SEO-dependent sites). Leave empty to ignore. |
32
- | `hasAiTraffic` | `boolean` | Keep only sites that get visits from AI assistants (ChatGPT, Perplexity, etc.). |
33
- | `onlyGrowing` | `boolean` | Keep only sites whose most recent month is higher than their earliest tracked month (positive trend). |
34
- | `minGrowthPercent` | `integer` | Keep only sites growing at least this % from their earliest to most recent tracked month (e.g. 20 = fast-growing / trending sites). Leave empty to ignore. |
35
- | `keyword` | `string` | Keep only sites that have this keyword among their top search keywords (reverse keyword lookup, e.g. 'crypto wallet'). Best combined with other filters. Leave empty to ignore. |
36
- | `trafficSource` | `string` | Which traffic channel to filter by (used together with 'Minimum channel share' below). E.g. pick 'social' + 40 to find sites where social is 40%+ of traffic. |
37
- | `minSourceShare` | `integer` | Keep only sites where the chosen 'Traffic channel' is at least this % of total traffic (e.g. 40). Leave empty to ignore this filter. |
38
- | `maxBounce` | `integer` | Keep only sites with a bounce rate at or below this % (lower = more engaged visitors, e.g. 40). Leave empty to ignore. |
39
- | `minPagesPerVisit` | `integer` | Keep only sites where visitors view at least this many pages per visit (engagement, e.g. 3). Leave empty to ignore. |
40
- | `minTimeOnSite` | `integer` | Keep only sites with an average visit duration of at least this many seconds (e.g. 120 = 2 min). Leave empty to ignore. |
41
- | `maxResults` | `integer` | Maximum number of websites to return — this is also your cost control, since you pay per result. Up to 50,000 are returned as an accurate global top-N; larger values (up to 1,000,0 |
42
- | `sortBy` | `string` | Which field to sort results by. |
43
- | `sortOrder` | `string` | Highest first (descending) or lowest first (ascending). |
44
- | `outputPreset` | `string` | Compact returns core traffic and ranking facts. Outreach adds engagement, growth and channel mix. Full keeps monthly history, countries, keywords and every available field. Calls t |
45
- | `domains` | `array` | Enter one or more domains (e.g. 'stripe.com', 'https://www.nike.com/') to get their traffic data directly. Only domains already tracked in the database return a row. When you fill |
46
- | `similarTo` | `string` | Enter ONE domain (e.g. 'stripe.com') to get its competitors: other sites in the same category with comparable monthly traffic. The domain must be in the database and have a known c |
47
-
48
- ## Pricing
49
-
50
- The package is free. Actor runs are billed by Apify using the pricing shown on the [Actor page](https://apify.com/apivault_labs/website-traffic-database); platform usage may also apply.
51
-
52
- ## Resources
53
-
54
- - [Actor and live input schema](https://apify.com/apivault_labs/website-traffic-database)
55
- - [Source repository](https://github.com/apivault-labs/n8n-nodes-apivault-website-traffic-database)
56
- - [n8n community-node documentation](https://docs.n8n.io/integrations/community-nodes/)
57
-
58
- ## License
59
-
60
- MIT. The hosted Actor is a separate paid service governed by Apify terms.
11
+ Runtime dependencies: none. n8n Cloud availability requires separate n8n verification.
@@ -1,6 +1,10 @@
1
1
  import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
2
  export declare class ApifyApi implements ICredentialType {
3
3
  name: string;
4
+ icon: {
5
+ readonly light: "file:apivault.svg";
6
+ readonly dark: "file:apivault.svg";
7
+ };
4
8
  displayName: string;
5
9
  documentationUrl: string;
6
10
  properties: INodeProperties[];
@@ -2,27 +2,25 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ApifyApi = void 0;
4
4
  class ApifyApi {
5
- constructor() {
6
- this.name = 'apifyApi';
7
- this.displayName = 'Apify API';
8
- this.documentationUrl = 'https://docs.apify.com/platform/integrations/api';
9
- this.properties = [
10
- {
11
- displayName: 'API Token',
12
- name: 'apiToken',
13
- type: 'string',
14
- typeOptions: { password: true },
15
- default: '',
16
- required: true,
17
- description: 'Your personal Apify API token (Apify Console -> Settings -> Integrations).',
18
- },
19
- ];
20
- this.authenticate = {
21
- type: 'generic',
22
- properties: { headers: { Authorization: '=Bearer {{$credentials.apiToken}}' } },
23
- };
24
- this.test = { request: { baseURL: 'https://api.apify.com/v2', url: '/users/me' } };
25
- }
5
+ name = 'apifyApi';
6
+ icon = { light: 'file:apivault.svg', dark: 'file:apivault.svg' };
7
+ displayName = 'Apify API';
8
+ documentationUrl = 'https://docs.apify.com/platform/integrations/api';
9
+ properties = [
10
+ {
11
+ displayName: 'API Token',
12
+ name: 'apiToken',
13
+ type: 'string',
14
+ typeOptions: { password: true },
15
+ default: '',
16
+ required: true,
17
+ description: 'Your personal Apify API token (Apify Console -> Settings -> Integrations).',
18
+ },
19
+ ];
20
+ authenticate = {
21
+ type: 'generic',
22
+ properties: { headers: { Authorization: '=Bearer {{$credentials.apiToken}}' } },
23
+ };
24
+ test = { request: { baseURL: 'https://api.apify.com/v2', url: '/users/me' } };
26
25
  }
27
26
  exports.ApifyApi = ApifyApi;
28
- //# sourceMappingURL=ApifyApi.credentials.js.map
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="#4f46e5"/><path d="M16 48 28 16h8l12 32H38l-2-7H26l-2 7zm12-15h6l-3-10z" fill="white"/></svg>
@@ -2,26 +2,47 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WebsiteTrafficDatabase = void 0;
4
4
  const n8n_workflow_1 = require("n8n-workflow");
5
- const ACTOR_ID = 'apivault_labs~website-traffic-database';
5
+ const actorTransport_1 = require("../helpers/actorTransport");
6
+ const ACTOR_ID = '2ahDVr2hK6unhydIJ';
6
7
  class WebsiteTrafficDatabase {
7
- constructor() {
8
- this.description = {
9
- displayName: 'Websites by Traffic Volume 2026',
10
- name: 'websiteTrafficDatabase',
11
- icon: 'file:websitetrafficdatabase.svg',
12
- group: ['transform'],
13
- version: 1,
14
- description: 'The reverse of SimilarWeb: instead of one domain\'s stats, get every website in a traffic range. Filter 40M+ sites by monthly visits, category, country, rank, traffic channel, engagement, growth, or the keywords they rank for. A cheaper SimilarWeb/Semrush alternative for bulk site discovery. $5/1K.',
15
- defaults: { name: 'Websites by Traffic Volume 2026' },
16
- inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
17
- outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
18
- usableAsTool: true,
19
- credentials: [{ name: 'apifyApi', required: true }],
20
- properties: [
8
+ description = {
9
+ "displayName": "Websites by Traffic Volume 2026",
10
+ "name": "websiteTrafficDatabase",
11
+ "icon": {
12
+ "light": "file:websitetrafficdatabase.svg",
13
+ "dark": "file:websitetrafficdatabase.svg"
14
+ },
15
+ "subtitle": "={{$parameter[\"apivaultMode\"]}}",
16
+ "group": [
17
+ "transform"
18
+ ],
19
+ "version": [
20
+ 1,
21
+ 2
22
+ ],
23
+ "description": "Run Website Traffic Database through the hosted Apify Actor and read its results",
24
+ "defaults": {
25
+ "name": "Websites by Traffic Volume 2026"
26
+ },
27
+ "inputs": [
28
+ n8n_workflow_1.NodeConnectionTypes.Main
29
+ ],
30
+ "outputs": [
31
+ n8n_workflow_1.NodeConnectionTypes.Main
32
+ ],
33
+ "usableAsTool": true,
34
+ "credentials": [
35
+ {
36
+ "name": "apifyApi",
37
+ "required": true
38
+ }
39
+ ],
40
+ "defaultVersion": 2,
41
+ properties: [...actorTransport_1.executionProperties, ...[
21
42
  {
22
43
  "displayName": "Workflow",
23
44
  "name": "workflow",
24
- "description": "Choose one way to find websites. Auto-detect preserves existing integrations and uses this priority: domain lookup, similar sites, global rank, then traffic filters.",
45
+ "description": "Actor input: Workflow",
25
46
  "type": "options",
26
47
  "options": [
27
48
  {
@@ -45,92 +66,169 @@ class WebsiteTrafficDatabase {
45
66
  "value": "similarSites"
46
67
  }
47
68
  ],
48
- "default": "auto"
69
+ "default": "auto",
70
+ "displayOptions": {
71
+ "show": {
72
+ "apivaultMode": [
73
+ "run"
74
+ ]
75
+ }
76
+ }
49
77
  },
50
78
  {
51
79
  "displayName": "Minimum monthly visits",
52
80
  "name": "minVisits",
53
- "description": "Return sites with at least this many visits in the most recent month (e.g. 1000 = 1K+ visits/month). Used only in traffic mode. If you use ‘Top sites by Global Rank’ in section ②, this field is ignored automatically — you don't need to clear it.",
81
+ "description": "Actor input: Minimum monthly visits",
54
82
  "type": "number",
55
83
  "default": 1000,
56
84
  "typeOptions": {
57
85
  "minValue": 0
86
+ },
87
+ "displayOptions": {
88
+ "show": {
89
+ "apivaultMode": [
90
+ "run"
91
+ ]
92
+ }
58
93
  }
59
94
  },
60
95
  {
61
96
  "displayName": "Maximum monthly visits",
62
97
  "name": "maxVisits",
63
- "description": "Return sites with at most this many visits in the most recent month. Leave empty for no upper limit.",
98
+ "description": "Actor input: Maximum monthly visits",
64
99
  "type": "number",
65
- "default": 0
100
+ "default": 0,
101
+ "displayOptions": {
102
+ "show": {
103
+ "apivaultMode": [
104
+ "run"
105
+ ]
106
+ }
107
+ }
66
108
  },
67
109
  {
68
110
  "displayName": "Top sites worldwide (enter N, e.g. 100)",
69
111
  "name": "topGlobalRank",
70
- "description": "Enter 1000 to get the world's top 1000 sites — global ranks 1 through 1000, ordered from rank 1 (rank 1 = the most visited site in the world). Just fill this one field: the monthly-visits fields in ① and every optional filter in ③ (category, country, etc.) are ignored automatically, so you don't have to clear anything. Leave this empty to select by traffic instead.",
112
+ "description": "Actor input: Top sites worldwide (enter N, e.g. 100)",
71
113
  "type": "number",
72
114
  "default": 0,
73
115
  "typeOptions": {
74
116
  "minValue": 1
117
+ },
118
+ "displayOptions": {
119
+ "show": {
120
+ "apivaultMode": [
121
+ "run"
122
+ ]
123
+ }
75
124
  }
76
125
  },
77
126
  {
78
127
  "displayName": "Category contains",
79
128
  "name": "category",
80
- "description": "Keep only sites whose category contains this text (e.g. 'finance', 'health', 'games'). Category is known for a subset of sites, so this narrows results. Leave empty for all.",
129
+ "description": "Actor input: Category contains",
81
130
  "type": "string",
82
- "default": ""
131
+ "default": "",
132
+ "displayOptions": {
133
+ "show": {
134
+ "apivaultMode": [
135
+ "run"
136
+ ]
137
+ }
138
+ }
83
139
  },
84
140
  {
85
141
  "displayName": "Top country (code)",
86
142
  "name": "country",
87
- "description": "Keep only sites whose #1 visitor country matches this code (e.g. 'US', 'DE', 'GB'). Leave empty for all countries.",
143
+ "description": "Actor input: Top country (code)",
88
144
  "type": "string",
89
- "default": ""
145
+ "default": "",
146
+ "displayOptions": {
147
+ "show": {
148
+ "apivaultMode": [
149
+ "run"
150
+ ]
151
+ }
152
+ }
90
153
  },
91
154
  {
92
155
  "displayName": "Minimum search-traffic share (%)",
93
156
  "name": "minSearchShare",
94
- "description": "Keep only sites where organic search is at least this % of traffic (e.g. 50 = SEO-dependent sites). Leave empty to ignore.",
157
+ "description": "Actor input: Minimum search-traffic share (%)",
95
158
  "type": "number",
96
159
  "default": 0,
97
160
  "typeOptions": {
98
161
  "minValue": 0,
99
162
  "maxValue": 100
163
+ },
164
+ "displayOptions": {
165
+ "show": {
166
+ "apivaultMode": [
167
+ "run"
168
+ ]
169
+ }
100
170
  }
101
171
  },
102
172
  {
103
173
  "displayName": "Only sites with AI/LLM traffic",
104
174
  "name": "hasAiTraffic",
105
- "description": "Keep only sites that get visits from AI assistants (ChatGPT, Perplexity, etc.).",
175
+ "description": "Actor input: Only sites with AI/LLM traffic",
106
176
  "type": "boolean",
107
- "default": false
177
+ "default": false,
178
+ "displayOptions": {
179
+ "show": {
180
+ "apivaultMode": [
181
+ "run"
182
+ ]
183
+ }
184
+ }
108
185
  },
109
186
  {
110
187
  "displayName": "Only growing sites",
111
188
  "name": "onlyGrowing",
112
- "description": "Keep only sites whose most recent month is higher than their earliest tracked month (positive trend).",
189
+ "description": "Actor input: Only growing sites",
113
190
  "type": "boolean",
114
- "default": false
191
+ "default": false,
192
+ "displayOptions": {
193
+ "show": {
194
+ "apivaultMode": [
195
+ "run"
196
+ ]
197
+ }
198
+ }
115
199
  },
116
200
  {
117
201
  "displayName": "Minimum growth (%)",
118
202
  "name": "minGrowthPercent",
119
- "description": "Keep only sites growing at least this % from their earliest to most recent tracked month (e.g. 20 = fast-growing / trending sites). Leave empty to ignore.",
203
+ "description": "Actor input: Minimum growth (%)",
120
204
  "type": "number",
121
- "default": 0
205
+ "default": 0,
206
+ "displayOptions": {
207
+ "show": {
208
+ "apivaultMode": [
209
+ "run"
210
+ ]
211
+ }
212
+ }
122
213
  },
123
214
  {
124
215
  "displayName": "Ranks for keyword",
125
216
  "name": "keyword",
126
- "description": "Keep only sites that have this keyword among their top search keywords (reverse keyword lookup, e.g. 'crypto wallet'). Best combined with other filters. Leave empty to ignore.",
217
+ "description": "Actor input: Ranks for keyword",
127
218
  "type": "string",
128
- "default": ""
219
+ "default": "",
220
+ "displayOptions": {
221
+ "show": {
222
+ "apivaultMode": [
223
+ "run"
224
+ ]
225
+ }
226
+ }
129
227
  },
130
228
  {
131
229
  "displayName": "Traffic channel",
132
230
  "name": "trafficSource",
133
- "description": "Which traffic channel to filter by (used together with 'Minimum channel share' below). E.g. pick 'social' + 40 to find sites where social is 40%+ of traffic.",
231
+ "description": "Actor input: Traffic channel",
134
232
  "type": "options",
135
233
  "options": [
136
234
  {
@@ -162,65 +260,107 @@ class WebsiteTrafficDatabase {
162
260
  "value": "ai"
163
261
  }
164
262
  ],
165
- "default": "search"
263
+ "default": "search",
264
+ "displayOptions": {
265
+ "show": {
266
+ "apivaultMode": [
267
+ "run"
268
+ ]
269
+ }
270
+ }
166
271
  },
167
272
  {
168
273
  "displayName": "Minimum channel share (%)",
169
274
  "name": "minSourceShare",
170
- "description": "Keep only sites where the chosen 'Traffic channel' is at least this % of total traffic (e.g. 40). Leave empty to ignore this filter.",
275
+ "description": "Actor input: Minimum channel share (%)",
171
276
  "type": "number",
172
277
  "default": 0,
173
278
  "typeOptions": {
174
279
  "minValue": 0,
175
280
  "maxValue": 100
281
+ },
282
+ "displayOptions": {
283
+ "show": {
284
+ "apivaultMode": [
285
+ "run"
286
+ ]
287
+ }
176
288
  }
177
289
  },
178
290
  {
179
291
  "displayName": "Maximum bounce rate (%)",
180
292
  "name": "maxBounce",
181
- "description": "Keep only sites with a bounce rate at or below this % (lower = more engaged visitors, e.g. 40). Leave empty to ignore.",
293
+ "description": "Actor input: Maximum bounce rate (%)",
182
294
  "type": "number",
183
295
  "default": 0,
184
296
  "typeOptions": {
185
297
  "minValue": 0,
186
298
  "maxValue": 100
299
+ },
300
+ "displayOptions": {
301
+ "show": {
302
+ "apivaultMode": [
303
+ "run"
304
+ ]
305
+ }
187
306
  }
188
307
  },
189
308
  {
190
309
  "displayName": "Minimum pages per visit",
191
310
  "name": "minPagesPerVisit",
192
- "description": "Keep only sites where visitors view at least this many pages per visit (engagement, e.g. 3). Leave empty to ignore.",
311
+ "description": "Actor input: Minimum pages per visit",
193
312
  "type": "number",
194
313
  "default": 0,
195
314
  "typeOptions": {
196
315
  "minValue": 0
316
+ },
317
+ "displayOptions": {
318
+ "show": {
319
+ "apivaultMode": [
320
+ "run"
321
+ ]
322
+ }
197
323
  }
198
324
  },
199
325
  {
200
326
  "displayName": "Minimum time on site (seconds)",
201
327
  "name": "minTimeOnSite",
202
- "description": "Keep only sites with an average visit duration of at least this many seconds (e.g. 120 = 2 min). Leave empty to ignore.",
328
+ "description": "Actor input: Minimum time on site (seconds)",
203
329
  "type": "number",
204
330
  "default": 0,
205
331
  "typeOptions": {
206
332
  "minValue": 0
333
+ },
334
+ "displayOptions": {
335
+ "show": {
336
+ "apivaultMode": [
337
+ "run"
338
+ ]
339
+ }
207
340
  }
208
341
  },
209
342
  {
210
343
  "displayName": "Max results",
211
344
  "name": "maxResults",
212
- "description": "Maximum number of websites to return — this is also your cost control, since you pay per result. Up to 50,000 are returned as an accurate global top-N; larger values (up to 1,000,000) switch to a streaming bulk export for full-list pulls.",
345
+ "description": "Actor input: Max results",
213
346
  "type": "number",
214
347
  "default": 25,
215
348
  "typeOptions": {
216
349
  "minValue": 1,
217
350
  "maxValue": 1000000
351
+ },
352
+ "displayOptions": {
353
+ "show": {
354
+ "apivaultMode": [
355
+ "run"
356
+ ]
357
+ }
218
358
  }
219
359
  },
220
360
  {
221
361
  "displayName": "Sort by",
222
362
  "name": "sortBy",
223
- "description": "Which field to sort results by.",
363
+ "description": "Actor input: Sort by",
224
364
  "type": "options",
225
365
  "options": [
226
366
  {
@@ -256,12 +396,19 @@ class WebsiteTrafficDatabase {
256
396
  "value": "ai_visits"
257
397
  }
258
398
  ],
259
- "default": "visits"
399
+ "default": "visits",
400
+ "displayOptions": {
401
+ "show": {
402
+ "apivaultMode": [
403
+ "run"
404
+ ]
405
+ }
406
+ }
260
407
  },
261
408
  {
262
409
  "displayName": "Sort order",
263
410
  "name": "sortOrder",
264
- "description": "Highest first (descending) or lowest first (ascending).",
411
+ "description": "Actor input: Sort order",
265
412
  "type": "options",
266
413
  "options": [
267
414
  {
@@ -273,12 +420,19 @@ class WebsiteTrafficDatabase {
273
420
  "value": "asc"
274
421
  }
275
422
  ],
276
- "default": "desc"
423
+ "default": "desc",
424
+ "displayOptions": {
425
+ "show": {
426
+ "apivaultMode": [
427
+ "run"
428
+ ]
429
+ }
430
+ }
277
431
  },
278
432
  {
279
433
  "displayName": "Output detail",
280
434
  "name": "outputPreset",
281
- "description": "Compact returns core traffic and ranking facts. Outreach adds engagement, growth and channel mix. Full keeps monthly history, countries, keywords and every available field. Calls that omit this field retain the legacy full output.",
435
+ "description": "Actor input: Output detail",
282
436
  "type": "options",
283
437
  "options": [
284
438
  {
@@ -294,101 +448,93 @@ class WebsiteTrafficDatabase {
294
448
  "value": "full"
295
449
  }
296
450
  ],
297
- "default": "outreach"
451
+ "default": "outreach",
452
+ "displayOptions": {
453
+ "show": {
454
+ "apivaultMode": [
455
+ "run"
456
+ ]
457
+ }
458
+ }
298
459
  },
299
460
  {
300
461
  "displayName": "Domains to look up",
301
462
  "name": "domains",
302
- "description": "Enter one or more domains (e.g. 'stripe.com', 'https://www.nike.com/') to get their traffic data directly. Only domains already tracked in the database return a row. When you fill this, the filter fields above are ignored. (comma or new-line separated)",
463
+ "description": "Actor input: Domains to look up",
303
464
  "type": "string",
304
- "default": ""
465
+ "default": "",
466
+ "displayOptions": {
467
+ "show": {
468
+ "apivaultMode": [
469
+ "run"
470
+ ]
471
+ }
472
+ }
305
473
  },
306
474
  {
307
475
  "displayName": "Find competitors of this domain",
308
476
  "name": "similarTo",
309
- "description": "Enter ONE domain (e.g. 'stripe.com') to get its competitors: other sites in the same category with comparable monthly traffic. The domain must be in the database and have a known category. When you fill this, everything above is ignored.",
477
+ "description": "Actor input: Find competitors of this domain",
310
478
  "type": "string",
311
- "default": ""
479
+ "default": "",
480
+ "displayOptions": {
481
+ "show": {
482
+ "apivaultMode": [
483
+ "run"
484
+ ]
485
+ }
486
+ }
312
487
  }
313
- ],
314
- };
315
- }
488
+ ]]
489
+ };
316
490
  async execute() {
317
- var _b;
318
- const items = this.getInputData();
319
- const returnData = [];
320
- for (let i = 0; i < items.length; i++) {
491
+ const output = [];
492
+ for (let i = 0; i < this.getInputData().length; i++) {
321
493
  try {
322
- const body = {};
323
- body["workflow"] = this.getNodeParameter("workflow", i);
324
- body["minVisits"] = this.getNodeParameter("minVisits", i);
325
- body["maxVisits"] = this.getNodeParameter("maxVisits", i);
326
- body["topGlobalRank"] = this.getNodeParameter("topGlobalRank", i);
327
- body["category"] = this.getNodeParameter("category", i);
328
- body["country"] = this.getNodeParameter("country", i);
329
- body["minSearchShare"] = this.getNodeParameter("minSearchShare", i);
330
- body["hasAiTraffic"] = this.getNodeParameter("hasAiTraffic", i);
331
- body["onlyGrowing"] = this.getNodeParameter("onlyGrowing", i);
332
- body["minGrowthPercent"] = this.getNodeParameter("minGrowthPercent", i);
333
- body["keyword"] = this.getNodeParameter("keyword", i);
334
- body["trafficSource"] = this.getNodeParameter("trafficSource", i);
335
- body["minSourceShare"] = this.getNodeParameter("minSourceShare", i);
336
- body["maxBounce"] = this.getNodeParameter("maxBounce", i);
337
- body["minPagesPerVisit"] = this.getNodeParameter("minPagesPerVisit", i);
338
- body["minTimeOnSite"] = this.getNodeParameter("minTimeOnSite", i);
339
- body["maxResults"] = this.getNodeParameter("maxResults", i);
340
- body["sortBy"] = this.getNodeParameter("sortBy", i);
341
- body["sortOrder"] = this.getNodeParameter("sortOrder", i);
342
- body["outputPreset"] = this.getNodeParameter("outputPreset", i);
343
- {
344
- const _v = this.getNodeParameter("domains", i, '');
345
- const _a = _v.split(/[,\n]/).map(s => s.trim()).filter(s => s.length > 0);
346
- if (_a.length)
347
- body["domains"] = _a;
348
- }
349
- body["similarTo"] = this.getNodeParameter("similarTo", i);
350
- const options = {
351
- method: 'POST',
352
- url: `https://api.apify.com/v2/acts/${ACTOR_ID}/runs`,
353
- body,
354
- json: true,
355
- };
356
- const started = await this.helpers.requestWithAuthentication.call(this, 'apifyApi', options);
357
- const runId = (_b = started === null || started === void 0 ? void 0 : started.data) === null || _b === void 0 ? void 0 : _b.id;
358
- if (!runId)
359
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Apify did not return a run ID', { itemIndex: i });
360
- let run = started.data;
361
- const deadline = Date.now() + 60 * 60 * 1000;
362
- while (!['SUCCEEDED', 'FAILED', 'ABORTED', 'TIMED-OUT'].includes(run.status)) {
363
- if (Date.now() >= deadline)
364
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Waiting timed out; check the existing run in Apify before retrying', { itemIndex: i });
365
- const polled = await this.helpers.requestWithAuthentication.call(this, 'apifyApi', { method: 'GET', url: `https://api.apify.com/v2/actor-runs/${runId}?waitForFinish=20`, json: true });
366
- run = polled.data;
367
- }
368
- if (run.status !== 'SUCCEEDED')
369
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Apify run ended with status ' + run.status, { itemIndex: i });
370
- let offset = 0;
371
- while (true) {
372
- const page = await this.helpers.requestWithAuthentication.call(this, 'apifyApi', { method: 'GET', url: `https://api.apify.com/v2/datasets/${run.defaultDatasetId}/items?clean=1&limit=1000&offset=${offset}`, json: true });
373
- if (!Array.isArray(page))
374
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Unexpected Dataset response', { itemIndex: i });
375
- for (const result of page)
376
- returnData.push({ json: result, pairedItem: { item: i } });
377
- offset += page.length;
378
- if (page.length < 1000)
379
- break;
380
- }
494
+ const body = this.getNodeParameter('apivaultMode', i, 'run') === 'read' ? {} : (() => {
495
+ const body = {};
496
+ body["workflow"] = this.getNodeParameter("workflow", i);
497
+ body["minVisits"] = this.getNodeParameter("minVisits", i);
498
+ body["maxVisits"] = this.getNodeParameter("maxVisits", i);
499
+ body["topGlobalRank"] = this.getNodeParameter("topGlobalRank", i);
500
+ body["category"] = this.getNodeParameter("category", i);
501
+ body["country"] = this.getNodeParameter("country", i);
502
+ body["minSearchShare"] = this.getNodeParameter("minSearchShare", i);
503
+ body["hasAiTraffic"] = this.getNodeParameter("hasAiTraffic", i);
504
+ body["onlyGrowing"] = this.getNodeParameter("onlyGrowing", i);
505
+ body["minGrowthPercent"] = this.getNodeParameter("minGrowthPercent", i);
506
+ body["keyword"] = this.getNodeParameter("keyword", i);
507
+ body["trafficSource"] = this.getNodeParameter("trafficSource", i);
508
+ body["minSourceShare"] = this.getNodeParameter("minSourceShare", i);
509
+ body["maxBounce"] = this.getNodeParameter("maxBounce", i);
510
+ body["minPagesPerVisit"] = this.getNodeParameter("minPagesPerVisit", i);
511
+ body["minTimeOnSite"] = this.getNodeParameter("minTimeOnSite", i);
512
+ body["maxResults"] = this.getNodeParameter("maxResults", i);
513
+ body["sortBy"] = this.getNodeParameter("sortBy", i);
514
+ body["sortOrder"] = this.getNodeParameter("sortOrder", i);
515
+ body["outputPreset"] = this.getNodeParameter("outputPreset", i);
516
+ {
517
+ const _v = this.getNodeParameter("domains", i, '');
518
+ const _a = _v.split(/[,\n]/).map(s => s.trim()).filter(s => s.length > 0);
519
+ if (_a.length)
520
+ body["domains"] = _a;
521
+ }
522
+ body["similarTo"] = this.getNodeParameter("similarTo", i);
523
+ return body;
524
+ })();
525
+ const results = await (0, actorTransport_1.runActor)(this, i, ACTOR_ID, body);
526
+ for (const result of results)
527
+ output.push({ json: result, pairedItem: { item: i } });
381
528
  }
382
529
  catch (error) {
383
- if (this.continueOnFail()) {
384
- returnData.push({ json: { error: error.message }, pairedItem: { item: i } });
385
- continue;
386
- }
387
- throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
530
+ const message = error instanceof actorTransport_1.SafeActorError ? error.message : 'Could not finish the Actor request. Check inputs and credentials; use Read Existing Run if a run was already started';
531
+ if (this.continueOnFail())
532
+ output.push({ json: { error: message }, pairedItem: { item: i } });
533
+ else
534
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), message, { itemIndex: i });
388
535
  }
389
536
  }
390
- return [returnData];
537
+ return [output];
391
538
  }
392
539
  }
393
540
  exports.WebsiteTrafficDatabase = WebsiteTrafficDatabase;
394
- //# sourceMappingURL=WebsiteTrafficDatabase.node.js.map
@@ -0,0 +1,9 @@
1
+ import type { IExecuteFunctions, INodeProperties, IDataObject } from 'n8n-workflow';
2
+ import { NodeOperationError } from 'n8n-workflow';
3
+ export declare class SafeActorError extends NodeOperationError {
4
+ readonly runId: string;
5
+ constructor(context: IExecuteFunctions, runId: string, item: number);
6
+ }
7
+ export declare const executionProperties: INodeProperties[];
8
+ export declare function runActor(context: IExecuteFunctions, item: number, actorId: string, body: Record<string, unknown>): Promise<IDataObject[]>;
9
+ export declare function legacyRequest(context: IExecuteFunctions, item: number, raw: unknown): Promise<any>;
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executionProperties = exports.SafeActorError = void 0;
4
+ exports.runActor = runActor;
5
+ exports.legacyRequest = legacyRequest;
6
+ const n8n_workflow_1 = require("n8n-workflow");
7
+ const API = 'https://api.apify.com/v2';
8
+ const id = (value) => {
9
+ if (typeof value !== 'string' || !/^[A-Za-z0-9_-]{1,100}$/.test(value))
10
+ throw new Error('Invalid Apify identifier');
11
+ return value;
12
+ };
13
+ class SafeActorError extends n8n_workflow_1.NodeOperationError {
14
+ runId;
15
+ constructor(context, runId, item) {
16
+ super(context.getNode(), runId ? `Could not finish reading run ${runId}; select Read Existing Run to resume` : 'Could not start or read the Actor. Check inputs and credentials before retrying', { itemIndex: item });
17
+ this.runId = runId;
18
+ }
19
+ }
20
+ exports.SafeActorError = SafeActorError;
21
+ exports.executionProperties = [
22
+ { displayName: 'Run Mode', name: 'apivaultMode', type: 'options', noDataExpression: true,
23
+ options: [{ name: 'Run Actor', value: 'run', action: 'Run the actor' }, { name: 'Read Existing Run', value: 'read', action: 'Read an existing run' }], default: 'run' },
24
+ { displayName: 'Execution Options', name: 'apivaultExecution', type: 'collection', placeholder: 'Add Option', default: {}, options: [
25
+ { displayName: 'Run ID', name: 'runId', type: 'string', default: '', description: 'Existing Apify run to resume without starting a paid duplicate' },
26
+ { displayName: 'Maximum Event Charge (USD)', name: 'maxTotalChargeUsd', type: 'number', default: 0.05,
27
+ typeOptions: { minValue: 0.001, maxValue: 1000, numberPrecision: 3 }, description: 'Event charge cap. Platform usage may be billed separately.' },
28
+ { displayName: 'Maximum Rows to Read', name: 'maxRows', type: 'number', default: 1000,
29
+ typeOptions: { minValue: 1, maxValue: 100000, numberPrecision: 0 }, description: 'Download cap only. Actor input limits control paid collection.' },
30
+ { displayName: 'Output', name: 'output', type: 'options', default: 'rows',
31
+ options: [{ name: 'Dataset Rows', value: 'rows' }, { name: 'Run, Rows and Diagnostics', value: 'full' }], description: 'Include SUMMARY and ERRORS to inspect partial or empty results' },
32
+ ] },
33
+ ];
34
+ async function runActor(context, item, actorId, body) {
35
+ let runId = '';
36
+ try {
37
+ const mode = context.getNodeParameter('apivaultMode', item, 'run');
38
+ const options = context.getNodeParameter('apivaultExecution', item, {});
39
+ const legacy = context.getNode().typeVersion < 2;
40
+ const maxRows = options.maxRows ?? (legacy ? 100000 : 1000);
41
+ if (!Number.isInteger(maxRows) || maxRows < 1 || maxRows > 100000)
42
+ throw new Error('Invalid row limit');
43
+ const request = async (requestOptions) => context.helpers.httpRequestWithAuthentication.call(context, 'apifyApi', requestOptions);
44
+ if (mode === 'run') {
45
+ const cap = options.maxTotalChargeUsd ?? (legacy ? undefined : 0.05);
46
+ if (cap !== undefined && (!Number.isFinite(cap) || cap <= 0 || cap > 1000))
47
+ throw new Error('Invalid event charge cap');
48
+ // Legacy workflows retain their existing charge behavior unless a cap is selected.
49
+ const qs = { build: 'latest' };
50
+ if (cap !== undefined)
51
+ qs.maxTotalChargeUsd = cap;
52
+ // Never retry POST: a duplicate could create a second paid Actor run.
53
+ const started = await request({ method: 'POST', url: `${API}/acts/${id(actorId)}/runs`, body: body, qs, json: true });
54
+ runId = id(started?.data?.id);
55
+ }
56
+ else if (mode === 'read')
57
+ runId = id(options.runId);
58
+ else
59
+ throw new Error('Unsupported run mode');
60
+ const deadline = Date.now() + 10 * 60 * 1000;
61
+ let run;
62
+ while (true) {
63
+ if (Date.now() >= deadline)
64
+ throw new Error('Waiting timed out');
65
+ const response = await request({ method: 'GET', url: `${API}/actor-runs/${runId}`, qs: { waitForFinish: 20 }, json: true });
66
+ run = response.data;
67
+ if (run.status === 'SUCCEEDED')
68
+ break;
69
+ if (['FAILED', 'TIMED-OUT', 'ABORTED'].includes(run.status))
70
+ throw new Error('Run did not succeed');
71
+ }
72
+ const datasetId = id(run.defaultDatasetId);
73
+ const dataset = await request({ method: 'GET', url: `${API}/datasets/${datasetId}`, json: true });
74
+ const rows = [];
75
+ while (rows.length < maxRows) {
76
+ const limit = Math.min(1000, maxRows - rows.length);
77
+ const page = await request({ method: 'GET', url: `${API}/datasets/${datasetId}/items`, qs: { clean: true, offset: rows.length, limit }, json: true });
78
+ if (!Array.isArray(page) || page.length > limit)
79
+ throw new Error('Invalid Dataset response');
80
+ rows.push(...page);
81
+ if (page.length < limit)
82
+ break;
83
+ }
84
+ const truncated = (dataset.data?.itemCount ?? rows.length) > rows.length;
85
+ if (options.output === 'full') {
86
+ const storeId = id(run.defaultKeyValueStoreId);
87
+ const records = {};
88
+ for (const key of ['SUMMARY', 'ERRORS']) {
89
+ try {
90
+ records[key] = await request({ method: 'GET', url: `${API}/key-value-stores/${storeId}/records/${key}`, json: true });
91
+ }
92
+ catch (error) {
93
+ const e = error;
94
+ if (e.statusCode === 404 || e.response?.status === 404)
95
+ records[key] = null;
96
+ else
97
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), 'Diagnostics unavailable');
98
+ }
99
+ }
100
+ return [{ runId, datasetId, status: run.status, rows, summary: records.SUMMARY ?? null, errors: records.ERRORS ?? null, truncated }];
101
+ }
102
+ return rows.map(row => ({ ...row, _apifyRunId: runId, _apifyTruncated: truncated }));
103
+ }
104
+ catch {
105
+ const safeError = new SafeActorError(context, runId, item);
106
+ throw safeError;
107
+ }
108
+ }
109
+ // Keep specialized legacy output modes (including asynchronous start and MCP_RESPONSE).
110
+ async function legacyRequest(context, item, raw) {
111
+ const options = raw;
112
+ const controls = context.getNodeParameter('apivaultExecution', item, {});
113
+ const request = async (value) => context.helpers.httpRequestWithAuthentication.call(context, 'apifyApi', value);
114
+ if (options.method === 'POST' && options.url.endsWith('/runs')) {
115
+ const qs = { ...(options.qs ?? {}) };
116
+ const wait = qs.waitForFinish;
117
+ delete qs.waitForFinish;
118
+ const cap = controls.maxTotalChargeUsd ?? (context.getNode().typeVersion < 2 ? undefined : 0.05);
119
+ if (cap !== undefined) {
120
+ if (!Number.isFinite(cap) || cap <= 0 || cap > 1000)
121
+ throw new Error('Invalid event charge cap');
122
+ qs.maxTotalChargeUsd = cap;
123
+ }
124
+ const started = await request({ ...options, qs });
125
+ if (!wait)
126
+ return started;
127
+ const runId = id(started?.data?.id);
128
+ const deadline = Date.now() + 10 * 60 * 1000;
129
+ while (Date.now() < deadline) {
130
+ const result = await request({ method: 'GET', url: `${API}/actor-runs/${runId}`, qs: { waitForFinish: 20 }, json: true });
131
+ if (result.data.status === 'SUCCEEDED')
132
+ return result;
133
+ if (['FAILED', 'TIMED-OUT', 'ABORTED'].includes(result.data.status))
134
+ throw new Error('Run did not succeed');
135
+ }
136
+ throw new Error('Waiting timed out');
137
+ }
138
+ if (options.method === 'GET' && /\/datasets\/[A-Za-z0-9_-]+\/items$/.test(options.url)) {
139
+ const maxRows = controls.maxRows ?? (context.getNode().typeVersion < 2 ? 100000 : 1000);
140
+ if (!Number.isInteger(maxRows) || maxRows < 1 || maxRows > 100000)
141
+ throw new Error('Invalid row limit');
142
+ const rows = [];
143
+ while (rows.length < maxRows) {
144
+ const limit = Math.min(1000, maxRows - rows.length);
145
+ const page = await request({ ...options, qs: { ...(options.qs ?? {}), offset: rows.length, limit } });
146
+ if (!Array.isArray(page) || page.length > limit)
147
+ throw new Error('Invalid Dataset response');
148
+ rows.push(...page);
149
+ if (page.length < limit)
150
+ break;
151
+ }
152
+ return rows;
153
+ }
154
+ return request(options);
155
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "n8n-nodes-apivault-website-traffic-database",
3
- "version": "0.2.0",
4
- "description": "n8n community node for Websites by Traffic Volume 2026 — backed by the Apify Actor apivault_labs/website-traffic-database. No login. Pay-as-you-go via Apify.",
3
+ "version": "0.3.0",
4
+ "description": "Run Website Traffic Database through the hosted Apify Actor and read its results",
5
5
  "license": "MIT",
6
6
  "homepage": "https://apify.com/apivault_labs/website-traffic-database",
7
7
  "keywords": [
@@ -15,20 +15,24 @@
15
15
  "workflow-automation"
16
16
  ],
17
17
  "author": {
18
- "name": "apivault_labs"
18
+ "name": "ApiVault Labs",
19
+ "email": "68965192+apivault-labs@users.noreply.github.com"
19
20
  },
20
21
  "repository": {
21
22
  "type": "git",
22
- "url": "https://github.com/apivault-labs/n8n-nodes-apivault-website-traffic-database.git"
23
+ "url": "git+https://github.com/apivault-labs/apivault-labs.git",
24
+ "directory": "community-nodes/n8n-nodes-apivault-website-traffic-database"
23
25
  },
24
26
  "main": "index.js",
25
27
  "scripts": {
26
- "build": "tsc && copyfiles \"nodes/**/*.svg\" dist",
27
- "dev": "tsc --watch",
28
+ "build": "tsc && node scripts/copy-icons.cjs",
29
+ "lint": "n8n-node lint",
30
+ "test": "node test/contract.cjs",
28
31
  "prepublishOnly": "npm run build"
29
32
  },
30
33
  "files": [
31
- "dist"
34
+ "dist",
35
+ "examples"
32
36
  ],
33
37
  "n8n": {
34
38
  "n8nNodesApiVersion": 1,
@@ -40,12 +44,12 @@
40
44
  ]
41
45
  },
42
46
  "devDependencies": {
43
- "@types/node": "^20.10.0",
44
- "copyfiles": "^2.4.1",
45
- "n8n-workflow": "*",
46
- "typescript": "^5.4.0"
47
+ "@n8n/node-cli": "0.52.2",
48
+ "n8n-workflow": "2.16.0",
49
+ "typescript": "npm:@typescript/typescript6@6.0.2",
50
+ "@types/node": "^22.0.0"
47
51
  },
48
52
  "peerDependencies": {
49
53
  "n8n-workflow": "*"
50
54
  }
51
- }
55
+ }
@@ -1 +0,0 @@
1
- {"version":3,"file":"ApifyApi.credentials.js","sourceRoot":"","sources":["../../credentials/ApifyApi.credentials.ts"],"names":[],"mappings":";;;AAOA,MAAa,QAAQ;IAArB;QACC,SAAI,GAAG,UAAU,CAAC;QAClB,gBAAW,GAAG,WAAW,CAAC;QAC1B,qBAAgB,GAAG,kDAAkD,CAAC;QACtE,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,WAAW;gBACxB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,4EAA4E;aACzF;SACD,CAAC;QACF,iBAAY,GAAyB;YACpC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,mCAAmC,EAAE,EAAE;SAC/E,CAAC;QACF,SAAI,GAA2B,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,0BAA0B,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,CAAC;IACvG,CAAC;CAAA;AApBD,4BAoBC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"WebsiteTrafficDatabase.node.js","sourceRoot":"","sources":["../../../nodes/WebsiteTrafficDatabase/WebsiteTrafficDatabase.node.ts"],"names":[],"mappings":"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"}