@seranking/n8n-nodes-seranking 1.5.9 → 1.5.12
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 +9 -11
- package/dist/credentials/SeRankingApi.credentials.d.ts +2 -1
- package/dist/credentials/SeRankingApi.credentials.js +9 -14
- package/dist/credentials/SeRankingProjectApi.credentials.d.ts +9 -0
- package/dist/credentials/SeRankingProjectApi.credentials.js +39 -0
- package/dist/nodes/SeRanking/SeRanking.node.js +36 -0
- package/dist/nodes/SeRanking/projectApi/descriptions/AiResultTrackerDescription.js +39 -1
- package/dist/nodes/SeRanking/projectApi/operations/AiResultTrackerOperations.js +17 -3
- package/dist/nodes/SeRanking/utils/apiRequest.js +19 -18
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -89,10 +89,9 @@ Open `http://localhost:5678` and add the SE Ranking node to your workflow.
|
|
|
89
89
|
### Step 4: Configure Credentials
|
|
90
90
|
|
|
91
91
|
1. Add SE Ranking node
|
|
92
|
-
2. Click "Create New Credential"
|
|
93
|
-
3.
|
|
94
|
-
4.
|
|
95
|
-
5. Save
|
|
92
|
+
2. Click "Create New Credential" for **SE Ranking API** (Data API) — enter your Data API token
|
|
93
|
+
3. (Optional) Click "Create New Credential" for **SE Ranking Project API** — enter your Project API token
|
|
94
|
+
4. Save
|
|
96
95
|
|
|
97
96
|
### Docker Installation
|
|
98
97
|
|
|
@@ -163,14 +162,13 @@ To use this node, you need:
|
|
|
163
162
|
### Setting up credentials in n8n
|
|
164
163
|
|
|
165
164
|
1. Open any workflow and add the **SE Ranking** node
|
|
166
|
-
2.
|
|
167
|
-
3.
|
|
168
|
-
4.
|
|
169
|
-
5. Click **Save**
|
|
165
|
+
2. In the **SE Ranking API** credential slot, click "Create New Credential" and enter your **Data API token** — required for all Data API resources
|
|
166
|
+
3. In the **SE Ranking Project API** credential slot, click "Create New Credential" and enter your **Project API token** — only needed for Project API resources
|
|
167
|
+
4. Click **Save**
|
|
170
168
|
|
|
171
|
-
The node
|
|
169
|
+
Each credential is tested independently when saved. The node automatically routes requests to the correct API and credential based on the resource you select.
|
|
172
170
|
|
|
173
|
-
> **Note:** Data API and Project API use different tokens. Data API tokens are UUID format; Project API tokens are 40-character hex format. Get both from your [SE Ranking API Dashboard](https://online.seranking.com/admin.api.dashboard.html).
|
|
171
|
+
> **Note:** Data API and Project API use different tokens. Data API tokens are UUID format; Project API tokens are 40-character hex format. Get both from your [SE Ranking API Dashboard](https://online.seranking.com/admin.api.dashboard.html).
|
|
174
172
|
|
|
175
173
|
---
|
|
176
174
|
|
|
@@ -880,7 +878,7 @@ For more details, see [n8n's rate limiting documentation](https://docs.n8n.io/in
|
|
|
880
878
|
**Solution**:
|
|
881
879
|
|
|
882
880
|
1. Verify API token is correct (copy from SE Ranking dashboard)
|
|
883
|
-
2. For Project API resources, ensure the Project API
|
|
881
|
+
2. For Project API resources, ensure the SE Ranking Project API credential is configured
|
|
884
882
|
3. Check token hasn't expired
|
|
885
883
|
4. Regenerate token in SE Ranking dashboard if needed
|
|
886
884
|
5. Test credentials using the "Test" button in n8n
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
1
|
+
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
2
|
export declare class SeRankingApi implements ICredentialType {
|
|
3
3
|
name: string;
|
|
4
4
|
displayName: string;
|
|
5
5
|
documentationUrl: string;
|
|
6
6
|
properties: INodeProperties[];
|
|
7
|
+
authenticate: IAuthenticateGeneric;
|
|
7
8
|
test: ICredentialTestRequest;
|
|
8
9
|
}
|
|
@@ -8,7 +8,7 @@ class SeRankingApi {
|
|
|
8
8
|
this.documentationUrl = 'https://seranking.com/api-google-organic.html';
|
|
9
9
|
this.properties = [
|
|
10
10
|
{
|
|
11
|
-
displayName: 'API Token
|
|
11
|
+
displayName: 'API Token',
|
|
12
12
|
name: 'apiToken',
|
|
13
13
|
type: 'string',
|
|
14
14
|
typeOptions: {
|
|
@@ -16,27 +16,22 @@ class SeRankingApi {
|
|
|
16
16
|
},
|
|
17
17
|
default: '',
|
|
18
18
|
required: true,
|
|
19
|
-
description: '
|
|
19
|
+
description: 'Your SE Ranking Data API token from the API Dashboard',
|
|
20
20
|
},
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
];
|
|
22
|
+
this.authenticate = {
|
|
23
|
+
type: 'generic',
|
|
24
|
+
properties: {
|
|
25
|
+
headers: {
|
|
26
|
+
'Authorization': '=Token {{$credentials.apiToken}}',
|
|
27
27
|
},
|
|
28
|
-
default: '',
|
|
29
|
-
description: 'Token for Project API — project management, competitors, audit, etc. Leave empty if not using Project API resources.',
|
|
30
28
|
},
|
|
31
|
-
|
|
29
|
+
};
|
|
32
30
|
this.test = {
|
|
33
31
|
request: {
|
|
34
32
|
baseURL: 'https://api.seranking.com/v1',
|
|
35
33
|
url: '/account/subscription',
|
|
36
34
|
method: 'GET',
|
|
37
|
-
headers: {
|
|
38
|
-
'Authorization': '=Token {{$credentials.apiToken}}',
|
|
39
|
-
},
|
|
40
35
|
},
|
|
41
36
|
};
|
|
42
37
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class SeRankingProjectApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
authenticate: IAuthenticateGeneric;
|
|
8
|
+
test: ICredentialTestRequest;
|
|
9
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SeRankingProjectApi = void 0;
|
|
4
|
+
class SeRankingProjectApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'seRankingProjectApi';
|
|
7
|
+
this.displayName = 'SE Ranking Project API';
|
|
8
|
+
this.documentationUrl = 'https://seranking.com/api-google-organic.html';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'API Token',
|
|
12
|
+
name: 'apiToken',
|
|
13
|
+
type: 'string',
|
|
14
|
+
typeOptions: {
|
|
15
|
+
password: true,
|
|
16
|
+
},
|
|
17
|
+
default: '',
|
|
18
|
+
required: true,
|
|
19
|
+
description: 'Your SE Ranking Project API token from the API Dashboard',
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
this.authenticate = {
|
|
23
|
+
type: 'generic',
|
|
24
|
+
properties: {
|
|
25
|
+
headers: {
|
|
26
|
+
'Authorization': '=Token {{$credentials.apiToken}}',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
this.test = {
|
|
31
|
+
request: {
|
|
32
|
+
baseURL: 'https://api4.seranking.com',
|
|
33
|
+
url: '/sites',
|
|
34
|
+
method: 'GET',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.SeRankingProjectApi = SeRankingProjectApi;
|
|
@@ -62,6 +62,42 @@ class SeRanking {
|
|
|
62
62
|
{
|
|
63
63
|
name: 'seRankingApi',
|
|
64
64
|
required: true,
|
|
65
|
+
displayOptions: {
|
|
66
|
+
show: {
|
|
67
|
+
resource: [
|
|
68
|
+
'aiSearch',
|
|
69
|
+
'backlinks',
|
|
70
|
+
'domainAnalysis',
|
|
71
|
+
'keywordResearch',
|
|
72
|
+
'serpClassic',
|
|
73
|
+
'websiteAudit',
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'seRankingProjectApi',
|
|
80
|
+
required: true,
|
|
81
|
+
displayOptions: {
|
|
82
|
+
show: {
|
|
83
|
+
resource: [
|
|
84
|
+
'accountSystem',
|
|
85
|
+
'aiResultTracker',
|
|
86
|
+
'analyticsTraffic',
|
|
87
|
+
'backlinkChecker',
|
|
88
|
+
'competitors',
|
|
89
|
+
'generalData',
|
|
90
|
+
'keywordGroups',
|
|
91
|
+
'marketingPlan',
|
|
92
|
+
'projectGroups',
|
|
93
|
+
'projectManagement',
|
|
94
|
+
'searchVolume',
|
|
95
|
+
'subAccount',
|
|
96
|
+
'urlTags',
|
|
97
|
+
'websiteAuditProject',
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
65
101
|
},
|
|
66
102
|
],
|
|
67
103
|
properties: [
|
|
@@ -302,6 +302,14 @@ exports.aiResultTrackerFields = [
|
|
|
302
302
|
},
|
|
303
303
|
},
|
|
304
304
|
options: [
|
|
305
|
+
{
|
|
306
|
+
displayName: 'Group IDs',
|
|
307
|
+
name: 'groupIds',
|
|
308
|
+
type: 'string',
|
|
309
|
+
default: '',
|
|
310
|
+
placeholder: '12,18',
|
|
311
|
+
description: 'Comma-separated prompt group IDs to filter by',
|
|
312
|
+
},
|
|
305
313
|
{
|
|
306
314
|
displayName: 'Limit',
|
|
307
315
|
name: 'limit',
|
|
@@ -333,6 +341,28 @@ exports.aiResultTrackerFields = [
|
|
|
333
341
|
placeholder: 'best seo tool,seo rank tracker,website audit tool',
|
|
334
342
|
description: 'Comma-separated list of keyword prompts to add (max 255 chars each)',
|
|
335
343
|
},
|
|
344
|
+
{
|
|
345
|
+
displayName: 'Additional Fields',
|
|
346
|
+
name: 'additionalFields',
|
|
347
|
+
type: 'collection',
|
|
348
|
+
placeholder: 'Add Field',
|
|
349
|
+
default: {},
|
|
350
|
+
displayOptions: {
|
|
351
|
+
show: {
|
|
352
|
+
resource: ['aiResultTracker'],
|
|
353
|
+
operation: ['addPrompts'],
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
options: [
|
|
357
|
+
{
|
|
358
|
+
displayName: 'Group ID',
|
|
359
|
+
name: 'groupId',
|
|
360
|
+
type: 'number',
|
|
361
|
+
default: 0,
|
|
362
|
+
description: 'Target prompt group ID. Defaults to the site\'s default group.',
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
},
|
|
336
366
|
{
|
|
337
367
|
displayName: 'Keyword-LLM Link IDs',
|
|
338
368
|
name: 'k2siteLlmIds',
|
|
@@ -377,6 +407,14 @@ exports.aiResultTrackerFields = [
|
|
|
377
407
|
placeholder: '2026-01-31',
|
|
378
408
|
description: 'End date (YYYY-MM-DD). Defaults to current date.',
|
|
379
409
|
},
|
|
410
|
+
{
|
|
411
|
+
displayName: 'Group IDs',
|
|
412
|
+
name: 'groupIds',
|
|
413
|
+
type: 'string',
|
|
414
|
+
default: '',
|
|
415
|
+
placeholder: '12,18',
|
|
416
|
+
description: 'Comma-separated prompt group IDs to filter by',
|
|
417
|
+
},
|
|
380
418
|
{
|
|
381
419
|
displayName: 'Limit',
|
|
382
420
|
name: 'limit',
|
|
@@ -405,7 +443,7 @@ exports.aiResultTrackerFields = [
|
|
|
405
443
|
},
|
|
406
444
|
},
|
|
407
445
|
default: 0,
|
|
408
|
-
description: 'Value of the <code>k2site_llm_id</code> field from List Prompts
|
|
446
|
+
description: 'Value of the <code>k2site_llm_id</code> field from List Prompts',
|
|
409
447
|
},
|
|
410
448
|
{
|
|
411
449
|
displayName: 'Additional Fields',
|
|
@@ -79,13 +79,22 @@ async function AiResultTrackerOperations(index) {
|
|
|
79
79
|
query.limit = additionalFields.limit;
|
|
80
80
|
if (additionalFields.offset !== undefined)
|
|
81
81
|
query.offset = additionalFields.offset;
|
|
82
|
-
|
|
82
|
+
let endpoint = `/sites/${siteId}/airt/llm/${llmId}/prompts`;
|
|
83
|
+
if (additionalFields.groupIds) {
|
|
84
|
+
const ids = additionalFields.groupIds.split(',').map((id) => id.trim());
|
|
85
|
+
endpoint += '?' + ids.map((id) => `group_ids[]=${encodeURIComponent(id)}`).join('&');
|
|
86
|
+
}
|
|
87
|
+
return await apiRequest_1.apiRequest.call(this, 'GET', endpoint, {}, query, index);
|
|
83
88
|
}
|
|
84
89
|
case 'addPrompts': {
|
|
85
90
|
const llmId = this.getNodeParameter('llmId', index);
|
|
86
91
|
const promptsStr = this.getNodeParameter('prompts', index);
|
|
87
92
|
const prompts = promptsStr.split(',').map((p) => p.trim()).filter((p) => p.length > 0);
|
|
88
|
-
|
|
93
|
+
const additionalFields = this.getNodeParameter('additionalFields', index, {});
|
|
94
|
+
const body = { prompts };
|
|
95
|
+
if (additionalFields.groupId)
|
|
96
|
+
body.group_id = additionalFields.groupId;
|
|
97
|
+
return await apiRequest_1.apiRequest.call(this, 'POST', `/sites/${siteId}/airt/llm/${llmId}/prompts`, body, {}, index);
|
|
89
98
|
}
|
|
90
99
|
case 'deletePrompts': {
|
|
91
100
|
const llmId = this.getNodeParameter('llmId', index);
|
|
@@ -106,7 +115,12 @@ async function AiResultTrackerOperations(index) {
|
|
|
106
115
|
query.limit = additionalFields.limit;
|
|
107
116
|
if (additionalFields.offset !== undefined)
|
|
108
117
|
query.offset = additionalFields.offset;
|
|
109
|
-
|
|
118
|
+
let endpoint = `/sites/${siteId}/airt/llm/${llmId}/prompts/rankings`;
|
|
119
|
+
if (additionalFields.groupIds) {
|
|
120
|
+
const ids = additionalFields.groupIds.split(',').map((id) => id.trim());
|
|
121
|
+
endpoint += '?' + ids.map((id) => `group_ids[]=${encodeURIComponent(id)}`).join('&');
|
|
122
|
+
}
|
|
123
|
+
return await apiRequest_1.apiRequest.call(this, 'GET', endpoint, {}, query, index);
|
|
110
124
|
}
|
|
111
125
|
case 'getPromptAnswer': {
|
|
112
126
|
const llmId = this.getNodeParameter('llmId', index);
|
|
@@ -5,7 +5,7 @@ const n8n_workflow_1 = require("n8n-workflow");
|
|
|
5
5
|
let lastRequestTime = 0;
|
|
6
6
|
const MIN_REQUEST_INTERVAL = 300;
|
|
7
7
|
async function apiRequest(method, endpoint, body = {}, query = {}, itemIndex = 0) {
|
|
8
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
8
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
9
9
|
const now = Date.now();
|
|
10
10
|
const timeSinceLastRequest = now - lastRequestTime;
|
|
11
11
|
if (timeSinceLastRequest < MIN_REQUEST_INTERVAL) {
|
|
@@ -29,23 +29,12 @@ async function apiRequest(method, endpoint, body = {}, query = {}, itemIndex = 0
|
|
|
29
29
|
resource = '';
|
|
30
30
|
}
|
|
31
31
|
const isDataApi = DATA_API_RESOURCES.has(resource);
|
|
32
|
-
const
|
|
33
|
-
const token = isDataApi
|
|
34
|
-
? credentials.apiToken
|
|
35
|
-
: credentials.projectApiToken;
|
|
36
|
-
if (!token) {
|
|
37
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), isDataApi
|
|
38
|
-
? 'Data API token not configured. Add your Data API token in the SE Ranking credentials.'
|
|
39
|
-
: 'Project API token not configured. Add your Project API token in the SE Ranking credentials.', { itemIndex });
|
|
40
|
-
}
|
|
32
|
+
const credentialType = isDataApi ? 'seRankingApi' : 'seRankingProjectApi';
|
|
41
33
|
const httpMethod = method.toUpperCase();
|
|
42
34
|
const options = {
|
|
43
35
|
method: httpMethod,
|
|
44
36
|
timeout: 60000,
|
|
45
37
|
url: '',
|
|
46
|
-
headers: {
|
|
47
|
-
'Authorization': `Token ${token}`,
|
|
48
|
-
},
|
|
49
38
|
};
|
|
50
39
|
if (query._fullUrl) {
|
|
51
40
|
options.url = endpoint;
|
|
@@ -117,12 +106,24 @@ async function apiRequest(method, endpoint, body = {}, query = {}, itemIndex = 0
|
|
|
117
106
|
}
|
|
118
107
|
}
|
|
119
108
|
try {
|
|
120
|
-
const response = await this.helpers.
|
|
109
|
+
const response = await this.helpers.httpRequestWithAuthentication.call(this, credentialType, options);
|
|
121
110
|
return response;
|
|
122
111
|
}
|
|
123
112
|
catch (error) {
|
|
124
|
-
|
|
125
|
-
|
|
113
|
+
if (((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('does not require credentials')) || ((_b = error.message) === null || _b === void 0 ? void 0 : _b.includes('No credentials'))) {
|
|
114
|
+
const missingCred = isDataApi
|
|
115
|
+
? 'SE Ranking API credential not configured'
|
|
116
|
+
: 'SE Ranking Project API credential not configured';
|
|
117
|
+
const missingDesc = isDataApi
|
|
118
|
+
? 'This resource requires the SE Ranking API credential. Click the node, find the "SE Ranking API" credential slot, and create/select your Data API token.'
|
|
119
|
+
: 'This resource requires the SE Ranking Project API credential. Click the node, find the "SE Ranking Project API" credential slot, and create/select your Project API token.';
|
|
120
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), missingCred, {
|
|
121
|
+
itemIndex,
|
|
122
|
+
description: missingDesc,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
const errorData = ((_c = error.response) === null || _c === void 0 ? void 0 : _c.body) || ((_d = error.response) === null || _d === void 0 ? void 0 : _d.data) || {};
|
|
126
|
+
const statusCode = error.statusCode || ((_e = error.response) === null || _e === void 0 ? void 0 : _e.status) || 'Unknown';
|
|
126
127
|
let errorMessage = 'Unknown error occurred';
|
|
127
128
|
let errorDescription = '';
|
|
128
129
|
if (statusCode === 400) {
|
|
@@ -143,7 +144,7 @@ async function apiRequest(method, endpoint, body = {}, query = {}, itemIndex = 0
|
|
|
143
144
|
}
|
|
144
145
|
else if (statusCode === 429) {
|
|
145
146
|
errorMessage = 'Rate Limit Exceeded';
|
|
146
|
-
const retryAfter = ((
|
|
147
|
+
const retryAfter = ((_g = (_f = error.response) === null || _f === void 0 ? void 0 : _f.headers) === null || _g === void 0 ? void 0 : _g['retry-after']) || ((_j = (_h = error.response) === null || _h === void 0 ? void 0 : _h.headers) === null || _j === void 0 ? void 0 : _j['Retry-After']) || 60;
|
|
147
148
|
errorDescription = `Too many requests. SE Ranking requires you to wait ${retryAfter} seconds. The node now automatically adds 300ms delay between requests, but SE Ranking may have additional hourly/daily limits.`;
|
|
148
149
|
}
|
|
149
150
|
else if (statusCode === 500 || statusCode === 502 || statusCode === 503) {
|
|
@@ -158,7 +159,7 @@ async function apiRequest(method, endpoint, body = {}, query = {}, itemIndex = 0
|
|
|
158
159
|
errorMessage = 'Connection Failed';
|
|
159
160
|
errorDescription = 'Cannot reach SE Ranking API. Check your internet connection';
|
|
160
161
|
}
|
|
161
|
-
else if (error.code === 'ETIMEDOUT' || ((
|
|
162
|
+
else if (error.code === 'ETIMEDOUT' || ((_k = error.message) === null || _k === void 0 ? void 0 : _k.includes('timeout'))) {
|
|
162
163
|
errorMessage = 'Request Timeout';
|
|
163
164
|
errorDescription = 'Request exceeded 60 seconds. Try with fewer items or use a faster operation';
|
|
164
165
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seranking/n8n-nodes-seranking",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.12",
|
|
4
4
|
"description": "n8n connector for SE Ranking API - AI Search, Backlinks, Domain Analysis, Keyword Research, Website Audit, Project Management, Project Groups, AI Result Tracker, Keyword Groups, Competitors, URL Tags, Analytics Traffic, Account System, Sub-Account Management, General Data, Marketing Plan, Backlink Checker, Search Volume",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/seranking/n8n-nodes-seranking",
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"dist/nodes/SeRanking/SeRanking.node.js"
|
|
52
52
|
],
|
|
53
53
|
"credentials": [
|
|
54
|
-
"dist/credentials/SeRankingApi.credentials.js"
|
|
54
|
+
"dist/credentials/SeRankingApi.credentials.js",
|
|
55
|
+
"dist/credentials/SeRankingProjectApi.credentials.js"
|
|
55
56
|
]
|
|
56
57
|
},
|
|
57
58
|
"scripts": {
|