@seranking/n8n-nodes-seranking 1.5.8 → 1.5.10

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
@@ -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. Enter your API Token
94
- 4. Select "Data API" as API Type
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,11 @@ 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. Click on **Create New Credential**
167
- 3. Enter your **API Token**
168
- 4. Select **API Type**:
169
- - **Data API** — SEO research data (domains, keywords, backlinks, SERP)
170
- - **Project API** — Project management, tracking, audits, sub-accounts
171
- 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**
172
168
 
173
- The node will automatically test your credentials by making a test request to the SE Ranking API.
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.
174
170
 
175
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).
176
172
 
@@ -882,7 +878,7 @@ For more details, see [n8n's rate limiting documentation](https://docs.n8n.io/in
882
878
  **Solution**:
883
879
 
884
880
  1. Verify API token is correct (copy from SE Ranking dashboard)
885
- 2. Ensure API Type is set to "Data API"
881
+ 2. For Project API resources, ensure the SE Ranking Project API credential is configured
886
882
  3. Check token hasn't expired
887
883
  4. Regenerate token in SE Ranking dashboard if needed
888
884
  5. Test credentials using the "Test" button in n8n
@@ -16,26 +16,7 @@ class SeRankingApi {
16
16
  },
17
17
  default: '',
18
18
  required: true,
19
- description: 'Your SE Ranking API token from the API Dashboard',
20
- },
21
- {
22
- displayName: 'API Type',
23
- name: 'apiType',
24
- type: 'options',
25
- options: [
26
- {
27
- name: 'Data API',
28
- value: 'data',
29
- description: 'Used for positions, rankings, keyword data.',
30
- },
31
- {
32
- name: 'Project API',
33
- value: 'project',
34
- description: 'Used for managing sites, accounts, etc.',
35
- },
36
- ],
37
- default: 'data',
38
- description: 'Choose which SE Ranking API you want to connect to',
19
+ description: 'Your SE Ranking Data API token from the API Dashboard',
39
20
  },
40
21
  ];
41
22
  this.authenticate = {
@@ -48,8 +29,8 @@ class SeRankingApi {
48
29
  };
49
30
  this.test = {
50
31
  request: {
51
- baseURL: '={{$credentials.apiType === "project" ? "https://api4.seranking.com" : "https://api.seranking.com/v1"}}',
52
- url: '={{$credentials.apiType === "project" ? "/sites" : "/account/subscription"}}',
32
+ baseURL: 'https://api.seranking.com/v1',
33
+ url: '/account/subscription',
53
34
  method: 'GET',
54
35
  },
55
36
  };
@@ -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;
@@ -50,7 +50,7 @@ class SeRanking {
50
50
  icon: 'file:seranking.svg',
51
51
  group: ['transform'],
52
52
  version: 1,
53
- subtitle: '={{$parameter["apiType"] === "projectApi" ? "Project" : "Data"}} — {{$parameter["resource"] + ": " + $parameter["operation"]}}',
53
+ subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
54
54
  description: 'Interact with SE Ranking API for SEO data',
55
55
  defaults: {
56
56
  name: 'SE Ranking',
@@ -62,80 +62,110 @@ 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: [
68
104
  {
69
- displayName: 'API Type',
70
- name: 'apiType',
105
+ displayName: 'Resource',
106
+ name: 'resource',
71
107
  type: 'options',
72
108
  noDataExpression: true,
73
109
  options: [
74
110
  {
75
- name: 'Data API',
76
- value: 'dataApi',
77
- description: 'SEO research data (domains, keywords, backlinks, etc.)',
111
+ name: 'Account System',
112
+ value: 'accountSystem',
113
+ description: 'Account balance, profile, and subscription info',
78
114
  },
79
115
  {
80
- name: 'Project API',
81
- value: 'projectApi',
82
- description: 'Project management, groups, and AI result tracking',
116
+ name: 'AI Result Tracker',
117
+ value: 'aiResultTracker',
118
+ description: 'Track brand visibility across AI search engines',
83
119
  },
84
- ],
85
- default: 'dataApi',
86
- },
87
- {
88
- displayName: 'Resource',
89
- name: 'resource',
90
- type: 'options',
91
- noDataExpression: true,
92
- displayOptions: {
93
- show: { apiType: ['dataApi'] },
94
- },
95
- options: [
96
120
  {
97
121
  name: 'AI Search',
98
122
  value: 'aiSearch',
99
123
  description: 'LLM visibility and AI search data',
100
124
  },
125
+ {
126
+ name: 'Analytics Traffic',
127
+ value: 'analyticsTraffic',
128
+ description: 'Google Search Console data and SEO potential',
129
+ },
130
+ {
131
+ name: 'Backlink Checker',
132
+ value: 'backlinkChecker',
133
+ description: 'Backlink monitoring, disavow, and groups',
134
+ },
101
135
  {
102
136
  name: 'Backlinks',
103
137
  value: 'backlinks',
104
138
  description: 'Backlink analysis and authority metrics',
105
139
  },
140
+ {
141
+ name: 'Competitors',
142
+ value: 'competitors',
143
+ description: 'Manage competitors and retrieve ranking data',
144
+ },
106
145
  {
107
146
  name: 'Domain Analysis',
108
147
  value: 'domainAnalysis',
109
148
  description: 'Domain keyword rankings and competitor analysis',
110
149
  },
111
150
  {
112
- name: 'Keyword Research',
113
- value: 'keywordResearch',
114
- description: 'Keyword metrics, volume, CPC, and related keywords',
151
+ name: 'General Data',
152
+ value: 'generalData',
153
+ description: 'System search engines, languages, regions, and keyword volume',
115
154
  },
116
155
  {
117
- name: 'SERP Classic',
118
- value: 'serpClassic',
119
- description: 'SERP tracking and results retrieval',
156
+ name: 'Keyword Groups',
157
+ value: 'keywordGroups',
158
+ description: 'Manage keyword groups within a project',
120
159
  },
121
160
  {
122
- name: 'Website Audit',
123
- value: 'websiteAudit',
124
- description: 'Site crawling, technical SEO, and on-page analysis',
161
+ name: 'Keyword Research',
162
+ value: 'keywordResearch',
163
+ description: 'Keyword metrics, volume, CPC, and related keywords',
125
164
  },
126
- ],
127
- default: 'domainAnalysis',
128
- },
129
- {
130
- displayName: 'Resource',
131
- name: 'resource',
132
- type: 'options',
133
- noDataExpression: true,
134
- options: [
135
165
  {
136
- name: 'Project Management',
137
- value: 'projectManagement',
138
- description: 'Manage projects and search engine configurations',
166
+ name: 'Marketing Plan',
167
+ value: 'marketingPlan',
168
+ description: 'Marketing plan checklists and tasks',
139
169
  },
140
170
  {
141
171
  name: 'Project Groups',
@@ -143,34 +173,19 @@ class SeRanking {
143
173
  description: 'Manage project groups within the account',
144
174
  },
145
175
  {
146
- name: 'AI Result Tracker',
147
- value: 'aiResultTracker',
148
- description: 'Track brand visibility across AI search engines',
149
- },
150
- {
151
- name: 'Keyword Groups',
152
- value: 'keywordGroups',
153
- description: 'Manage keyword groups within a project',
154
- },
155
- {
156
- name: 'Competitors',
157
- value: 'competitors',
158
- description: 'Manage competitors and retrieve ranking data',
159
- },
160
- {
161
- name: 'URL Tags',
162
- value: 'urlTags',
163
- description: 'Manage landing page tags within a site',
176
+ name: 'Project Management',
177
+ value: 'projectManagement',
178
+ description: 'Manage projects and search engine configurations',
164
179
  },
165
180
  {
166
- name: 'Analytics Traffic',
167
- value: 'analyticsTraffic',
168
- description: 'Google Search Console data and SEO potential',
181
+ name: 'Search Volume',
182
+ value: 'searchVolume',
183
+ description: 'Keyword search volume check requests',
169
184
  },
170
185
  {
171
- name: 'Account System',
172
- value: 'accountSystem',
173
- description: 'Account balance, profile, and subscription info',
186
+ name: 'SERP Classic',
187
+ value: 'serpClassic',
188
+ description: 'SERP tracking and results retrieval',
174
189
  },
175
190
  {
176
191
  name: 'Sub-Account Management',
@@ -178,32 +193,22 @@ class SeRanking {
178
193
  description: 'Manage sub-accounts, sharing, and permissions',
179
194
  },
180
195
  {
181
- name: 'General Data',
182
- value: 'generalData',
183
- description: 'System search engines, languages, regions, and keyword volume',
196
+ name: 'URL Tags',
197
+ value: 'urlTags',
198
+ description: 'Manage landing page tags within a site',
184
199
  },
185
200
  {
186
- name: 'Marketing Plan',
187
- value: 'marketingPlan',
188
- description: 'Marketing plan checklists and tasks',
201
+ name: 'Website Audit (Data)',
202
+ value: 'websiteAudit',
203
+ description: 'Site crawling, technical SEO, and on-page analysis',
189
204
  },
190
205
  {
191
- name: 'Website Audit',
206
+ name: 'Website Audit (Project)',
192
207
  value: 'websiteAuditProject',
193
208
  description: 'Technical SEO audits lifecycle management',
194
209
  },
195
- {
196
- name: 'Backlink Checker',
197
- value: 'backlinkChecker',
198
- description: 'Backlink monitoring, disavow, and groups',
199
- },
200
- {
201
- name: 'Search Volume',
202
- value: 'searchVolume',
203
- description: 'Keyword search volume check requests',
204
- },
205
210
  ],
206
- default: 'projectManagement',
211
+ default: 'domainAnalysis',
207
212
  },
208
213
  ...AiSearchDescription_1.aiSearchOperations,
209
214
  ...AiSearchDescription_1.aiSearchFields,
@@ -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) {
@@ -13,7 +13,23 @@ async function apiRequest(method, endpoint, body = {}, query = {}, itemIndex = 0
13
13
  await (0, n8n_workflow_1.sleep)(waitTime);
14
14
  }
15
15
  lastRequestTime = Date.now();
16
- const credentials = await this.getCredentials('seRankingApi');
16
+ const DATA_API_RESOURCES = new Set([
17
+ 'aiSearch',
18
+ 'backlinks',
19
+ 'domainAnalysis',
20
+ 'keywordResearch',
21
+ 'serpClassic',
22
+ 'websiteAudit',
23
+ ]);
24
+ let resource;
25
+ try {
26
+ resource = this.getNodeParameter('resource', itemIndex);
27
+ }
28
+ catch {
29
+ resource = '';
30
+ }
31
+ const isDataApi = DATA_API_RESOURCES.has(resource);
32
+ const credentialType = isDataApi ? 'seRankingApi' : 'seRankingProjectApi';
17
33
  const httpMethod = method.toUpperCase();
18
34
  const options = {
19
35
  method: httpMethod,
@@ -28,16 +44,9 @@ async function apiRequest(method, endpoint, body = {}, query = {}, itemIndex = 0
28
44
  options.json = false;
29
45
  }
30
46
  else {
31
- let baseUrl;
32
- if (endpoint.startsWith('/site-audit/') || endpoint.startsWith('/backlinks/') || endpoint.startsWith('/ai-search/') || endpoint.startsWith('/domain/') || endpoint.startsWith('/keywords/')) {
33
- baseUrl = 'https://api.seranking.com/v1';
34
- }
35
- else if (credentials.apiType === 'project') {
36
- baseUrl = 'https://api4.seranking.com';
37
- }
38
- else {
39
- baseUrl = 'https://api.seranking.com/v1';
40
- }
47
+ const baseUrl = isDataApi
48
+ ? 'https://api.seranking.com/v1'
49
+ : 'https://api4.seranking.com';
41
50
  options.url = `${baseUrl}${endpoint}`;
42
51
  options.json = true;
43
52
  }
@@ -97,12 +106,24 @@ async function apiRequest(method, endpoint, body = {}, query = {}, itemIndex = 0
97
106
  }
98
107
  }
99
108
  try {
100
- const response = await this.helpers.httpRequestWithAuthentication.call(this, 'seRankingApi', options);
109
+ const response = await this.helpers.httpRequestWithAuthentication.call(this, credentialType, options);
101
110
  return response;
102
111
  }
103
112
  catch (error) {
104
- const errorData = ((_a = error.response) === null || _a === void 0 ? void 0 : _a.body) || ((_b = error.response) === null || _b === void 0 ? void 0 : _b.data) || {};
105
- const statusCode = error.statusCode || ((_c = error.response) === null || _c === void 0 ? void 0 : _c.status) || 'Unknown';
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';
106
127
  let errorMessage = 'Unknown error occurred';
107
128
  let errorDescription = '';
108
129
  if (statusCode === 400) {
@@ -123,7 +144,7 @@ async function apiRequest(method, endpoint, body = {}, query = {}, itemIndex = 0
123
144
  }
124
145
  else if (statusCode === 429) {
125
146
  errorMessage = 'Rate Limit Exceeded';
126
- const retryAfter = ((_e = (_d = error.response) === null || _d === void 0 ? void 0 : _d.headers) === null || _e === void 0 ? void 0 : _e['retry-after']) || ((_g = (_f = error.response) === null || _f === void 0 ? void 0 : _f.headers) === null || _g === void 0 ? void 0 : _g['Retry-After']) || 60;
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;
127
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.`;
128
149
  }
129
150
  else if (statusCode === 500 || statusCode === 502 || statusCode === 503) {
@@ -138,7 +159,7 @@ async function apiRequest(method, endpoint, body = {}, query = {}, itemIndex = 0
138
159
  errorMessage = 'Connection Failed';
139
160
  errorDescription = 'Cannot reach SE Ranking API. Check your internet connection';
140
161
  }
141
- else if (error.code === 'ETIMEDOUT' || ((_h = error.message) === null || _h === void 0 ? void 0 : _h.includes('timeout'))) {
162
+ else if (error.code === 'ETIMEDOUT' || ((_k = error.message) === null || _k === void 0 ? void 0 : _k.includes('timeout'))) {
142
163
  errorMessage = 'Request Timeout';
143
164
  errorDescription = 'Request exceeded 60 seconds. Try with fewer items or use a faster operation';
144
165
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seranking/n8n-nodes-seranking",
3
- "version": "1.5.8",
3
+ "version": "1.5.10",
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": {
@@ -79,6 +80,5 @@
79
80
  "engines": {
80
81
  "node": ">=18.0.0"
81
82
  },
82
- "dependencies": {
83
- }
83
+ "dependencies": {}
84
84
  }