universal-mcp-applications 0.1.1__py3-none-any.whl → 0.1.2__py3-none-any.whl

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.

Potentially problematic release.


This version of universal-mcp-applications might be problematic. Click here for more details.

Files changed (43) hide show
  1. universal_mcp/applications/{aws-s3 → aws_s3}/app.py +1 -1
  2. universal_mcp/applications/cal_com_v2/__init__.py +1 -0
  3. universal_mcp/applications/{cal-com-v2 → cal_com_v2}/app.py +137 -139
  4. universal_mcp/applications/clickup/app.py +2 -2
  5. universal_mcp/applications/github/app.py +1 -2
  6. universal_mcp/applications/{google-sheet → google_sheet}/app.py +1 -1
  7. universal_mcp/applications/google_sheet/helper.py +345 -0
  8. universal_mcp/applications/hashnode/app.py +1 -1
  9. universal_mcp/applications/hubspot/app.py +13 -2
  10. universal_mcp/applications/markitdown/app.py +1 -1
  11. universal_mcp/applications/{ms-teams → ms_teams}/app.py +1 -1
  12. universal_mcp/applications/semrush/app.py +479 -1468
  13. universal_mcp/applications/sharepoint/app.py +1 -1
  14. {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.2.dist-info}/METADATA +1 -1
  15. {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.2.dist-info}/RECORD +42 -41
  16. universal_mcp/applications/cal-com-v2/__init__.py +0 -1
  17. /universal_mcp/applications/{aws-s3 → aws_s3}/README.md +0 -0
  18. /universal_mcp/applications/{aws-s3 → aws_s3}/__init__.py +0 -0
  19. /universal_mcp/applications/{cal-com-v2 → cal_com_v2}/README.md +0 -0
  20. /universal_mcp/applications/{google-calendar → google_calendar}/README.md +0 -0
  21. /universal_mcp/applications/{google-calendar → google_calendar}/__init__.py +0 -0
  22. /universal_mcp/applications/{google-calendar → google_calendar}/app.py +0 -0
  23. /universal_mcp/applications/{google-docs → google_docs}/README.md +0 -0
  24. /universal_mcp/applications/{google-docs → google_docs}/__init__.py +0 -0
  25. /universal_mcp/applications/{google-docs → google_docs}/app.py +0 -0
  26. /universal_mcp/applications/{google-drive → google_drive}/README.md +0 -0
  27. /universal_mcp/applications/{google-drive → google_drive}/__init__.py +0 -0
  28. /universal_mcp/applications/{google-drive → google_drive}/app.py +0 -0
  29. /universal_mcp/applications/{google-gemini → google_gemini}/README.md +0 -0
  30. /universal_mcp/applications/{google-gemini → google_gemini}/__init__.py +0 -0
  31. /universal_mcp/applications/{google-gemini → google_gemini}/app.py +0 -0
  32. /universal_mcp/applications/{google-mail → google_mail}/README.md +0 -0
  33. /universal_mcp/applications/{google-mail → google_mail}/__init__.py +0 -0
  34. /universal_mcp/applications/{google-mail → google_mail}/app.py +0 -0
  35. /universal_mcp/applications/{google-sheet → google_sheet}/README.md +0 -0
  36. /universal_mcp/applications/{google-sheet → google_sheet}/__init__.py +0 -0
  37. /universal_mcp/applications/{ms-teams → ms_teams}/README.md +0 -0
  38. /universal_mcp/applications/{ms-teams → ms_teams}/__init__.py +0 -0
  39. /universal_mcp/applications/{whatsapp-business → whatsapp_business}/README.md +0 -0
  40. /universal_mcp/applications/{whatsapp-business → whatsapp_business}/__init__.py +0 -0
  41. /universal_mcp/applications/{whatsapp-business → whatsapp_business}/app.py +0 -0
  42. {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.2.dist-info}/WHEEL +0 -0
  43. {universal_mcp_applications-0.1.1.dist-info → universal_mcp_applications-0.1.2.dist-info}/licenses/LICENSE +0 -0
@@ -40,6 +40,25 @@ class SemrushApp(APIApplication):
40
40
  """
41
41
  self._api_key = value
42
42
 
43
+ def _build_params_and_get(self, report_type: str, **kwargs) -> dict[str, Any]:
44
+ """
45
+ Builds the parameters dictionary and makes a GET request to the Semrush API.
46
+ """
47
+ params = {
48
+ "type": report_type,
49
+ "key": self.api_key,
50
+ }
51
+ for key, value in kwargs.items():
52
+ if value is not None:
53
+ params[key] = value
54
+
55
+ url = self.base_url
56
+ if "analytics" in report_type:
57
+ url = f"{self.base_url}/analytics/v1"
58
+
59
+ response = self._get(url, params=params)
60
+ return self._handle_response(response)
61
+
43
62
  def domain_ad_history(
44
63
  self,
45
64
  domain: str,
@@ -55,60 +74,22 @@ class SemrushApp(APIApplication):
55
74
  ) -> dict[str, Any]:
56
75
  """
57
76
  Get domain ad history data showing past ad copies, landing pages, and performance metrics over time.
58
-
59
- Args:
60
- domain (str): Unique name of the website to investigate
61
- database (str): Regional database (default: "us")
62
- display_limit (int, optional): Number of results to return (max 100,000)
63
- display_offset (int, optional): Number of results to skip
64
- display_date (str, optional): Date in format "YYYYMM15" for historical data
65
- export_columns (str, optional): Comma-separated list of columns to include
66
- display_sort (str, optional): Sorting order (e.g., "tr_desc", "pc_asc")
67
- display_filter (str, optional): Filter criteria for columns
68
- export_escape (int, optional): Set to 1 to wrap columns in quotes
69
- export_decode (int, optional): Set to 0 for URL-encoded response
70
-
71
- Returns:
72
- Dict[str, Any]: API response data
73
-
74
- Raises:
75
- ValueError: If required parameters are missing
76
- httpx.HTTPStatusError: If the API request fails
77
-
78
- Tags:
79
- domain-search, important
80
77
  """
81
78
  if not domain:
82
79
  raise ValueError("Domain parameter is required")
83
-
84
- # Build parameters dictionary
85
- params = {
86
- "type": "domain_ad_history",
87
- "key": self.api_key,
88
- "domain": domain,
89
- "database": database,
90
- }
91
-
92
- if display_limit is not None:
93
- params["display_limit"] = display_limit
94
- if display_offset is not None:
95
- params["display_offset"] = display_offset
96
- if display_date is not None:
97
- params["display_date"] = display_date
98
- if export_columns is not None:
99
- params["export_columns"] = export_columns
100
- if display_sort is not None:
101
- params["display_sort"] = display_sort
102
- if display_filter is not None:
103
- params["display_filter"] = display_filter
104
- if export_escape is not None:
105
- params["export_escape"] = export_escape
106
- if export_decode is not None:
107
- params["export_decode"] = export_decode
108
-
109
- response = self._get(self.base_url, params=params)
110
-
111
- return self._handle_response(response)
80
+ return self._build_params_and_get(
81
+ "domain_ad_history",
82
+ domain=domain,
83
+ database=database,
84
+ display_limit=display_limit,
85
+ display_offset=display_offset,
86
+ display_date=display_date,
87
+ export_columns=export_columns,
88
+ display_sort=display_sort,
89
+ display_filter=display_filter,
90
+ export_escape=export_escape,
91
+ export_decode=export_decode,
92
+ )
112
93
 
113
94
  def domain_organic_pages(
114
95
  self,
@@ -125,60 +106,22 @@ class SemrushApp(APIApplication):
125
106
  ) -> dict[str, Any]:
126
107
  """
127
108
  Get unique pages of a domain that rank in Google's top 100 organic search results.
128
-
129
- Args:
130
- domain (str): Unique name of the website to investigate
131
- database (str): Regional database (default: "us")
132
- display_limit (int, optional): Number of results to return (max 100,000)
133
- display_offset (int, optional): Number of results to skip
134
- display_date (str, optional): Date in format "YYYYMM15" for historical data
135
- export_columns (str, optional): Comma-separated list of columns to include
136
- display_sort (str, optional): Sorting order (e.g., "tr_desc", "pc_asc")
137
- display_filter (str, optional): Filter criteria for columns
138
- export_escape (int, optional): Set to 1 to wrap columns in quotes
139
- export_decode (int, optional): Set to 0 for URL-encoded response
140
-
141
- Returns:
142
- dict[str, Any]: API response data
143
-
144
- Raises:
145
- ValueError: If required parameters are missing
146
- httpx.HTTPStatusError: If the API request fails
147
-
148
- Tags:
149
- domain-search
150
109
  """
151
110
  if not domain:
152
111
  raise ValueError("Domain parameter is required")
153
-
154
- # Build parameters dictionary
155
- params = {
156
- "type": "domain_organic_unique",
157
- "key": self.api_key,
158
- "domain": domain,
159
- "database": database,
160
- }
161
-
162
- if display_limit is not None:
163
- params["display_limit"] = display_limit
164
- if display_offset is not None:
165
- params["display_offset"] = display_offset
166
- if display_date is not None:
167
- params["display_date"] = display_date
168
- if export_columns is not None:
169
- params["export_columns"] = export_columns
170
- if display_sort is not None:
171
- params["display_sort"] = display_sort
172
- if display_filter is not None:
173
- params["display_filter"] = display_filter
174
- if export_escape is not None:
175
- params["export_escape"] = export_escape
176
- if export_decode is not None:
177
- params["export_decode"] = export_decode
178
-
179
- response = self._get(self.base_url, params=params)
180
-
181
- return self._handle_response(response)
112
+ return self._build_params_and_get(
113
+ "domain_organic_unique",
114
+ domain=domain,
115
+ database=database,
116
+ display_limit=display_limit,
117
+ display_offset=display_offset,
118
+ display_date=display_date,
119
+ export_columns=export_columns,
120
+ display_sort=display_sort,
121
+ display_filter=display_filter,
122
+ export_escape=export_escape,
123
+ export_decode=export_decode,
124
+ )
182
125
 
183
126
  def domain_organic_search_keywords(
184
127
  self,
@@ -220,43 +163,25 @@ class SemrushApp(APIApplication):
220
163
  httpx.HTTPStatusError: If the API request fails
221
164
 
222
165
  Tags:
223
- domain-search
166
+ domain-search, important
224
167
  """
225
168
  if not domain:
226
169
  raise ValueError("Domain parameter is required")
227
-
228
- # Build parameters dictionary
229
- params = {
230
- "type": "domain_organic",
231
- "key": self.api_key,
232
- "domain": domain,
233
- "database": database,
234
- }
235
-
236
- if display_limit is not None:
237
- params["display_limit"] = display_limit
238
- if display_offset is not None:
239
- params["display_offset"] = display_offset
240
- if display_date is not None:
241
- params["display_date"] = display_date
242
- if display_daily is not None:
243
- params["display_daily"] = display_daily
244
- if export_columns is not None:
245
- params["export_columns"] = export_columns
246
- if display_sort is not None:
247
- params["display_sort"] = display_sort
248
- if display_positions is not None:
249
- params["display_positions"] = display_positions
250
- if display_positions_type is not None:
251
- params["display_positions_type"] = display_positions_type
252
- if display_filter is not None:
253
- params["display_filter"] = display_filter
254
- if export_escape is not None:
255
- params["export_escape"] = export_escape
256
-
257
- response = self._get(self.base_url, params=params)
258
-
259
- return self._handle_response(response)
170
+ return self._build_params_and_get(
171
+ "domain_organic",
172
+ domain=domain,
173
+ database=database,
174
+ display_limit=display_limit,
175
+ display_offset=display_offset,
176
+ display_date=display_date,
177
+ display_daily=display_daily,
178
+ export_columns=export_columns,
179
+ display_sort=display_sort,
180
+ display_positions=display_positions,
181
+ display_positions_type=display_positions_type,
182
+ display_filter=display_filter,
183
+ export_escape=export_escape,
184
+ )
260
185
 
261
186
  def domain_organic_subdomains(
262
187
  self,
@@ -272,57 +197,21 @@ class SemrushApp(APIApplication):
272
197
  ) -> dict[str, Any]:
273
198
  """
274
199
  Get subdomains of a domain that rank in Google's top 100 organic search results.
275
-
276
- Args:
277
- domain (str): Unique name of the website to investigate
278
- database (str): Regional database (default: "us")
279
- display_limit (int, optional): Number of results to return (max 100,000)
280
- display_offset (int, optional): Number of results to skip
281
- display_date (str, optional): Date in format "YYYYMM15" for historical data
282
- export_columns (str, optional): Comma-separated list of columns to include
283
- display_sort (str, optional): Sorting order (e.g., "pc_asc", "tg_desc")
284
- export_escape (int, optional): Set to 1 to wrap columns in quotes
285
- export_decode (int, optional): Set to 0 for URL-encoded response
286
-
287
- Returns:
288
- dict[str, Any]: API response data
289
-
290
- Raises:
291
- ValueError: If required parameters are missing
292
- httpx.HTTPStatusError: If the API request fails
293
-
294
- Tags:
295
- domain-search
296
200
  """
297
201
  if not domain:
298
202
  raise ValueError("Domain parameter is required")
299
-
300
- # Build parameters dictionary
301
- params = {
302
- "type": "domain_organic_subdomains",
303
- "key": self.api_key,
304
- "domain": domain,
305
- "database": database,
306
- }
307
-
308
- if display_limit is not None:
309
- params["display_limit"] = display_limit
310
- if display_offset is not None:
311
- params["display_offset"] = display_offset
312
- if display_date is not None:
313
- params["display_date"] = display_date
314
- if export_columns is not None:
315
- params["export_columns"] = export_columns
316
- if display_sort is not None:
317
- params["display_sort"] = display_sort
318
- if export_escape is not None:
319
- params["export_escape"] = export_escape
320
- if export_decode is not None:
321
- params["export_decode"] = export_decode
322
-
323
- response = self._get(self.base_url, params=params)
324
-
325
- return self._handle_response(response)
203
+ return self._build_params_and_get(
204
+ "domain_organic_subdomains",
205
+ domain=domain,
206
+ database=database,
207
+ display_limit=display_limit,
208
+ display_offset=display_offset,
209
+ display_date=display_date,
210
+ export_columns=export_columns,
211
+ display_sort=display_sort,
212
+ export_escape=export_escape,
213
+ export_decode=export_decode,
214
+ )
326
215
 
327
216
  def domain_paid_search_keywords(
328
217
  self,
@@ -340,63 +229,23 @@ class SemrushApp(APIApplication):
340
229
  ) -> dict[str, Any]:
341
230
  """
342
231
  Get keywords that bring paid traffic to a domain via Google's paid search results.
343
-
344
- Args:
345
- domain (str): Unique name of a website to investigate
346
- database (str): Regional database (default: "us")
347
- display_limit (int, optional): Number of results to return (max 100,000)
348
- display_offset (int, optional): Number of results to skip
349
- display_date (str, optional): Date in format "YYYYMM15" for historical data
350
- export_columns (str, optional): Comma-separated list of columns to include
351
- display_sort (str, optional): Sorting order (e.g., "po_asc", "tg_desc")
352
- display_positions (str, optional): Filter by position changes ("new", "lost", "rise", "fall")
353
- display_filter (str, optional): Filter criteria for columns
354
- export_escape (int, optional): Set to 1 to wrap columns in quotes
355
- export_decode (int, optional): Set to 0 for URL-encoded response
356
-
357
- Returns:
358
- dict[str, Any]: API response data
359
-
360
- Raises:
361
- ValueError: If required parameters are missing
362
- httpx.HTTPStatusError: If the API request fails
363
-
364
- Tags:
365
- domain-search
366
232
  """
367
233
  if not domain:
368
234
  raise ValueError("Domain parameter is required")
369
-
370
- # Build parameters dictionary
371
- params = {
372
- "type": "domain_adwords",
373
- "key": self.api_key,
374
- "domain": domain,
375
- "database": database,
376
- }
377
-
378
- if display_limit is not None:
379
- params["display_limit"] = display_limit
380
- if display_offset is not None:
381
- params["display_offset"] = display_offset
382
- if display_date is not None:
383
- params["display_date"] = display_date
384
- if export_columns is not None:
385
- params["export_columns"] = export_columns
386
- if display_sort is not None:
387
- params["display_sort"] = display_sort
388
- if display_positions is not None:
389
- params["display_positions"] = display_positions
390
- if display_filter is not None:
391
- params["display_filter"] = display_filter
392
- if export_escape is not None:
393
- params["export_escape"] = export_escape
394
- if export_decode is not None:
395
- params["export_decode"] = export_decode
396
-
397
- response = self._get(self.base_url, params=params)
398
-
399
- return self._handle_response(response)
235
+ return self._build_params_and_get(
236
+ "domain_adwords",
237
+ domain=domain,
238
+ database=database,
239
+ display_limit=display_limit,
240
+ display_offset=display_offset,
241
+ display_date=display_date,
242
+ export_columns=export_columns,
243
+ display_sort=display_sort,
244
+ display_positions=display_positions,
245
+ display_filter=display_filter,
246
+ export_escape=export_escape,
247
+ export_decode=export_decode,
248
+ )
400
249
 
401
250
  def domain_pla_search_keywords(
402
251
  self,
@@ -412,57 +261,21 @@ class SemrushApp(APIApplication):
412
261
  ) -> dict[str, Any]:
413
262
  """
414
263
  Get keywords that trigger a domain's product listing ads (PLA) in Google's paid search results.
415
-
416
- Args:
417
- domain (str): Unique name of a website to investigate
418
- database (str): Regional database (default: "us")
419
- display_limit (int, optional): Number of results to return (max 100,000)
420
- display_offset (int, optional): Number of results to skip
421
- export_columns (str, optional): Comma-separated list of columns to include
422
- display_sort (str, optional): Sorting order (e.g., "po_asc", "nq_desc")
423
- display_filter (str, optional): Filter criteria for columns
424
- export_escape (int, optional): Set to 1 to wrap columns in quotes
425
- export_decode (int, optional): Set to 0 for URL-encoded response
426
-
427
- Returns:
428
- dict[str, Any]: API response data
429
-
430
- Raises:
431
- ValueError: If required parameters are missing
432
- httpx.HTTPStatusError: If the API request fails
433
-
434
- Tags:
435
- domain-search
436
264
  """
437
265
  if not domain:
438
266
  raise ValueError("Domain parameter is required")
439
-
440
- # Build parameters dictionary
441
- params = {
442
- "type": "domain_shopping",
443
- "key": self.api_key,
444
- "domain": domain,
445
- "database": database,
446
- }
447
-
448
- if display_limit is not None:
449
- params["display_limit"] = display_limit
450
- if display_offset is not None:
451
- params["display_offset"] = display_offset
452
- if export_columns is not None:
453
- params["export_columns"] = export_columns
454
- if display_sort is not None:
455
- params["display_sort"] = display_sort
456
- if display_filter is not None:
457
- params["display_filter"] = display_filter
458
- if export_escape is not None:
459
- params["export_escape"] = export_escape
460
- if export_decode is not None:
461
- params["export_decode"] = export_decode
462
-
463
- response = self._get(self.base_url, params=params)
464
-
465
- return self._handle_response(response)
267
+ return self._build_params_and_get(
268
+ "domain_shopping",
269
+ domain=domain,
270
+ database=database,
271
+ display_limit=display_limit,
272
+ display_offset=display_offset,
273
+ export_columns=export_columns,
274
+ display_sort=display_sort,
275
+ display_filter=display_filter,
276
+ export_escape=export_escape,
277
+ export_decode=export_decode,
278
+ )
466
279
 
467
280
  def domain_vs_domain(
468
281
  self,
@@ -479,65 +292,22 @@ class SemrushApp(APIApplication):
479
292
  ) -> dict[str, Any]:
480
293
  """
481
294
  Compare up to five domains by common keywords, unique keywords, or search terms unique to the first domain.
482
-
483
- Args:
484
- domains (str): URL-encoded string containing domains in format: <sign>|<type>|<domain>
485
- Examples:
486
- - Shared keywords: "*|or|domain1|*|or|domain2|*|or|domain3"
487
- - All keywords: "*|or|domain1|+|or|domain2|+|or|domain3"
488
- - Unique keywords: "*|or|domain1|-|or|domain2|-|or|domain3"
489
- - Untapped keywords: "*|or|domain2|+|or|domain3|-|or|domain1"
490
- database (str): Regional database (default: "us")
491
- display_limit (int, optional): Number of results to return (max 100,000)
492
- display_offset (int, optional): Number of results to skip
493
- display_date (str, optional): Date in format "YYYYMM15" for historical data
494
- export_columns (str, optional): Comma-separated list of columns to include
495
- display_sort (str, optional): Sorting order (e.g., "p0_asc", "p1_desc")
496
- display_filter (str, optional): Filter criteria for columns
497
- export_escape (int, optional): Set to 1 to wrap columns in quotes
498
- export_decode (int, optional): Set to 0 for URL-encoded response
499
-
500
- Returns:
501
- dict[str, Any]: API response data
502
-
503
- Raises:
504
- ValueError: If required parameters are missing
505
- httpx.HTTPStatusError: If the API request fails
506
-
507
- Tags:
508
- domain-search
509
295
  """
510
296
  if not domains:
511
297
  raise ValueError("Domains parameter is required")
512
-
513
- # Build parameters dictionary
514
- params = {
515
- "type": "domain_domains",
516
- "key": self.api_key,
517
- "domains": domains,
518
- "database": database,
519
- }
520
-
521
- if display_limit is not None:
522
- params["display_limit"] = display_limit
523
- if display_offset is not None:
524
- params["display_offset"] = display_offset
525
- if display_date is not None:
526
- params["display_date"] = display_date
527
- if export_columns is not None:
528
- params["export_columns"] = export_columns
529
- if display_sort is not None:
530
- params["display_sort"] = display_sort
531
- if display_filter is not None:
532
- params["display_filter"] = display_filter
533
- if export_escape is not None:
534
- params["export_escape"] = export_escape
535
- if export_decode is not None:
536
- params["export_decode"] = export_decode
537
-
538
- response = self._get(self.base_url, params=params)
539
-
540
- return self._handle_response(response)
298
+ return self._build_params_and_get(
299
+ "domain_domains",
300
+ domains=domains,
301
+ database=database,
302
+ display_limit=display_limit,
303
+ display_offset=display_offset,
304
+ display_date=display_date,
305
+ export_columns=export_columns,
306
+ display_sort=display_sort,
307
+ display_filter=display_filter,
308
+ export_escape=export_escape,
309
+ export_decode=export_decode,
310
+ )
541
311
 
542
312
  def backlinks(
543
313
  self,
@@ -551,54 +321,19 @@ class SemrushApp(APIApplication):
551
321
  ) -> dict[str, Any]:
552
322
  """
553
323
  Get backlinks data for a domain, root domain, or URL.
554
-
555
- Args:
556
- target (str): Root domain, subdomain, or URL of the website to investigate
557
- target_type (str): Type of requested target (root_domain, domain, or url)
558
- export_columns (str, optional): Comma-separated list of columns to include
559
- display_sort (str, optional): Sorting order (e.g., "page_ascore_desc", "last_seen_asc")
560
- display_limit (int, optional): Number of results to return (max 1,000,000)
561
- display_offset (int, optional): Number of results to skip
562
- display_filter (str, optional): Filter criteria for columns
563
-
564
- Returns:
565
- dict[str, Any]: API response data
566
-
567
- Raises:
568
- ValueError: If required parameters are missing
569
- httpx.HTTPStatusError: If the API request fails
570
-
571
- Tags:
572
- backlinks
573
324
  """
574
- if not target:
575
- raise ValueError("Target parameter is required")
576
- if not target_type:
577
- raise ValueError("Target_type parameter is required")
578
-
579
- # Build parameters dictionary
580
- params = {
581
- "type": "backlinks",
582
- "key": self.api_key,
583
- "target": target,
584
- "target_type": target_type,
585
- }
586
-
587
- if export_columns is not None:
588
- params["export_columns"] = export_columns
589
- if display_sort is not None:
590
- params["display_sort"] = display_sort
591
- if display_limit is not None:
592
- params["display_limit"] = display_limit
593
- if display_offset is not None:
594
- params["display_offset"] = display_offset
595
- if display_filter is not None:
596
- params["display_filter"] = display_filter
597
-
598
- url = f"{self.base_url}/analytics/v1"
599
- response = self._get(url, params=params)
600
-
601
- return self._handle_response(response)
325
+ if not target or not target_type:
326
+ raise ValueError("Target and target_type parameters are required")
327
+ return self._build_params_and_get(
328
+ "backlinks_analytics",
329
+ target=target,
330
+ target_type=target_type,
331
+ export_columns=export_columns,
332
+ display_sort=display_sort,
333
+ display_limit=display_limit,
334
+ display_offset=display_offset,
335
+ display_filter=display_filter,
336
+ )
602
337
 
603
338
  def backlinks_overview(
604
339
  self,
@@ -612,54 +347,19 @@ class SemrushApp(APIApplication):
612
347
  ) -> dict[str, Any]:
613
348
  """
614
349
  Get backlinks overview summary including type, referring domains, and IP addresses.
615
-
616
- Args:
617
- target (str): Root domain, subdomain, or URL of the website to investigate
618
- target_type (str): Type of requested target (root_domain, domain, or url)
619
- export_columns (str, optional): Comma-separated list of columns to include
620
- display_sort (str, optional): Sorting order for results
621
- display_limit (int, optional): Number of results to return
622
- display_offset (int, optional): Number of results to skip
623
- display_filter (str, optional): Filter criteria for columns
624
-
625
- Returns:
626
- dict[str, Any]: API response data
627
-
628
- Raises:
629
- ValueError: If required parameters are missing
630
- httpx.HTTPStatusError: If the API request fails
631
-
632
- Tags:
633
- backlinks
634
350
  """
635
- if not target:
636
- raise ValueError("Target parameter is required")
637
- if not target_type:
638
- raise ValueError("Target_type parameter is required")
639
-
640
- # Build parameters dictionary
641
- params = {
642
- "type": "backlinks_overview",
643
- "key": self.api_key,
644
- "target": target,
645
- "target_type": target_type,
646
- }
647
-
648
- if export_columns is not None:
649
- params["export_columns"] = export_columns
650
- if display_sort is not None:
651
- params["display_sort"] = display_sort
652
- if display_limit is not None:
653
- params["display_limit"] = display_limit
654
- if display_offset is not None:
655
- params["display_offset"] = display_offset
656
- if display_filter is not None:
657
- params["display_filter"] = display_filter
658
-
659
- url = f"{self.base_url}/analytics/v1"
660
- response = self._get(url, params=params)
661
-
662
- return self._handle_response(response)
351
+ if not target or not target_type:
352
+ raise ValueError("Target and target_type parameters are required")
353
+ return self._build_params_and_get(
354
+ "backlinks_overview_analytics",
355
+ target=target,
356
+ target_type=target_type,
357
+ export_columns=export_columns,
358
+ display_sort=display_sort,
359
+ display_limit=display_limit,
360
+ display_offset=display_offset,
361
+ display_filter=display_filter,
362
+ )
663
363
 
664
364
  def keyword_difficulty(
665
365
  self,
@@ -670,42 +370,16 @@ class SemrushApp(APIApplication):
670
370
  ) -> dict[str, Any]:
671
371
  """
672
372
  Get keyword difficulty data to estimate ranking difficulty for organic search terms.
673
-
674
- Args:
675
- phrase (str): Phrase (from 1 to 100 keywords separated by semicolons)
676
- database (str): Regional database (default: "us")
677
- export_columns (str, optional): Comma-separated list of columns to include
678
- export_escape (int, optional): Set to 1 to wrap columns in quotes
679
-
680
- Returns:
681
- dict[str, Any]: API response data
682
-
683
- Raises:
684
- ValueError: If required parameters are missing
685
- httpx.HTTPStatusError: If the API request fails
686
-
687
- Tags:
688
- keyword-analysis, important
689
373
  """
690
374
  if not phrase:
691
375
  raise ValueError("Phrase parameter is required")
692
-
693
- # Build parameters dictionary
694
- params = {
695
- "type": "phrase_kdi",
696
- "key": self.api_key,
697
- "phrase": phrase,
698
- "database": database,
699
- }
700
-
701
- if export_columns is not None:
702
- params["export_columns"] = export_columns
703
- if export_escape is not None:
704
- params["export_escape"] = export_escape
705
-
706
- response = self._get(self.base_url, params=params)
707
-
708
- return self._handle_response(response)
376
+ return self._build_params_and_get(
377
+ "phrase_kdi",
378
+ phrase=phrase,
379
+ database=database,
380
+ export_columns=export_columns,
381
+ export_escape=export_escape,
382
+ )
709
383
 
710
384
  def ads_copies(
711
385
  self,
@@ -721,57 +395,21 @@ class SemrushApp(APIApplication):
721
395
  ) -> dict[str, Any]:
722
396
  """
723
397
  Get unique ad copies that appeared when a domain ranked in Google's paid search results.
724
-
725
- Args:
726
- domain (str): Unique name of a website to investigate
727
- database (str): Regional database (default: "us")
728
- display_limit (int, optional): Number of results to return (max 100,000)
729
- display_offset (int, optional): Number of results to skip
730
- export_columns (str, optional): Comma-separated list of columns to include
731
- display_sort (str, optional): Sorting order (e.g., "pc_asc", "pc_desc")
732
- display_filter (str, optional): Filter criteria for columns
733
- export_escape (int, optional): Set to 1 to wrap columns in quotes
734
- export_decode (int, optional): Set to 0 for URL-encoded response
735
-
736
- Returns:
737
- dict[str, Any]: API response data
738
-
739
- Raises:
740
- ValueError: If required parameters are missing
741
- httpx.HTTPStatusError: If the API request fails
742
-
743
- Tags:
744
- domain-search
745
- """
746
- if not domain:
747
- raise ValueError("Domain parameter is required")
748
-
749
- # Build parameters dictionary
750
- params = {
751
- "type": "domain_adwords_unique",
752
- "key": self.api_key,
753
- "domain": domain,
754
- "database": database,
755
- }
756
-
757
- if display_limit is not None:
758
- params["display_limit"] = display_limit
759
- if display_offset is not None:
760
- params["display_offset"] = display_offset
761
- if export_columns is not None:
762
- params["export_columns"] = export_columns
763
- if display_sort is not None:
764
- params["display_sort"] = display_sort
765
- if display_filter is not None:
766
- params["display_filter"] = display_filter
767
- if export_escape is not None:
768
- params["export_escape"] = export_escape
769
- if export_decode is not None:
770
- params["export_decode"] = export_decode
771
-
772
- response = self._get(self.base_url, params=params)
773
-
774
- return self._handle_response(response)
398
+ """
399
+ if not domain:
400
+ raise ValueError("Domain parameter is required")
401
+ return self._build_params_and_get(
402
+ "domain_adwords_unique",
403
+ domain=domain,
404
+ database=database,
405
+ display_limit=display_limit,
406
+ display_offset=display_offset,
407
+ export_columns=export_columns,
408
+ display_sort=display_sort,
409
+ display_filter=display_filter,
410
+ export_escape=export_escape,
411
+ export_decode=export_decode,
412
+ )
775
413
 
776
414
  def anchors(
777
415
  self,
@@ -784,87 +422,30 @@ class SemrushApp(APIApplication):
784
422
  ) -> dict[str, Any]:
785
423
  """
786
424
  Get anchor texts used in backlinks leading to a domain, root domain, or URL.
787
-
788
- Args:
789
- target (str): Root domain, subdomain, or URL of the website to investigate
790
- target_type (str): Type of requested target (root_domain, domain, or url)
791
- export_columns (str, optional): Comma-separated list of columns to include
792
- display_sort (str, optional): Sorting order (e.g., "domains_num_desc", "backlinks_num_asc")
793
- display_limit (int, optional): Number of results to return (default: 10,000)
794
- display_offset (int, optional): Number of results to skip
795
-
796
- Returns:
797
- dict[str, Any]: API response data
798
-
799
- Raises:
800
- ValueError: If required parameters are missing
801
- httpx.HTTPStatusError: If the API request fails
802
-
803
- Tags:
804
- backlinks
805
425
  """
806
- if not target:
807
- raise ValueError("Target parameter is required")
808
- if not target_type:
809
- raise ValueError("Target_type parameter is required")
810
-
811
- # Build parameters dictionary
812
- params = {
813
- "type": "backlinks_anchors",
814
- "key": self.api_key,
815
- "target": target,
816
- "target_type": target_type,
817
- }
818
-
819
- if export_columns is not None:
820
- params["export_columns"] = export_columns
821
- if display_sort is not None:
822
- params["display_sort"] = display_sort
823
- if display_limit is not None:
824
- params["display_limit"] = display_limit
825
- if display_offset is not None:
826
- params["display_offset"] = display_offset
827
-
828
- url = f"{self.base_url}/analytics/v1"
829
- response = self._get(url, params=params)
830
-
831
- return self._handle_response(response)
426
+ if not target or not target_type:
427
+ raise ValueError("Target and target_type parameters are required")
428
+ return self._build_params_and_get(
429
+ "backlinks_anchors_analytics",
430
+ target=target,
431
+ target_type=target_type,
432
+ export_columns=export_columns,
433
+ display_sort=display_sort,
434
+ display_limit=display_limit,
435
+ display_offset=display_offset,
436
+ )
832
437
 
833
438
  def authority_score_profile(self, target: str, target_type: str) -> dict[str, Any]:
834
439
  """
835
440
  Get distribution of referring domains by Authority Score from 0 to 100.
836
-
837
- Args:
838
- target (str): Root domain, subdomain, or URL of the website to investigate
839
- target_type (str): Type of requested target (root_domain, domain, or url)
840
-
841
- Returns:
842
- dict[str, Any]: API response data
843
-
844
- Raises:
845
- ValueError: If required parameters are missing
846
- httpx.HTTPStatusError: If the API request fails
847
-
848
- Tags:
849
- backlinks
850
441
  """
851
- if not target:
852
- raise ValueError("Target parameter is required")
853
- if not target_type:
854
- raise ValueError("Target_type parameter is required")
855
-
856
- # Build parameters dictionary
857
- params = {
858
- "type": "backlinks_ascore_profile",
859
- "key": self.api_key,
860
- "target": target,
861
- "target_type": target_type,
862
- }
863
-
864
- url = f"{self.base_url}/analytics/v1"
865
- response = self._get(url, params=params)
866
-
867
- return self._handle_response(response)
442
+ if not target or not target_type:
443
+ raise ValueError("Target and target_type parameters are required")
444
+ return self._build_params_and_get(
445
+ "backlinks_ascore_profile_analytics",
446
+ target=target,
447
+ target_type=target_type,
448
+ )
868
449
 
869
450
  def batch_comparison(
870
451
  self,
@@ -874,49 +455,21 @@ class SemrushApp(APIApplication):
874
455
  ) -> dict[str, Any]:
875
456
  """
876
457
  Compare backlink profiles and link-building progress across multiple competitors.
877
-
878
- Args:
879
- targets (list[str]): Array of root domains, subdomains, or URLs to investigate (max 200)
880
- target_types (list[str]): Array of target types corresponding to targets (root_domain, domain, or url)
881
- export_columns (str, optional): Comma-separated list of columns to include
882
-
883
- Returns:
884
- dict[str, Any]: API response data
885
-
886
- Raises:
887
- ValueError: If required parameters are missing or invalid
888
- httpx.HTTPStatusError: If the API request fails
889
-
890
- Tags:
891
- backlinks
892
458
  """
893
- if not targets:
894
- raise ValueError("Targets parameter is required")
895
- if not target_types:
896
- raise ValueError("Target_types parameter is required")
459
+ if not targets or not target_types:
460
+ raise ValueError("Targets and target_types parameters are required")
897
461
  if len(targets) > 200:
898
462
  raise ValueError("Maximum 200 targets allowed")
899
463
  if len(targets) != len(target_types):
900
464
  raise ValueError(
901
465
  "Targets and target_types arrays must have the same length"
902
466
  )
903
-
904
- # Build parameters dictionary
905
- params = {"type": "backlinks_comparison", "key": self.api_key}
906
-
907
- # Add targets and target_types as arrays
908
- for target in targets:
909
- params["targets[]"] = target
910
- for target_type in target_types:
911
- params["target_types[]"] = target_type
912
-
913
- if export_columns is not None:
914
- params["export_columns"] = export_columns
915
-
916
- url = f"{self.base_url}/analytics/v1"
917
- response = self._get(url, params=params)
918
-
919
- return self._handle_response(response)
467
+ return self._build_params_and_get(
468
+ "backlinks_comparison_analytics",
469
+ targets=targets,
470
+ target_types=target_types,
471
+ export_columns=export_columns,
472
+ )
920
473
 
921
474
  def batch_keyword_overview(
922
475
  self,
@@ -929,48 +482,18 @@ class SemrushApp(APIApplication):
929
482
  ) -> dict[str, Any]:
930
483
  """
931
484
  Get summary of up to 100 keywords including volume, CPC, competition level, and results count.
932
-
933
- Args:
934
- phrase (str): Keyword or keyword expression to investigate (up to 100 keywords separated by semicolons)
935
- database (str): Regional database (default: "us")
936
- export_escape (int, optional): Set to 1 to wrap columns in quotes
937
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
938
- display_date (str, optional): Date in format "YYYYMM15" for historical data
939
- export_columns (str, optional): Comma-separated list of columns to include
940
-
941
- Returns:
942
- dict[str, Any]: API response data
943
-
944
- Raises:
945
- ValueError: If required parameters are missing
946
- httpx.HTTPStatusError: If the API request fails
947
-
948
- Tags:
949
- keyword-analysis
950
485
  """
951
486
  if not phrase:
952
487
  raise ValueError("Phrase parameter is required")
953
-
954
- # Build parameters dictionary
955
- params = {
956
- "type": "phrase_these",
957
- "key": self.api_key,
958
- "phrase": phrase,
959
- "database": database,
960
- }
961
-
962
- if export_escape is not None:
963
- params["export_escape"] = export_escape
964
- if export_decode is not None:
965
- params["export_decode"] = export_decode
966
- if display_date is not None:
967
- params["display_date"] = display_date
968
- if export_columns is not None:
969
- params["export_columns"] = export_columns
970
-
971
- response = self._get(self.base_url, params=params)
972
-
973
- return self._handle_response(response)
488
+ return self._build_params_and_get(
489
+ "phrase_these",
490
+ phrase=phrase,
491
+ database=database,
492
+ export_escape=export_escape,
493
+ export_decode=export_decode,
494
+ display_date=display_date,
495
+ export_columns=export_columns,
496
+ )
974
497
 
975
498
  def broad_match_keyword(
976
499
  self,
@@ -986,99 +509,36 @@ class SemrushApp(APIApplication):
986
509
  ) -> dict[str, Any]:
987
510
  """
988
511
  Get broad matches and alternate search queries for a keyword or keyword expression.
989
-
990
- Args:
991
- phrase (str): Keyword or keyword expression to investigate
992
- database (str): Regional database (default: "us")
993
- display_limit (int, optional): Number of results to return (max 100,000)
994
- display_offset (int, optional): Number of results to skip
995
- export_escape (int, optional): Set to 1 to wrap columns in quotes
996
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
997
- export_columns (str, optional): Comma-separated list of columns to include
998
- display_sort (str, optional): Sorting order (e.g., "nq_desc", "cp_asc")
999
- display_filter (str, optional): Filter criteria for columns
1000
-
1001
- Returns:
1002
- dict[str, Any]: API response data
1003
-
1004
- Raises:
1005
- ValueError: If required parameters are missing
1006
- httpx.HTTPStatusError: If the API request fails
1007
-
1008
- Tags:
1009
- keyword-analysis
1010
512
  """
1011
513
  if not phrase:
1012
514
  raise ValueError("Phrase parameter is required")
1013
-
1014
- # Build parameters dictionary
1015
- params = {
1016
- "type": "phrase_fullsearch",
1017
- "key": self.api_key,
1018
- "phrase": phrase,
1019
- "database": database,
1020
- }
1021
-
1022
- if display_limit is not None:
1023
- params["display_limit"] = display_limit
1024
- if display_offset is not None:
1025
- params["display_offset"] = display_offset
1026
- if export_escape is not None:
1027
- params["export_escape"] = export_escape
1028
- if export_decode is not None:
1029
- params["export_decode"] = export_decode
1030
- if export_columns is not None:
1031
- params["export_columns"] = export_columns
1032
- if display_sort is not None:
1033
- params["display_sort"] = display_sort
1034
- if display_filter is not None:
1035
- params["display_filter"] = display_filter
1036
-
1037
- response = self._get(self.base_url, params=params)
1038
-
1039
- return self._handle_response(response)
515
+ return self._build_params_and_get(
516
+ "phrase_fullsearch",
517
+ phrase=phrase,
518
+ database=database,
519
+ display_limit=display_limit,
520
+ display_offset=display_offset,
521
+ export_escape=export_escape,
522
+ export_decode=export_decode,
523
+ export_columns=export_columns,
524
+ display_sort=display_sort,
525
+ display_filter=display_filter,
526
+ )
1040
527
 
1041
528
  def categories(
1042
529
  self, target: str, target_type: str, export_columns: str | None = None
1043
530
  ) -> dict[str, Any]:
1044
531
  """
1045
532
  Get list of categories that the queried domain belongs to with confidence ratings.
1046
-
1047
- Args:
1048
- target (str): Root domain, subdomain, or URL of the website to investigate
1049
- target_type (str): Type of requested target (root_domain, subdomain, or url)
1050
- export_columns (str, optional): Comma-separated list of columns to include
1051
-
1052
- Returns:
1053
- dict[str, Any]: API response data
1054
-
1055
- Raises:
1056
- ValueError: If required parameters are missing
1057
- httpx.HTTPStatusError: If the API request fails
1058
-
1059
- Tags:
1060
- backlinks
1061
533
  """
1062
- if not target:
1063
- raise ValueError("Target parameter is required")
1064
- if not target_type:
1065
- raise ValueError("Target_type parameter is required")
1066
-
1067
- # Build parameters dictionary
1068
- params = {
1069
- "type": "backlinks_categories",
1070
- "key": self.api_key,
1071
- "target": target,
1072
- "target_type": target_type,
1073
- }
1074
-
1075
- if export_columns is not None:
1076
- params["export_columns"] = export_columns
1077
-
1078
- url = f"{self.base_url}/analytics/v1"
1079
- response = self._get(url, params=params)
1080
-
1081
- return self._handle_response(response)
534
+ if not target or not target_type:
535
+ raise ValueError("Target and target_type parameters are required")
536
+ return self._build_params_and_get(
537
+ "backlinks_categories_analytics",
538
+ target=target,
539
+ target_type=target_type,
540
+ export_columns=export_columns,
541
+ )
1082
542
 
1083
543
  def categories_profile(
1084
544
  self,
@@ -1090,48 +550,17 @@ class SemrushApp(APIApplication):
1090
550
  ) -> dict[str, Any]:
1091
551
  """
1092
552
  Get categories that referring domains belong to with domain counts for the queried domain.
1093
-
1094
- Args:
1095
- target (str): Root domain, subdomain, or URL of the website to investigate
1096
- target_type (str): Type of requested target (root_domain, domain, or url)
1097
- export_columns (str, optional): Comma-separated list of columns to include
1098
- display_limit (int, optional): Number of results to return (default: 10,000)
1099
- display_offset (int, optional): Number of results to skip
1100
-
1101
- Returns:
1102
- dict[str, Any]: API response data
1103
-
1104
- Raises:
1105
- ValueError: If required parameters are missing
1106
- httpx.HTTPStatusError: If the API request fails
1107
-
1108
- Tags:
1109
- backlinks
1110
553
  """
1111
- if not target:
1112
- raise ValueError("Target parameter is required")
1113
- if not target_type:
1114
- raise ValueError("Target_type parameter is required")
1115
-
1116
- # Build parameters dictionary
1117
- params = {
1118
- "type": "backlinks_categories_profile",
1119
- "key": self.api_key,
1120
- "target": target,
1121
- "target_type": target_type,
1122
- }
1123
-
1124
- if export_columns is not None:
1125
- params["export_columns"] = export_columns
1126
- if display_limit is not None:
1127
- params["display_limit"] = display_limit
1128
- if display_offset is not None:
1129
- params["display_offset"] = display_offset
1130
-
1131
- url = f"{self.base_url}/analytics/v1"
1132
- response = self._get(url, params=params)
1133
-
1134
- return self._handle_response(response)
554
+ if not target or not target_type:
555
+ raise ValueError("Target and target_type parameters are required")
556
+ return self._build_params_and_get(
557
+ "backlinks_categories_profile_analytics",
558
+ target=target,
559
+ target_type=target_type,
560
+ export_columns=export_columns,
561
+ display_limit=display_limit,
562
+ display_offset=display_offset,
563
+ )
1135
564
 
1136
565
  def competitors(
1137
566
  self,
@@ -1143,48 +572,17 @@ class SemrushApp(APIApplication):
1143
572
  ) -> dict[str, Any]:
1144
573
  """
1145
574
  Get domains that share a similar backlink profile with the analyzed domain.
1146
-
1147
- Args:
1148
- target (str): Root domain, subdomain, or URL of the website to investigate
1149
- target_type (str): Type of requested target (root_domain, domain, or url)
1150
- export_columns (str, optional): Comma-separated list of columns to include
1151
- display_limit (int, optional): Number of results to return (default: 10,000)
1152
- display_offset (int, optional): Number of results to skip
1153
-
1154
- Returns:
1155
- dict[str, Any]: API response data
1156
-
1157
- Raises:
1158
- ValueError: If required parameters are missing
1159
- httpx.HTTPStatusError: If the API request fails
1160
-
1161
- Tags:
1162
- backlinks
1163
575
  """
1164
- if not target:
1165
- raise ValueError("Target parameter is required")
1166
- if not target_type:
1167
- raise ValueError("Target_type parameter is required")
1168
-
1169
- # Build parameters dictionary
1170
- params = {
1171
- "type": "backlinks_competitors",
1172
- "key": self.api_key,
1173
- "target": target,
1174
- "target_type": target_type,
1175
- }
1176
-
1177
- if export_columns is not None:
1178
- params["export_columns"] = export_columns
1179
- if display_limit is not None:
1180
- params["display_limit"] = display_limit
1181
- if display_offset is not None:
1182
- params["display_offset"] = display_offset
1183
-
1184
- url = f"{self.base_url}/analytics/v1"
1185
- response = self._get(url, params=params)
1186
-
1187
- return self._handle_response(response)
576
+ if not target or not target_type:
577
+ raise ValueError("Target and target_type parameters are required")
578
+ return self._build_params_and_get(
579
+ "backlinks_competitors_analytics",
580
+ target=target,
581
+ target_type=target_type,
582
+ export_columns=export_columns,
583
+ display_limit=display_limit,
584
+ display_offset=display_offset,
585
+ )
1188
586
 
1189
587
  def competitors_organic_search(
1190
588
  self,
@@ -1200,57 +598,21 @@ class SemrushApp(APIApplication):
1200
598
  ) -> dict[str, Any]:
1201
599
  """
1202
600
  Get domain's competitors in organic search results with common keywords analysis.
1203
-
1204
- Args:
1205
- domain (str): Unique name of the website to investigate
1206
- database (str): Regional database (default: "us")
1207
- display_limit (int, optional): Number of results to return (max 100,000)
1208
- display_offset (int, optional): Number of results to skip
1209
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1210
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1211
- display_date (str, optional): Date in format "YYYYMM15" for historical data
1212
- export_columns (str, optional): Comma-separated list of columns to include
1213
- display_sort (str, optional): Sorting order (e.g., "np_desc", "cr_asc")
1214
-
1215
- Returns:
1216
- dict[str, Any]: API response data
1217
-
1218
- Raises:
1219
- ValueError: If required parameters are missing
1220
- httpx.HTTPStatusError: If the API request fails
1221
-
1222
- Tags:
1223
- domain-search
1224
601
  """
1225
602
  if not domain:
1226
603
  raise ValueError("Domain parameter is required")
1227
-
1228
- # Build parameters dictionary
1229
- params = {
1230
- "type": "domain_organic_organic",
1231
- "key": self.api_key,
1232
- "domain": domain,
1233
- "database": database,
1234
- }
1235
-
1236
- if display_limit is not None:
1237
- params["display_limit"] = display_limit
1238
- if display_offset is not None:
1239
- params["display_offset"] = display_offset
1240
- if export_escape is not None:
1241
- params["export_escape"] = export_escape
1242
- if export_decode is not None:
1243
- params["export_decode"] = export_decode
1244
- if display_date is not None:
1245
- params["display_date"] = display_date
1246
- if export_columns is not None:
1247
- params["export_columns"] = export_columns
1248
- if display_sort is not None:
1249
- params["display_sort"] = display_sort
1250
-
1251
- response = self._get(self.base_url, params=params)
1252
-
1253
- return self._handle_response(response)
604
+ return self._build_params_and_get(
605
+ "domain_organic_organic",
606
+ domain=domain,
607
+ database=database,
608
+ display_limit=display_limit,
609
+ display_offset=display_offset,
610
+ export_escape=export_escape,
611
+ export_decode=export_decode,
612
+ display_date=display_date,
613
+ export_columns=export_columns,
614
+ display_sort=display_sort,
615
+ )
1254
616
 
1255
617
  def competitors_paid_search(
1256
618
  self,
@@ -1266,57 +628,21 @@ class SemrushApp(APIApplication):
1266
628
  ) -> dict[str, Any]:
1267
629
  """
1268
630
  Get domain's competitors in paid search results with common keywords analysis.
1269
-
1270
- Args:
1271
- domain (str): Unique name of a website to investigate
1272
- database (str): Regional database (default: "us")
1273
- display_limit (int, optional): Number of results to return (max 100,000)
1274
- display_offset (int, optional): Number of results to skip
1275
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1276
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1277
- display_date (str, optional): Date in format "YYYYMM15" for historical data
1278
- export_columns (str, optional): Comma-separated list of columns to include
1279
- display_sort (str, optional): Sorting order (e.g., "np_desc", "cr_asc")
1280
-
1281
- Returns:
1282
- dict[str, Any]: API response data
1283
-
1284
- Raises:
1285
- ValueError: If required parameters are missing
1286
- httpx.HTTPStatusError: If the API request fails
1287
-
1288
- Tags:
1289
- domain-search
1290
631
  """
1291
632
  if not domain:
1292
633
  raise ValueError("Domain parameter is required")
1293
-
1294
- # Build parameters dictionary
1295
- params = {
1296
- "type": "domain_adwords_adwords",
1297
- "key": self.api_key,
1298
- "domain": domain,
1299
- "database": database,
1300
- }
1301
-
1302
- if display_limit is not None:
1303
- params["display_limit"] = display_limit
1304
- if display_offset is not None:
1305
- params["display_offset"] = display_offset
1306
- if export_escape is not None:
1307
- params["export_escape"] = export_escape
1308
- if export_decode is not None:
1309
- params["export_decode"] = export_decode
1310
- if display_date is not None:
1311
- params["display_date"] = display_date
1312
- if export_columns is not None:
1313
- params["export_columns"] = export_columns
1314
- if display_sort is not None:
1315
- params["display_sort"] = display_sort
1316
-
1317
- response = self._get(self.base_url, params=params)
1318
-
1319
- return self._handle_response(response)
634
+ return self._build_params_and_get(
635
+ "domain_adwords_adwords",
636
+ domain=domain,
637
+ database=database,
638
+ display_limit=display_limit,
639
+ display_offset=display_offset,
640
+ export_escape=export_escape,
641
+ export_decode=export_decode,
642
+ display_date=display_date,
643
+ export_columns=export_columns,
644
+ display_sort=display_sort,
645
+ )
1320
646
 
1321
647
  def indexed_pages(
1322
648
  self,
@@ -1329,51 +655,18 @@ class SemrushApp(APIApplication):
1329
655
  ) -> dict[str, Any]:
1330
656
  """
1331
657
  Get indexed pages of the queried domain with backlink and response data.
1332
-
1333
- Args:
1334
- target (str): Root domain, subdomain, or URL of the website to investigate
1335
- target_type (str): Type of requested target (root_domain, domain, or url)
1336
- export_columns (str, optional): Comma-separated list of columns to include
1337
- display_sort (str, optional): Sorting order (e.g., "backlinks_num_desc", "backlinks_num_asc")
1338
- display_limit (int, optional): Number of results to return (default: 10,000)
1339
- display_offset (int, optional): Number of results to skip
1340
-
1341
- Returns:
1342
- dict[str, Any]: API response data
1343
-
1344
- Raises:
1345
- ValueError: If required parameters are missing
1346
- httpx.HTTPStatusError: If the API request fails
1347
-
1348
- Tags:
1349
- backlinks
1350
658
  """
1351
- if not target:
1352
- raise ValueError("Target parameter is required")
1353
- if not target_type:
1354
- raise ValueError("Target_type parameter is required")
1355
-
1356
- # Build parameters dictionary
1357
- params = {
1358
- "type": "backlinks_pages",
1359
- "key": self.api_key,
1360
- "target": target,
1361
- "target_type": target_type,
1362
- }
1363
-
1364
- if export_columns is not None:
1365
- params["export_columns"] = export_columns
1366
- if display_sort is not None:
1367
- params["display_sort"] = display_sort
1368
- if display_limit is not None:
1369
- params["display_limit"] = display_limit
1370
- if display_offset is not None:
1371
- params["display_offset"] = display_offset
1372
-
1373
- url = f"{self.base_url}/analytics/v1"
1374
- response = self._get(url, params=params)
1375
-
1376
- return self._handle_response(response)
659
+ if not target or not target_type:
660
+ raise ValueError("Target and target_type parameters are required")
661
+ return self._build_params_and_get(
662
+ "backlinks_pages_analytics",
663
+ target=target,
664
+ target_type=target_type,
665
+ export_columns=export_columns,
666
+ display_sort=display_sort,
667
+ display_limit=display_limit,
668
+ display_offset=display_offset,
669
+ )
1377
670
 
1378
671
  def keyword_ads_history(
1379
672
  self,
@@ -1387,98 +680,41 @@ class SemrushApp(APIApplication):
1387
680
  ) -> dict[str, Any]:
1388
681
  """
1389
682
  Get domains that have bid on a keyword in the last 12 months with their paid search positions.
1390
-
1391
- Args:
1392
- phrase (str): Keyword or keyword expression to investigate
1393
- database (str): Regional database (default: "us")
1394
- display_limit (int, optional): Number of results to return (max 100,000)
1395
- display_offset (int, optional): Number of results to skip
1396
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1397
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1398
- export_columns (str, optional): Comma-separated list of columns to include
1399
-
1400
- Returns:
1401
- dict[str, Any]: API response data
1402
-
1403
- Raises:
1404
- ValueError: If required parameters are missing
1405
- httpx.HTTPStatusError: If the API request fails
1406
-
1407
- Tags:
1408
- keyword-analysis
1409
683
  """
1410
684
  if not phrase:
1411
685
  raise ValueError("Phrase parameter is required")
1412
-
1413
- # Build parameters dictionary
1414
- params = {
1415
- "type": "phrase_adwords_historical",
1416
- "key": self.api_key,
1417
- "phrase": phrase,
1418
- "database": database,
1419
- }
1420
-
1421
- if display_limit is not None:
1422
- params["display_limit"] = display_limit
1423
- if display_offset is not None:
1424
- params["display_offset"] = display_offset
1425
- if export_escape is not None:
1426
- params["export_escape"] = export_escape
1427
- if export_decode is not None:
1428
- params["export_decode"] = export_decode
1429
- if export_columns is not None:
1430
- params["export_columns"] = export_columns
1431
-
1432
- response = self._get(self.base_url, params=params)
1433
-
1434
- return self._handle_response(response)
686
+ return self._build_params_and_get(
687
+ "phrase_adwords_historical",
688
+ phrase=phrase,
689
+ database=database,
690
+ display_limit=display_limit,
691
+ display_offset=display_offset,
692
+ export_escape=export_escape,
693
+ export_decode=export_decode,
694
+ export_columns=export_columns,
695
+ )
1435
696
 
1436
697
  def keyword_overview_all_databases(
1437
698
  self,
1438
699
  phrase: str,
1439
700
  database: str | None = None,
1440
701
  export_escape: int | None = None,
1441
- export_decode: int | None = None,
1442
- export_columns: str | None = None,
1443
- ) -> dict[str, Any]:
1444
- """
1445
- Get keyword summary including volume, CPC, competition level, and results across all regional databases.
1446
-
1447
- Args:
1448
- phrase (str): Keyword or keyword expression to investigate
1449
- database (str, optional): Regional database (if not specified, uses all databases)
1450
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1451
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1452
- export_columns (str, optional): Comma-separated list of columns to include
1453
-
1454
- Returns:
1455
- dict[str, Any]: API response data
1456
-
1457
- Raises:
1458
- ValueError: If required parameters are missing
1459
- httpx.HTTPStatusError: If the API request fails
1460
-
1461
- Tags:
1462
- keyword-analysis
702
+ export_decode: int | None = None,
703
+ export_columns: str | None = None,
704
+ ) -> dict[str, Any]:
705
+ """
706
+ Get keyword summary including volume, CPC, competition level, and results across all regional databases.
1463
707
  """
1464
708
  if not phrase:
1465
709
  raise ValueError("Phrase parameter is required")
1466
-
1467
- # Build parameters dictionary
1468
- params = {"type": "phrase_all", "key": self.api_key, "phrase": phrase}
1469
-
1470
- if database is not None:
1471
- params["database"] = database
1472
- if export_escape is not None:
1473
- params["export_escape"] = export_escape
1474
- if export_decode is not None:
1475
- params["export_decode"] = export_decode
1476
- if export_columns is not None:
1477
- params["export_columns"] = export_columns
1478
-
1479
- response = self._get(self.base_url, params=params)
1480
-
1481
- return self._handle_response(response)
710
+ return self._build_params_and_get(
711
+ "phrase_all",
712
+ phrase=phrase,
713
+ database=database,
714
+ export_escape=export_escape,
715
+ export_decode=export_decode,
716
+ export_columns=export_columns,
717
+ )
1482
718
 
1483
719
  def keyword_overview_one_database(
1484
720
  self,
@@ -1491,48 +727,18 @@ class SemrushApp(APIApplication):
1491
727
  ) -> dict[str, Any]:
1492
728
  """
1493
729
  Get keyword summary including volume, CPC, competition level, and results for a specific database.
1494
-
1495
- Args:
1496
- phrase (str): Keyword or keyword expression to investigate
1497
- database (str): Regional database (default: "us")
1498
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1499
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1500
- display_date (str, optional): Date in format "YYYYMM15" for historical data
1501
- export_columns (str, optional): Comma-separated list of columns to include
1502
-
1503
- Returns:
1504
- dict[str, Any]: API response data
1505
-
1506
- Raises:
1507
- ValueError: If required parameters are missing
1508
- httpx.HTTPStatusError: If the API request fails
1509
-
1510
- Tags:
1511
- keyword-analysis
1512
730
  """
1513
731
  if not phrase:
1514
732
  raise ValueError("Phrase parameter is required")
1515
-
1516
- # Build parameters dictionary
1517
- params = {
1518
- "type": "phrase_this",
1519
- "key": self.api_key,
1520
- "phrase": phrase,
1521
- "database": database,
1522
- }
1523
-
1524
- if export_escape is not None:
1525
- params["export_escape"] = export_escape
1526
- if export_decode is not None:
1527
- params["export_decode"] = export_decode
1528
- if display_date is not None:
1529
- params["display_date"] = display_date
1530
- if export_columns is not None:
1531
- params["export_columns"] = export_columns
1532
-
1533
- response = self._get(self.base_url, params=params)
1534
-
1535
- return self._handle_response(response)
733
+ return self._build_params_and_get(
734
+ "phrase_this",
735
+ phrase=phrase,
736
+ database=database,
737
+ export_escape=export_escape,
738
+ export_decode=export_decode,
739
+ display_date=display_date,
740
+ export_columns=export_columns,
741
+ )
1536
742
 
1537
743
  def organic_results(
1538
744
  self,
@@ -1548,57 +754,21 @@ class SemrushApp(APIApplication):
1548
754
  ) -> dict[str, Any]:
1549
755
  """
1550
756
  Get domains ranking in Google's top 100 organic search results for a keyword.
1551
-
1552
- Args:
1553
- phrase (str): Keyword or keyword expression to investigate
1554
- database (str): Regional database (default: "us")
1555
- display_limit (int, optional): Number of results to return (max 100,000)
1556
- display_offset (int, optional): Number of results to skip
1557
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1558
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1559
- display_date (str, optional): Date in format "YYYYMM15" for historical data
1560
- export_columns (str, optional): Comma-separated list of columns to include
1561
- positions_type (str, optional): Position type ("organic" or "all")
1562
-
1563
- Returns:
1564
- dict[str, Any]: API response data
1565
-
1566
- Raises:
1567
- ValueError: If required parameters are missing
1568
- httpx.HTTPStatusError: If the API request fails
1569
-
1570
- Tags:
1571
- keyword-analysis
1572
757
  """
1573
758
  if not phrase:
1574
759
  raise ValueError("Phrase parameter is required")
1575
-
1576
- # Build parameters dictionary
1577
- params = {
1578
- "type": "phrase_organic",
1579
- "key": self.api_key,
1580
- "phrase": phrase,
1581
- "database": database,
1582
- }
1583
-
1584
- if display_limit is not None:
1585
- params["display_limit"] = display_limit
1586
- if display_offset is not None:
1587
- params["display_offset"] = display_offset
1588
- if export_escape is not None:
1589
- params["export_escape"] = export_escape
1590
- if export_decode is not None:
1591
- params["export_decode"] = export_decode
1592
- if display_date is not None:
1593
- params["display_date"] = display_date
1594
- if export_columns is not None:
1595
- params["export_columns"] = export_columns
1596
- if positions_type is not None:
1597
- params["positions_type"] = positions_type
1598
-
1599
- response = self._get(self.base_url, params=params)
1600
-
1601
- return self._handle_response(response)
760
+ return self._build_params_and_get(
761
+ "phrase_organic",
762
+ phrase=phrase,
763
+ database=database,
764
+ display_limit=display_limit,
765
+ display_offset=display_offset,
766
+ export_escape=export_escape,
767
+ export_decode=export_decode,
768
+ display_date=display_date,
769
+ export_columns=export_columns,
770
+ positions_type=positions_type,
771
+ )
1602
772
 
1603
773
  def paid_results(
1604
774
  self,
@@ -1613,54 +783,20 @@ class SemrushApp(APIApplication):
1613
783
  ) -> dict[str, Any]:
1614
784
  """
1615
785
  Get domains ranking in Google's paid search results for a keyword.
1616
-
1617
- Args:
1618
- phrase (str): Keyword or keyword expression to investigate
1619
- database (str): Regional database (default: "us")
1620
- display_limit (int, optional): Number of results to return (max 100,000)
1621
- display_offset (int, optional): Number of results to skip
1622
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1623
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1624
- display_date (str, optional): Date in format "YYYYMM15" for historical data
1625
- export_columns (str, optional): Comma-separated list of columns to include
1626
-
1627
- Returns:
1628
- dict[str, Any]: API response data
1629
-
1630
- Raises:
1631
- ValueError: If required parameters are missing
1632
- httpx.HTTPStatusError: If the API request fails
1633
-
1634
- Tags:
1635
- keyword-analysis
1636
786
  """
1637
787
  if not phrase:
1638
788
  raise ValueError("Phrase parameter is required")
1639
-
1640
- # Build parameters dictionary
1641
- params = {
1642
- "type": "phrase_adwords",
1643
- "key": self.api_key,
1644
- "phrase": phrase,
1645
- "database": database,
1646
- }
1647
-
1648
- if display_limit is not None:
1649
- params["display_limit"] = display_limit
1650
- if display_offset is not None:
1651
- params["display_offset"] = display_offset
1652
- if export_escape is not None:
1653
- params["export_escape"] = export_escape
1654
- if export_decode is not None:
1655
- params["export_decode"] = export_decode
1656
- if display_date is not None:
1657
- params["display_date"] = display_date
1658
- if export_columns is not None:
1659
- params["export_columns"] = export_columns
1660
-
1661
- response = self._get(self.base_url, params=params)
1662
-
1663
- return self._handle_response(response)
789
+ return self._build_params_and_get(
790
+ "phrase_adwords",
791
+ phrase=phrase,
792
+ database=database,
793
+ display_limit=display_limit,
794
+ display_offset=display_offset,
795
+ export_escape=export_escape,
796
+ export_decode=export_decode,
797
+ display_date=display_date,
798
+ export_columns=export_columns,
799
+ )
1664
800
 
1665
801
  def phrase_questions(
1666
802
  self,
@@ -1676,57 +812,21 @@ class SemrushApp(APIApplication):
1676
812
  ) -> dict[str, Any]:
1677
813
  """
1678
814
  Get phrase questions relevant to a queried term in a chosen database.
1679
-
1680
- Args:
1681
- phrase (str): Keyword or keyword expression to investigate
1682
- database (str): Regional database (default: "us")
1683
- display_limit (int, optional): Number of results to return (max 100,000)
1684
- display_offset (int, optional): Number of results to skip
1685
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1686
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1687
- export_columns (str, optional): Comma-separated list of columns to include
1688
- display_sort (str, optional): Sorting order (e.g., "nq_desc", "cp_asc")
1689
- display_filter (str, optional): Filter criteria for columns
1690
-
1691
- Returns:
1692
- dict[str, Any]: API response data
1693
-
1694
- Raises:
1695
- ValueError: If required parameters are missing
1696
- httpx.HTTPStatusError: If the API request fails
1697
-
1698
- Tags:
1699
- keyword-analysis
1700
815
  """
1701
816
  if not phrase:
1702
817
  raise ValueError("Phrase parameter is required")
1703
-
1704
- # Build parameters dictionary
1705
- params = {
1706
- "type": "phrase_questions",
1707
- "key": self.api_key,
1708
- "phrase": phrase,
1709
- "database": database,
1710
- }
1711
-
1712
- if display_limit is not None:
1713
- params["display_limit"] = display_limit
1714
- if display_offset is not None:
1715
- params["display_offset"] = display_offset
1716
- if export_escape is not None:
1717
- params["export_escape"] = export_escape
1718
- if export_decode is not None:
1719
- params["export_decode"] = export_decode
1720
- if export_columns is not None:
1721
- params["export_columns"] = export_columns
1722
- if display_sort is not None:
1723
- params["display_sort"] = display_sort
1724
- if display_filter is not None:
1725
- params["display_filter"] = display_filter
1726
-
1727
- response = self._get(self.base_url, params=params)
1728
-
1729
- return self._handle_response(response)
818
+ return self._build_params_and_get(
819
+ "phrase_questions",
820
+ phrase=phrase,
821
+ database=database,
822
+ display_limit=display_limit,
823
+ display_offset=display_offset,
824
+ export_escape=export_escape,
825
+ export_decode=export_decode,
826
+ export_columns=export_columns,
827
+ display_sort=display_sort,
828
+ display_filter=display_filter,
829
+ )
1730
830
 
1731
831
  def pla_competitors(
1732
832
  self,
@@ -1741,54 +841,20 @@ class SemrushApp(APIApplication):
1741
841
  ) -> dict[str, Any]:
1742
842
  """
1743
843
  Get domains competing against the requested domain in Google's Product Listing Ads (PLA).
1744
-
1745
- Args:
1746
- domain (str): Unique name of a website to investigate
1747
- database (str): Regional database (default: "us")
1748
- display_limit (int, optional): Number of results to return (max 100,000)
1749
- display_offset (int, optional): Number of results to skip
1750
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1751
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1752
- export_columns (str, optional): Comma-separated list of columns to include
1753
- display_sort (str, optional): Sorting order (e.g., "np_desc", "cr_asc")
1754
-
1755
- Returns:
1756
- dict[str, Any]: API response data
1757
-
1758
- Raises:
1759
- ValueError: If required parameters are missing
1760
- httpx.HTTPStatusError: If the API request fails
1761
-
1762
- Tags:
1763
- domain-search
1764
844
  """
1765
845
  if not domain:
1766
846
  raise ValueError("Domain parameter is required")
1767
-
1768
- # Build parameters dictionary
1769
- params = {
1770
- "type": "domain_shopping_shopping",
1771
- "key": self.api_key,
1772
- "domain": domain,
1773
- "database": database,
1774
- }
1775
-
1776
- if display_limit is not None:
1777
- params["display_limit"] = display_limit
1778
- if display_offset is not None:
1779
- params["display_offset"] = display_offset
1780
- if export_escape is not None:
1781
- params["export_escape"] = export_escape
1782
- if export_decode is not None:
1783
- params["export_decode"] = export_decode
1784
- if export_columns is not None:
1785
- params["export_columns"] = export_columns
1786
- if display_sort is not None:
1787
- params["display_sort"] = display_sort
1788
-
1789
- response = self._get(self.base_url, params=params)
1790
-
1791
- return self._handle_response(response)
847
+ return self._build_params_and_get(
848
+ "domain_shopping_shopping",
849
+ domain=domain,
850
+ database=database,
851
+ display_limit=display_limit,
852
+ display_offset=display_offset,
853
+ export_escape=export_escape,
854
+ export_decode=export_decode,
855
+ export_columns=export_columns,
856
+ display_sort=display_sort,
857
+ )
1792
858
 
1793
859
  def pla_copies(
1794
860
  self,
@@ -1804,57 +870,21 @@ class SemrushApp(APIApplication):
1804
870
  ) -> dict[str, Any]:
1805
871
  """
1806
872
  Get product listing ad (PLA) copies that appeared when a domain ranked in Google's paid search results.
1807
-
1808
- Args:
1809
- domain (str): Unique name of a website to investigate
1810
- database (str): Regional database (default: "us")
1811
- display_limit (int, optional): Number of results to return (max 100,000)
1812
- display_offset (int, optional): Number of results to skip
1813
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1814
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1815
- export_columns (str, optional): Comma-separated list of columns to include
1816
- display_sort (str, optional): Sorting order (e.g., "pr_desc", "pc_asc")
1817
- display_filter (str, optional): Filter criteria for columns
1818
-
1819
- Returns:
1820
- dict[str, Any]: API response data
1821
-
1822
- Raises:
1823
- ValueError: If required parameters are missing
1824
- httpx.HTTPStatusError: If the API request fails
1825
-
1826
- Tags:
1827
- domain-search
1828
873
  """
1829
874
  if not domain:
1830
875
  raise ValueError("Domain parameter is required")
1831
-
1832
- # Build parameters dictionary
1833
- params = {
1834
- "type": "domain_shopping_unique",
1835
- "key": self.api_key,
1836
- "domain": domain,
1837
- "database": database,
1838
- }
1839
-
1840
- if display_limit is not None:
1841
- params["display_limit"] = display_limit
1842
- if display_offset is not None:
1843
- params["display_offset"] = display_offset
1844
- if export_escape is not None:
1845
- params["export_escape"] = export_escape
1846
- if export_decode is not None:
1847
- params["export_decode"] = export_decode
1848
- if export_columns is not None:
1849
- params["export_columns"] = export_columns
1850
- if display_sort is not None:
1851
- params["display_sort"] = display_sort
1852
- if display_filter is not None:
1853
- params["display_filter"] = display_filter
1854
-
1855
- response = self._get(self.base_url, params=params)
1856
-
1857
- return self._handle_response(response)
876
+ return self._build_params_and_get(
877
+ "domain_shopping_unique",
878
+ domain=domain,
879
+ database=database,
880
+ display_limit=display_limit,
881
+ display_offset=display_offset,
882
+ export_escape=export_escape,
883
+ export_decode=export_decode,
884
+ export_columns=export_columns,
885
+ display_sort=display_sort,
886
+ display_filter=display_filter,
887
+ )
1858
888
 
1859
889
  def referring_domains(
1860
890
  self,
@@ -1868,54 +898,19 @@ class SemrushApp(APIApplication):
1868
898
  ) -> dict[str, Any]:
1869
899
  """
1870
900
  Get domains pointing to the queried domain, root domain, or URL.
1871
-
1872
- Args:
1873
- target (str): Root domain, subdomain, or URL of the website to investigate
1874
- target_type (str): Type of requested target (root_domain, domain, or url)
1875
- export_columns (str, optional): Comma-separated list of columns to include
1876
- display_sort (str, optional): Sorting order (e.g., "domain_ascore_desc", "backlinks_num_asc")
1877
- display_limit (int, optional): Number of results to return (default: 10,000)
1878
- display_offset (int, optional): Number of results to skip
1879
- display_filter (str, optional): Filter criteria for columns
1880
-
1881
- Returns:
1882
- dict[str, Any]: API response data
1883
-
1884
- Raises:
1885
- ValueError: If required parameters are missing
1886
- httpx.HTTPStatusError: If the API request fails
1887
-
1888
- Tags:
1889
- backlinks
1890
901
  """
1891
- if not target:
1892
- raise ValueError("Target parameter is required")
1893
- if not target_type:
1894
- raise ValueError("Target_type parameter is required")
1895
-
1896
- # Build parameters dictionary
1897
- params = {
1898
- "type": "backlinks_refdomains",
1899
- "key": self.api_key,
1900
- "target": target,
1901
- "target_type": target_type,
1902
- }
1903
-
1904
- if export_columns is not None:
1905
- params["export_columns"] = export_columns
1906
- if display_sort is not None:
1907
- params["display_sort"] = display_sort
1908
- if display_limit is not None:
1909
- params["display_limit"] = display_limit
1910
- if display_offset is not None:
1911
- params["display_offset"] = display_offset
1912
- if display_filter is not None:
1913
- params["display_filter"] = display_filter
1914
-
1915
- url = f"{self.base_url}/analytics/v1"
1916
- response = self._get(url, params=params)
1917
-
1918
- return self._handle_response(response)
902
+ if not target or not target_type:
903
+ raise ValueError("Target and target_type parameters are required")
904
+ return self._build_params_and_get(
905
+ "backlinks_refdomains_analytics",
906
+ target=target,
907
+ target_type=target_type,
908
+ export_columns=export_columns,
909
+ display_sort=display_sort,
910
+ display_limit=display_limit,
911
+ display_offset=display_offset,
912
+ display_filter=display_filter,
913
+ )
1919
914
 
1920
915
  def referring_domains_by_country(
1921
916
  self,
@@ -1928,51 +923,18 @@ class SemrushApp(APIApplication):
1928
923
  ) -> dict[str, Any]:
1929
924
  """
1930
925
  Get referring domain distribution by country based on IP addresses.
1931
-
1932
- Args:
1933
- target (str): Root domain, subdomain, or URL of the website to investigate
1934
- target_type (str): Type of requested target (root_domain, domain, or url)
1935
- export_columns (str, optional): Comma-separated list of columns to include
1936
- display_sort (str, optional): Sorting order (e.g., "domains_num_desc", "backlinks_num_asc")
1937
- display_limit (int, optional): Number of results to return (default: 10,000)
1938
- display_offset (int, optional): Number of results to skip
1939
-
1940
- Returns:
1941
- dict[str, Any]: API response data
1942
-
1943
- Raises:
1944
- ValueError: If required parameters are missing
1945
- httpx.HTTPStatusError: If the API request fails
1946
-
1947
- Tags:
1948
- backlinks
1949
926
  """
1950
- if not target:
1951
- raise ValueError("Target parameter is required")
1952
- if not target_type:
1953
- raise ValueError("Target_type parameter is required")
1954
-
1955
- # Build parameters dictionary
1956
- params = {
1957
- "type": "backlinks_geo",
1958
- "key": self.api_key,
1959
- "target": target,
1960
- "target_type": target_type,
1961
- }
1962
-
1963
- if export_columns is not None:
1964
- params["export_columns"] = export_columns
1965
- if display_sort is not None:
1966
- params["display_sort"] = display_sort
1967
- if display_limit is not None:
1968
- params["display_limit"] = display_limit
1969
- if display_offset is not None:
1970
- params["display_offset"] = display_offset
1971
-
1972
- url = f"{self.base_url}/analytics/v1"
1973
- response = self._get(url, params=params)
1974
-
1975
- return self._handle_response(response)
927
+ if not target or not target_type:
928
+ raise ValueError("Target and target_type parameters are required")
929
+ return self._build_params_and_get(
930
+ "backlinks_geo_analytics",
931
+ target=target,
932
+ target_type=target_type,
933
+ export_columns=export_columns,
934
+ display_sort=display_sort,
935
+ display_limit=display_limit,
936
+ display_offset=display_offset,
937
+ )
1976
938
 
1977
939
  def related_keywords(
1978
940
  self,
@@ -1988,57 +950,103 @@ class SemrushApp(APIApplication):
1988
950
  ) -> dict[str, Any]:
1989
951
  """
1990
952
  Get extended list of related keywords, synonyms, and variations relevant to a queried term.
1991
-
1992
- Args:
1993
- phrase (str): Keyword or keyword expression to investigate
1994
- database (str): Regional database (default: "us")
1995
- display_limit (int, optional): Number of results to return (max 100,000)
1996
- display_offset (int, optional): Number of results to skip
1997
- export_escape (int, optional): Set to 1 to wrap columns in quotes
1998
- export_decode (int, optional): Set to 0 for URL-encoded response, 1 for normal response
1999
- export_columns (str, optional): Comma-separated list of columns to include
2000
- display_sort (str, optional): Sorting order (e.g., "nq_desc", "cp_asc")
2001
- display_filter (str, optional): Filter criteria for columns
2002
-
2003
- Returns:
2004
- dict[str, Any]: API response data
2005
-
2006
- Raises:
2007
- ValueError: If required parameters are missing
2008
- httpx.HTTPStatusError: If the API request fails
2009
-
2010
- Tags:
2011
- keyword-analysis
2012
953
  """
2013
954
  if not phrase:
2014
955
  raise ValueError("Phrase parameter is required")
2015
-
2016
- # Build parameters dictionary
2017
- params = {
2018
- "type": "phrase_related",
2019
- "key": self.api_key,
2020
- "phrase": phrase,
2021
- "database": database,
2022
- }
2023
-
2024
- if display_limit is not None:
2025
- params["display_limit"] = display_limit
2026
- if display_offset is not None:
2027
- params["display_offset"] = display_offset
2028
- if export_escape is not None:
2029
- params["export_escape"] = export_escape
2030
- if export_decode is not None:
2031
- params["export_decode"] = export_decode
2032
- if export_columns is not None:
2033
- params["export_columns"] = export_columns
2034
- if display_sort is not None:
2035
- params["display_sort"] = display_sort
2036
- if display_filter is not None:
2037
- params["display_filter"] = display_filter
2038
-
2039
- response = self._get(self.base_url, params=params)
2040
-
2041
- return self._handle_response(response)
956
+ return self._build_params_and_get(
957
+ "phrase_related",
958
+ phrase=phrase,
959
+ database=database,
960
+ display_limit=display_limit,
961
+ display_offset=display_offset,
962
+ export_escape=export_escape,
963
+ export_decode=export_decode,
964
+ export_columns=export_columns,
965
+ display_sort=display_sort,
966
+ display_filter=display_filter,
967
+ )
968
+
969
+ def tlds_distribution(
970
+ self,
971
+ target: str,
972
+ target_type: str,
973
+ export_columns: str | None = None,
974
+ display_sort: str | None = None,
975
+ display_limit: int | None = None,
976
+ display_offset: int | None = None,
977
+ ) -> dict[str, Any]:
978
+ """
979
+ Get referring domain distribution by top-level domain (e.g., .com, .org, .net).
980
+ """
981
+ if not target or not target_type:
982
+ raise ValueError("Target and target_type parameters are required")
983
+ return self._build_params_and_get(
984
+ "backlinks_tlds_profile_analytics",
985
+ target=target,
986
+ target_type=target_type,
987
+ export_columns=export_columns,
988
+ display_sort=display_sort,
989
+ display_limit=display_limit,
990
+ display_offset=display_offset,
991
+ )
992
+
993
+ def url_organic_search_keywords(
994
+ self,
995
+ url: str,
996
+ database: str = "us",
997
+ display_limit: int | None = None,
998
+ display_offset: int | None = None,
999
+ export_escape: int | None = None,
1000
+ export_decode: int | None = None,
1001
+ display_date: str | None = None,
1002
+ export_columns: str | None = None,
1003
+ display_sort: str | None = None,
1004
+ ) -> dict[str, Any]:
1005
+ """
1006
+ Get keywords that bring users to a URL via Google's top 100 organic search results.
1007
+ """
1008
+ if not url:
1009
+ raise ValueError("URL parameter is required")
1010
+ return self._build_params_and_get(
1011
+ "url_organic",
1012
+ url=url,
1013
+ database=database,
1014
+ display_limit=display_limit,
1015
+ display_offset=display_offset,
1016
+ export_escape=export_escape,
1017
+ export_decode=export_decode,
1018
+ display_date=display_date,
1019
+ export_columns=export_columns,
1020
+ display_sort=display_sort,
1021
+ )
1022
+
1023
+ def url_paid_search_keywords(
1024
+ self,
1025
+ url: str,
1026
+ database: str = "us",
1027
+ display_limit: int | None = None,
1028
+ display_offset: int | None = None,
1029
+ export_escape: int | None = None,
1030
+ export_decode: int | None = None,
1031
+ export_columns: str | None = None,
1032
+ display_sort: str | None = None,
1033
+ ) -> dict[str, Any]:
1034
+ """
1035
+ Get keywords that bring users to a URL via Google's paid search results.
1036
+ """
1037
+ if not url:
1038
+ raise ValueError("URL parameter is required")
1039
+ return self._build_params_and_get(
1040
+ "url_adwords",
1041
+ url=url,
1042
+ database=database,
1043
+ display_limit=display_limit,
1044
+ display_offset=display_offset,
1045
+ export_escape=export_escape,
1046
+ export_decode=export_decode,
1047
+ export_columns=export_columns,
1048
+ display_sort=display_sort,
1049
+ )
2042
1050
 
2043
1051
  def list_tools(self):
2044
1052
  """
@@ -2078,4 +1086,7 @@ class SemrushApp(APIApplication):
2078
1086
  self.referring_domains,
2079
1087
  self.referring_domains_by_country,
2080
1088
  self.related_keywords,
2081
- ]
1089
+ self.tlds_distribution,
1090
+ self.url_organic_search_keywords,
1091
+ self.url_paid_search_keywords,
1092
+ ]