mencoro 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +9 -0
  3. data/README.md +304 -0
  4. data/Rakefile +10 -0
  5. data/docs/AcceptedJobResource.md +22 -0
  6. data/docs/AccountApi.md +141 -0
  7. data/docs/AiResponseReportResource.md +28 -0
  8. data/docs/AiResponseResource.md +36 -0
  9. data/docs/AnalyticsApi.md +1320 -0
  10. data/docs/ApplyClusteringJobOutcome.md +26 -0
  11. data/docs/ApplyClusteringJobOutcomeClustersInner.md +22 -0
  12. data/docs/ApplyClusteringJobOutcomeFailedInner.md +22 -0
  13. data/docs/ApplyClusteringJobOutcomeSkippedClustersInner.md +20 -0
  14. data/docs/ApplyClusteringJobOutcomeSuccessfulInner.md +20 -0
  15. data/docs/AsyncJobResource.md +24 -0
  16. data/docs/BatchChangeTrackedQueryCheckFrequencyRequestData.md +20 -0
  17. data/docs/BatchChangeTrackedQueryPassesRequestData.md +20 -0
  18. data/docs/BatchCreateQueryClustersOutcome.md +20 -0
  19. data/docs/BatchCreateQueryClustersOutcomeFailedInner.md +22 -0
  20. data/docs/BatchCreateQueryClustersOutcomeSuccessfulInner.md +20 -0
  21. data/docs/BatchCreateQueryClustersRequestData.md +18 -0
  22. data/docs/BatchCreateTrackedQueriesRequestData.md +30 -0
  23. data/docs/BatchCreateTrackedQueriesResultResource.md +20 -0
  24. data/docs/BatchCreateTrackedQueriesResultResourceFailedInner.md +26 -0
  25. data/docs/BatchCreateTrackedQueriesResultResourceSuccessfulInner.md +24 -0
  26. data/docs/BatchForceCheckTrackedQueries200Response.md +20 -0
  27. data/docs/BatchForceCheckTrackedQueries200ResponseFailedInner.md +22 -0
  28. data/docs/BatchPauseTrackedQueries200Response.md +20 -0
  29. data/docs/BatchPauseTrackedQueries200ResponseFailedInner.md +22 -0
  30. data/docs/BatchPauseTrackedQueries200ResponseSuccessfulInner.md +18 -0
  31. data/docs/BatchResumeTrackedQueries200Response.md +20 -0
  32. data/docs/BatchTargetsRequestData.md +18 -0
  33. data/docs/BatchWriteOutcome.md +20 -0
  34. data/docs/BatchWriteOutcomeFailedInner.md +22 -0
  35. data/docs/BillingApi.md +77 -0
  36. data/docs/BrandProfileResource.md +24 -0
  37. data/docs/BulkAddClustersToTrackedQueries200Response.md +20 -0
  38. data/docs/BulkAddClustersToTrackedQueriesRequest.md +20 -0
  39. data/docs/BulkRemoveClustersFromTrackedQueries200Response.md +20 -0
  40. data/docs/BulkRemoveClustersFromTrackedQueriesRequest.md +20 -0
  41. data/docs/CapturesApi.md +267 -0
  42. data/docs/ChangeMemberRoleRequest.md +18 -0
  43. data/docs/ChangeTrackedQueryCheckFrequencyRequestData.md +18 -0
  44. data/docs/ChangeTrackedQueryPassesRequestData.md +18 -0
  45. data/docs/CitationResource.md +36 -0
  46. data/docs/ClusterMembershipRequestData.md +18 -0
  47. data/docs/ClustersApi.md +465 -0
  48. data/docs/CompetitorResource.md +24 -0
  49. data/docs/CreateCompetitorRequest.md +22 -0
  50. data/docs/CreateInvitation200Response.md +22 -0
  51. data/docs/CreateInvitation201Response.md +32 -0
  52. data/docs/CreateInvitationRequest.md +20 -0
  53. data/docs/CreateOrganization201Response.md +22 -0
  54. data/docs/CreateOrganizationRequest.md +18 -0
  55. data/docs/CreateProjectRequest.md +24 -0
  56. data/docs/CreateProjectRequestCompetitorsInner.md +22 -0
  57. data/docs/CreateQueryCluster409Response.md +22 -0
  58. data/docs/CreateQueryClusterRequest.md +18 -0
  59. data/docs/DeleteQueryCluster200Response.md +22 -0
  60. data/docs/DiscoveryApi.md +311 -0
  61. data/docs/EntitlementsResource.md +38 -0
  62. data/docs/GetAvailableFilters200Response.md +22 -0
  63. data/docs/GetAvailableFilters200ResponseClustersInner.md +20 -0
  64. data/docs/GetMeStats200Response.md +20 -0
  65. data/docs/GetMeStats200ResponseOrganizations.md +18 -0
  66. data/docs/GetMeStats200ResponseProjects.md +20 -0
  67. data/docs/GetMembershipStats200Response.md +30 -0
  68. data/docs/GetMentionSamples400Response.md +24 -0
  69. data/docs/GetMentionSamples400ResponseDetailsValueInner.md +22 -0
  70. data/docs/GetMetricGlossary200Response.md +18 -0
  71. data/docs/GetMetricGlossary200ResponseMetricsInner.md +30 -0
  72. data/docs/GetOrganizationOverview200Response.md +20 -0
  73. data/docs/GetOrganizationOverview200ResponseAggregate.md +30 -0
  74. data/docs/GetOrganizationOverview200ResponseProjectsInner.md +32 -0
  75. data/docs/GetOrganizationStats200Response.md +22 -0
  76. data/docs/GetOrganizationStats200ResponseInvitations.md +18 -0
  77. data/docs/GetOrganizationStats200ResponseMembers.md +18 -0
  78. data/docs/GetOrganizationStats200ResponseProjects.md +20 -0
  79. data/docs/GetShareOfVoiceFormula200Response.md +24 -0
  80. data/docs/GetTrackingCoverage200Response.md +30 -0
  81. data/docs/GetTrackingCoverage200ResponseSampleInner.md +28 -0
  82. data/docs/InvitationResource.md +30 -0
  83. data/docs/InvitationsApi.md +243 -0
  84. data/docs/JobsApi.md +79 -0
  85. data/docs/KeywordListingResource.md +88 -0
  86. data/docs/KeywordListingResourceMentionTypeCounts.md +26 -0
  87. data/docs/ListAiResponses200Response.md +20 -0
  88. data/docs/ListCompetitors200Response.md +20 -0
  89. data/docs/ListInvitations200Response.md +20 -0
  90. data/docs/ListKeywordListings200Response.md +24 -0
  91. data/docs/ListMembers200Response.md +20 -0
  92. data/docs/ListOrganizations200Response.md +20 -0
  93. data/docs/ListProjects200Response.md +20 -0
  94. data/docs/ListQueryClusters200Response.md +20 -0
  95. data/docs/ListSearchSnapshots200Response.md +20 -0
  96. data/docs/ListShoppingSnapshots200Response.md +20 -0
  97. data/docs/MeResource.md +34 -0
  98. data/docs/MemberResource.md +28 -0
  99. data/docs/MembersApi.md +391 -0
  100. data/docs/OperationEffect.md +22 -0
  101. data/docs/OrganizationOperationsApi.md +77 -0
  102. data/docs/OrganizationResource.md +32 -0
  103. data/docs/OrganizationsApi.md +807 -0
  104. data/docs/PreviewOrganizationOperation200Response.md +34 -0
  105. data/docs/PreviewOrganizationOperation200ResponseOrganization.md +20 -0
  106. data/docs/PreviewOrganizationOperationRequest.md +24 -0
  107. data/docs/ProjectDetailResource.md +32 -0
  108. data/docs/ProjectDetailResourceCompetitorsInner.md +24 -0
  109. data/docs/ProjectResource.md +50 -0
  110. data/docs/ProjectedMonthlyChecksResource.md +20 -0
  111. data/docs/ProjectsApi.md +999 -0
  112. data/docs/QueryClusterResource.md +22 -0
  113. data/docs/ReportAiResponseRequest.md +22 -0
  114. data/docs/SearchResultResource.md +32 -0
  115. data/docs/SearchSnapshotResource.md +28 -0
  116. data/docs/SearchTrackedQueries200Response.md +20 -0
  117. data/docs/ShoppingOfferResource.md +38 -0
  118. data/docs/ShoppingSnapshotResource.md +28 -0
  119. data/docs/StartBrandDiscoveryJobRequest.md +20 -0
  120. data/docs/StartBrandNameSuggestionJobRequest.md +24 -0
  121. data/docs/StartClusteringJobRequestData.md +22 -0
  122. data/docs/StartKeywordDiscoveryJobRequest.md +24 -0
  123. data/docs/StartPromptDiscoveryJobRequest.md +24 -0
  124. data/docs/SubmittedChecksResource.md +20 -0
  125. data/docs/SubscriptionResource.md +42 -0
  126. data/docs/TrackedQueriesApi.md +1549 -0
  127. data/docs/TrackedQueryCountResource.md +20 -0
  128. data/docs/TrackedQueryDetailResource.md +38 -0
  129. data/docs/TrackedQueryResource.md +52 -0
  130. data/docs/TrackedQueryUsageResource.md +18 -0
  131. data/docs/UpdateOrganizationRequest.md +22 -0
  132. data/docs/UpdateProjectBrandProfileRequest.md +20 -0
  133. data/docs/UpdateProjectRequest.md +18 -0
  134. data/git_push.sh +57 -0
  135. data/lib/mencoro/api/account_api.rb +136 -0
  136. data/lib/mencoro/api/analytics_api.rb +1555 -0
  137. data/lib/mencoro/api/billing_api.rb +85 -0
  138. data/lib/mencoro/api/captures_api.rb +338 -0
  139. data/lib/mencoro/api/clusters_api.rb +548 -0
  140. data/lib/mencoro/api/discovery_api.rb +360 -0
  141. data/lib/mencoro/api/invitations_api.rb +302 -0
  142. data/lib/mencoro/api/jobs_api.rb +91 -0
  143. data/lib/mencoro/api/members_api.rb +453 -0
  144. data/lib/mencoro/api/organization_operations_api.rb +90 -0
  145. data/lib/mencoro/api/organizations_api.rb +835 -0
  146. data/lib/mencoro/api/projects_api.rb +1158 -0
  147. data/lib/mencoro/api/tracked_queries_api.rb +1792 -0
  148. data/lib/mencoro/api_client.rb +441 -0
  149. data/lib/mencoro/api_error.rb +58 -0
  150. data/lib/mencoro/api_model_base.rb +88 -0
  151. data/lib/mencoro/configuration.rb +392 -0
  152. data/lib/mencoro/models/accepted_job_resource.rb +220 -0
  153. data/lib/mencoro/models/ai_response_report_resource.rb +311 -0
  154. data/lib/mencoro/models/ai_response_resource.rb +412 -0
  155. data/lib/mencoro/models/apply_clustering_job_outcome.rb +199 -0
  156. data/lib/mencoro/models/apply_clustering_job_outcome_clusters_inner.rb +165 -0
  157. data/lib/mencoro/models/apply_clustering_job_outcome_failed_inner.rb +165 -0
  158. data/lib/mencoro/models/apply_clustering_job_outcome_skipped_clusters_inner.rb +156 -0
  159. data/lib/mencoro/models/apply_clustering_job_outcome_successful_inner.rb +158 -0
  160. data/lib/mencoro/models/async_job_resource.rb +256 -0
  161. data/lib/mencoro/models/batch_change_tracked_query_check_frequency_request_data.rb +237 -0
  162. data/lib/mencoro/models/batch_change_tracked_query_passes_request_data.rb +231 -0
  163. data/lib/mencoro/models/batch_create_query_clusters_outcome.rb +163 -0
  164. data/lib/mencoro/models/batch_create_query_clusters_outcome_failed_inner.rb +165 -0
  165. data/lib/mencoro/models/batch_create_query_clusters_outcome_successful_inner.rb +156 -0
  166. data/lib/mencoro/models/batch_create_query_clusters_request_data.rb +186 -0
  167. data/lib/mencoro/models/batch_create_tracked_queries_request_data.rb +399 -0
  168. data/lib/mencoro/models/batch_create_tracked_queries_result_resource.rb +163 -0
  169. data/lib/mencoro/models/batch_create_tracked_queries_result_resource_failed_inner.rb +183 -0
  170. data/lib/mencoro/models/batch_create_tracked_queries_result_resource_successful_inner.rb +174 -0
  171. data/lib/mencoro/models/batch_force_check_tracked_queries200_response.rb +162 -0
  172. data/lib/mencoro/models/batch_force_check_tracked_queries200_response_failed_inner.rb +165 -0
  173. data/lib/mencoro/models/batch_pause_tracked_queries200_response.rb +162 -0
  174. data/lib/mencoro/models/batch_pause_tracked_queries200_response_failed_inner.rb +165 -0
  175. data/lib/mencoro/models/batch_pause_tracked_queries200_response_successful_inner.rb +147 -0
  176. data/lib/mencoro/models/batch_resume_tracked_queries200_response.rb +162 -0
  177. data/lib/mencoro/models/batch_targets_request_data.rb +186 -0
  178. data/lib/mencoro/models/batch_write_outcome.rb +163 -0
  179. data/lib/mencoro/models/batch_write_outcome_failed_inner.rb +166 -0
  180. data/lib/mencoro/models/brand_profile_resource.rb +235 -0
  181. data/lib/mencoro/models/bulk_add_clusters_to_tracked_queries200_response.rb +162 -0
  182. data/lib/mencoro/models/bulk_add_clusters_to_tracked_queries_request.rb +218 -0
  183. data/lib/mencoro/models/bulk_remove_clusters_from_tracked_queries200_response.rb +162 -0
  184. data/lib/mencoro/models/bulk_remove_clusters_from_tracked_queries_request.rb +218 -0
  185. data/lib/mencoro/models/change_member_role_request.rb +181 -0
  186. data/lib/mencoro/models/change_tracked_query_check_frequency_request_data.rb +190 -0
  187. data/lib/mencoro/models/change_tracked_query_passes_request_data.rb +184 -0
  188. data/lib/mencoro/models/citation_resource.rb +306 -0
  189. data/lib/mencoro/models/cluster_membership_request_data.rb +186 -0
  190. data/lib/mencoro/models/competitor_resource.rb +250 -0
  191. data/lib/mencoro/models/create_competitor_request.rb +247 -0
  192. data/lib/mencoro/models/create_invitation200_response.rb +202 -0
  193. data/lib/mencoro/models/create_invitation201_response.rb +347 -0
  194. data/lib/mencoro/models/create_invitation_request.rb +192 -0
  195. data/lib/mencoro/models/create_organization201_response.rb +166 -0
  196. data/lib/mencoro/models/create_organization_request.rb +175 -0
  197. data/lib/mencoro/models/create_project_request.rb +307 -0
  198. data/lib/mencoro/models/create_project_request_competitors_inner.rb +256 -0
  199. data/lib/mencoro/models/create_query_cluster409_response.rb +165 -0
  200. data/lib/mencoro/models/create_query_cluster_request.rb +167 -0
  201. data/lib/mencoro/models/delete_query_cluster200_response.rb +168 -0
  202. data/lib/mencoro/models/entitlements_resource.rb +328 -0
  203. data/lib/mencoro/models/get_available_filters200_response.rb +173 -0
  204. data/lib/mencoro/models/get_available_filters200_response_clusters_inner.rb +157 -0
  205. data/lib/mencoro/models/get_me_stats200_response.rb +156 -0
  206. data/lib/mencoro/models/get_me_stats200_response_organizations.rb +167 -0
  207. data/lib/mencoro/models/get_me_stats200_response_projects.rb +196 -0
  208. data/lib/mencoro/models/get_membership_stats200_response.rb +322 -0
  209. data/lib/mencoro/models/get_mention_samples400_response.rb +231 -0
  210. data/lib/mencoro/models/get_mention_samples400_response_details_value_inner.rb +168 -0
  211. data/lib/mencoro/models/get_metric_glossary200_response.rb +149 -0
  212. data/lib/mencoro/models/get_metric_glossary200_response_metrics_inner.rb +240 -0
  213. data/lib/mencoro/models/get_organization_overview200_response.rb +158 -0
  214. data/lib/mencoro/models/get_organization_overview200_response_aggregate.rb +209 -0
  215. data/lib/mencoro/models/get_organization_overview200_response_projects_inner.rb +218 -0
  216. data/lib/mencoro/models/get_organization_stats200_response.rb +165 -0
  217. data/lib/mencoro/models/get_organization_stats200_response_invitations.rb +167 -0
  218. data/lib/mencoro/models/get_organization_stats200_response_members.rb +167 -0
  219. data/lib/mencoro/models/get_organization_stats200_response_projects.rb +196 -0
  220. data/lib/mencoro/models/get_share_of_voice_formula200_response.rb +182 -0
  221. data/lib/mencoro/models/get_tracking_coverage200_response.rb +209 -0
  222. data/lib/mencoro/models/get_tracking_coverage200_response_sample_inner.rb +229 -0
  223. data/lib/mencoro/models/invitation_resource.rb +331 -0
  224. data/lib/mencoro/models/keyword_listing_resource.rb +798 -0
  225. data/lib/mencoro/models/keyword_listing_resource_mention_type_counts.rb +184 -0
  226. data/lib/mencoro/models/list_ai_responses200_response.rb +159 -0
  227. data/lib/mencoro/models/list_competitors200_response.rb +159 -0
  228. data/lib/mencoro/models/list_invitations200_response.rb +158 -0
  229. data/lib/mencoro/models/list_keyword_listings200_response.rb +180 -0
  230. data/lib/mencoro/models/list_members200_response.rb +158 -0
  231. data/lib/mencoro/models/list_organizations200_response.rb +158 -0
  232. data/lib/mencoro/models/list_projects200_response.rb +159 -0
  233. data/lib/mencoro/models/list_query_clusters200_response.rb +159 -0
  234. data/lib/mencoro/models/list_search_snapshots200_response.rb +159 -0
  235. data/lib/mencoro/models/list_shopping_snapshots200_response.rb +159 -0
  236. data/lib/mencoro/models/me_resource.rb +394 -0
  237. data/lib/mencoro/models/member_resource.rb +322 -0
  238. data/lib/mencoro/models/operation_effect.rb +204 -0
  239. data/lib/mencoro/models/organization_resource.rb +326 -0
  240. data/lib/mencoro/models/preview_organization_operation200_response.rb +236 -0
  241. data/lib/mencoro/models/preview_organization_operation200_response_organization.rb +158 -0
  242. data/lib/mencoro/models/preview_organization_operation_request.rb +213 -0
  243. data/lib/mencoro/models/project_detail_resource.rb +380 -0
  244. data/lib/mencoro/models/project_detail_resource_competitors_inner.rb +178 -0
  245. data/lib/mencoro/models/project_resource.rb +422 -0
  246. data/lib/mencoro/models/projected_monthly_checks_resource.rb +211 -0
  247. data/lib/mencoro/models/query_cluster_resource.rb +229 -0
  248. data/lib/mencoro/models/report_ai_response_request.rb +227 -0
  249. data/lib/mencoro/models/search_result_resource.rb +284 -0
  250. data/lib/mencoro/models/search_snapshot_resource.rb +302 -0
  251. data/lib/mencoro/models/search_tracked_queries200_response.rb +158 -0
  252. data/lib/mencoro/models/shopping_offer_resource.rb +381 -0
  253. data/lib/mencoro/models/shopping_snapshot_resource.rb +302 -0
  254. data/lib/mencoro/models/start_brand_discovery_job_request.rb +185 -0
  255. data/lib/mencoro/models/start_brand_name_suggestion_job_request.rb +287 -0
  256. data/lib/mencoro/models/start_clustering_job_request_data.rb +249 -0
  257. data/lib/mencoro/models/start_keyword_discovery_job_request.rb +266 -0
  258. data/lib/mencoro/models/start_prompt_discovery_job_request.rb +276 -0
  259. data/lib/mencoro/models/submitted_checks_resource.rb +195 -0
  260. data/lib/mencoro/models/subscription_resource.rb +362 -0
  261. data/lib/mencoro/models/tracked_query_count_resource.rb +193 -0
  262. data/lib/mencoro/models/tracked_query_detail_resource.rb +440 -0
  263. data/lib/mencoro/models/tracked_query_resource.rb +492 -0
  264. data/lib/mencoro/models/tracked_query_usage_resource.rb +175 -0
  265. data/lib/mencoro/models/update_organization_request.rb +195 -0
  266. data/lib/mencoro/models/update_project_brand_profile_request.rb +218 -0
  267. data/lib/mencoro/models/update_project_request.rb +182 -0
  268. data/lib/mencoro/version.rb +15 -0
  269. data/lib/mencoro.rb +169 -0
  270. data/mencoro.gemspec +41 -0
  271. data/spec/api/account_api_spec.rb +57 -0
  272. data/spec/api/analytics_api_spec.rb +318 -0
  273. data/spec/api/billing_api_spec.rb +47 -0
  274. data/spec/api/captures_api_spec.rb +93 -0
  275. data/spec/api/clusters_api_spec.rb +126 -0
  276. data/spec/api/discovery_api_spec.rb +94 -0
  277. data/spec/api/invitations_api_spec.rb +83 -0
  278. data/spec/api/jobs_api_spec.rb +48 -0
  279. data/spec/api/members_api_spec.rb +111 -0
  280. data/spec/api/organization_operations_api_spec.rb +47 -0
  281. data/spec/api/organizations_api_spec.rb +181 -0
  282. data/spec/api/projects_api_spec.rb +229 -0
  283. data/spec/api/tracked_queries_api_spec.rb +344 -0
  284. data/spec/models/accepted_job_resource_spec.rb +48 -0
  285. data/spec/models/ai_response_report_resource_spec.rb +70 -0
  286. data/spec/models/ai_response_resource_spec.rb +90 -0
  287. data/spec/models/apply_clustering_job_outcome_clusters_inner_spec.rb +48 -0
  288. data/spec/models/apply_clustering_job_outcome_failed_inner_spec.rb +48 -0
  289. data/spec/models/apply_clustering_job_outcome_skipped_clusters_inner_spec.rb +42 -0
  290. data/spec/models/apply_clustering_job_outcome_spec.rb +60 -0
  291. data/spec/models/apply_clustering_job_outcome_successful_inner_spec.rb +42 -0
  292. data/spec/models/async_job_resource_spec.rb +62 -0
  293. data/spec/models/batch_change_tracked_query_check_frequency_request_data_spec.rb +46 -0
  294. data/spec/models/batch_change_tracked_query_passes_request_data_spec.rb +42 -0
  295. data/spec/models/batch_create_query_clusters_outcome_failed_inner_spec.rb +48 -0
  296. data/spec/models/batch_create_query_clusters_outcome_spec.rb +42 -0
  297. data/spec/models/batch_create_query_clusters_outcome_successful_inner_spec.rb +42 -0
  298. data/spec/models/batch_create_query_clusters_request_data_spec.rb +36 -0
  299. data/spec/models/batch_create_tracked_queries_request_data_spec.rb +80 -0
  300. data/spec/models/batch_create_tracked_queries_result_resource_failed_inner_spec.rb +60 -0
  301. data/spec/models/batch_create_tracked_queries_result_resource_spec.rb +42 -0
  302. data/spec/models/batch_create_tracked_queries_result_resource_successful_inner_spec.rb +54 -0
  303. data/spec/models/batch_force_check_tracked_queries200_response_failed_inner_spec.rb +48 -0
  304. data/spec/models/batch_force_check_tracked_queries200_response_spec.rb +42 -0
  305. data/spec/models/batch_pause_tracked_queries200_response_failed_inner_spec.rb +48 -0
  306. data/spec/models/batch_pause_tracked_queries200_response_spec.rb +42 -0
  307. data/spec/models/batch_pause_tracked_queries200_response_successful_inner_spec.rb +36 -0
  308. data/spec/models/batch_resume_tracked_queries200_response_spec.rb +42 -0
  309. data/spec/models/batch_targets_request_data_spec.rb +36 -0
  310. data/spec/models/batch_write_outcome_failed_inner_spec.rb +48 -0
  311. data/spec/models/batch_write_outcome_spec.rb +42 -0
  312. data/spec/models/brand_profile_resource_spec.rb +54 -0
  313. data/spec/models/bulk_add_clusters_to_tracked_queries200_response_spec.rb +42 -0
  314. data/spec/models/bulk_add_clusters_to_tracked_queries_request_spec.rb +42 -0
  315. data/spec/models/bulk_remove_clusters_from_tracked_queries200_response_spec.rb +42 -0
  316. data/spec/models/bulk_remove_clusters_from_tracked_queries_request_spec.rb +42 -0
  317. data/spec/models/change_member_role_request_spec.rb +40 -0
  318. data/spec/models/change_tracked_query_check_frequency_request_data_spec.rb +40 -0
  319. data/spec/models/change_tracked_query_passes_request_data_spec.rb +36 -0
  320. data/spec/models/citation_resource_spec.rb +90 -0
  321. data/spec/models/cluster_membership_request_data_spec.rb +36 -0
  322. data/spec/models/competitor_resource_spec.rb +54 -0
  323. data/spec/models/create_competitor_request_spec.rb +48 -0
  324. data/spec/models/create_invitation200_response_spec.rb +52 -0
  325. data/spec/models/create_invitation201_response_spec.rb +86 -0
  326. data/spec/models/create_invitation_request_spec.rb +46 -0
  327. data/spec/models/create_organization201_response_spec.rb +48 -0
  328. data/spec/models/create_organization_request_spec.rb +36 -0
  329. data/spec/models/create_project_request_competitors_inner_spec.rb +48 -0
  330. data/spec/models/create_project_request_spec.rb +54 -0
  331. data/spec/models/create_query_cluster409_response_spec.rb +48 -0
  332. data/spec/models/create_query_cluster_request_spec.rb +36 -0
  333. data/spec/models/delete_query_cluster200_response_spec.rb +48 -0
  334. data/spec/models/entitlements_resource_spec.rb +104 -0
  335. data/spec/models/get_available_filters200_response_clusters_inner_spec.rb +42 -0
  336. data/spec/models/get_available_filters200_response_spec.rb +48 -0
  337. data/spec/models/get_me_stats200_response_organizations_spec.rb +36 -0
  338. data/spec/models/get_me_stats200_response_projects_spec.rb +42 -0
  339. data/spec/models/get_me_stats200_response_spec.rb +42 -0
  340. data/spec/models/get_membership_stats200_response_spec.rb +72 -0
  341. data/spec/models/get_mention_samples400_response_details_value_inner_spec.rb +48 -0
  342. data/spec/models/get_mention_samples400_response_spec.rb +54 -0
  343. data/spec/models/get_metric_glossary200_response_metrics_inner_spec.rb +76 -0
  344. data/spec/models/get_metric_glossary200_response_spec.rb +36 -0
  345. data/spec/models/get_organization_overview200_response_aggregate_spec.rb +72 -0
  346. data/spec/models/get_organization_overview200_response_projects_inner_spec.rb +78 -0
  347. data/spec/models/get_organization_overview200_response_spec.rb +42 -0
  348. data/spec/models/get_organization_stats200_response_invitations_spec.rb +36 -0
  349. data/spec/models/get_organization_stats200_response_members_spec.rb +36 -0
  350. data/spec/models/get_organization_stats200_response_projects_spec.rb +42 -0
  351. data/spec/models/get_organization_stats200_response_spec.rb +48 -0
  352. data/spec/models/get_share_of_voice_formula200_response_spec.rb +54 -0
  353. data/spec/models/get_tracking_coverage200_response_sample_inner_spec.rb +70 -0
  354. data/spec/models/get_tracking_coverage200_response_spec.rb +72 -0
  355. data/spec/models/invitation_resource_spec.rb +80 -0
  356. data/spec/models/keyword_listing_resource_mention_type_counts_spec.rb +60 -0
  357. data/spec/models/keyword_listing_resource_spec.rb +262 -0
  358. data/spec/models/list_ai_responses200_response_spec.rb +42 -0
  359. data/spec/models/list_competitors200_response_spec.rb +42 -0
  360. data/spec/models/list_invitations200_response_spec.rb +42 -0
  361. data/spec/models/list_keyword_listings200_response_spec.rb +54 -0
  362. data/spec/models/list_members200_response_spec.rb +42 -0
  363. data/spec/models/list_organizations200_response_spec.rb +42 -0
  364. data/spec/models/list_projects200_response_spec.rb +42 -0
  365. data/spec/models/list_query_clusters200_response_spec.rb +42 -0
  366. data/spec/models/list_search_snapshots200_response_spec.rb +42 -0
  367. data/spec/models/list_shopping_snapshots200_response_spec.rb +42 -0
  368. data/spec/models/me_resource_spec.rb +92 -0
  369. data/spec/models/member_resource_spec.rb +74 -0
  370. data/spec/models/operation_effect_spec.rb +48 -0
  371. data/spec/models/organization_resource_spec.rb +86 -0
  372. data/spec/models/preview_organization_operation200_response_organization_spec.rb +42 -0
  373. data/spec/models/preview_organization_operation200_response_spec.rb +84 -0
  374. data/spec/models/preview_organization_operation_request_spec.rb +58 -0
  375. data/spec/models/project_detail_resource_competitors_inner_spec.rb +54 -0
  376. data/spec/models/project_detail_resource_spec.rb +82 -0
  377. data/spec/models/project_resource_spec.rb +136 -0
  378. data/spec/models/projected_monthly_checks_resource_spec.rb +42 -0
  379. data/spec/models/query_cluster_resource_spec.rb +48 -0
  380. data/spec/models/report_ai_response_request_spec.rb +52 -0
  381. data/spec/models/search_result_resource_spec.rb +78 -0
  382. data/spec/models/search_snapshot_resource_spec.rb +66 -0
  383. data/spec/models/search_tracked_queries200_response_spec.rb +42 -0
  384. data/spec/models/shopping_offer_resource_spec.rb +96 -0
  385. data/spec/models/shopping_snapshot_resource_spec.rb +66 -0
  386. data/spec/models/start_brand_discovery_job_request_spec.rb +42 -0
  387. data/spec/models/start_brand_name_suggestion_job_request_spec.rb +54 -0
  388. data/spec/models/start_clustering_job_request_data_spec.rb +52 -0
  389. data/spec/models/start_keyword_discovery_job_request_spec.rb +54 -0
  390. data/spec/models/start_prompt_discovery_job_request_spec.rb +54 -0
  391. data/spec/models/submitted_checks_resource_spec.rb +42 -0
  392. data/spec/models/subscription_resource_spec.rb +120 -0
  393. data/spec/models/tracked_query_count_resource_spec.rb +42 -0
  394. data/spec/models/tracked_query_detail_resource_spec.rb +108 -0
  395. data/spec/models/tracked_query_resource_spec.rb +150 -0
  396. data/spec/models/tracked_query_usage_resource_spec.rb +36 -0
  397. data/spec/models/update_organization_request_spec.rb +48 -0
  398. data/spec/models/update_project_brand_profile_request_spec.rb +42 -0
  399. data/spec/models/update_project_request_spec.rb +36 -0
  400. data/spec/spec_helper.rb +111 -0
  401. metadata +641 -0
@@ -0,0 +1,1555 @@
1
+ =begin
2
+ #Mencoro API
3
+
4
+ #Public API for Mencoro customers. Authenticate with an API key: `Authorization: Bearer mencoro_sk_...`.
5
+
6
+ The version of the OpenAPI document: 1.0.0
7
+ Contact: support@mencoro.com
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.24.0
10
+
11
+ =end
12
+
13
+ require 'cgi'
14
+
15
+ module Mencoro
16
+ class AnalyticsApi
17
+ attr_accessor :api_client
18
+
19
+ def initialize(api_client = ApiClient.default)
20
+ @api_client = api_client
21
+ end
22
+ # Filter values a project is configured for
23
+ # Minimum role: viewer. Call this first: it is where every other analytics operation sends you for the valid engines, countries and keyword clusters of a project, and the values it returns are the exact strings the engines, countries and queryClusterIds parameters accept — anything else is rejected as a 400. Engines are engine codes, countries are ISO-3166 alpha-2 codes, and clusters are {id, name} objects whose id goes in queryClusterIds. Takes no date window: it describes how the project is configured right now, so a value is listed as soon as a tracked query uses it, even when no response has been captured for it yet. An empty list therefore means nothing is configured for it, not that no data was collected. Competitor ids are not part of this response.
24
+ # @param organization_id [String]
25
+ # @param project_id [String]
26
+ # @param [Hash] opts the optional parameters
27
+ # @return [GetAvailableFilters200Response]
28
+ def get_available_filters(organization_id, project_id, opts = {})
29
+ data, _status_code, _headers = get_available_filters_with_http_info(organization_id, project_id, opts)
30
+ data
31
+ end
32
+
33
+ # Filter values a project is configured for
34
+ # Minimum role: viewer. Call this first: it is where every other analytics operation sends you for the valid engines, countries and keyword clusters of a project, and the values it returns are the exact strings the engines, countries and queryClusterIds parameters accept — anything else is rejected as a 400. Engines are engine codes, countries are ISO-3166 alpha-2 codes, and clusters are {id, name} objects whose id goes in queryClusterIds. Takes no date window: it describes how the project is configured right now, so a value is listed as soon as a tracked query uses it, even when no response has been captured for it yet. An empty list therefore means nothing is configured for it, not that no data was collected. Competitor ids are not part of this response.
35
+ # @param organization_id [String]
36
+ # @param project_id [String]
37
+ # @param [Hash] opts the optional parameters
38
+ # @return [Array<(GetAvailableFilters200Response, Integer, Hash)>] GetAvailableFilters200Response data, response status code and response headers
39
+ def get_available_filters_with_http_info(organization_id, project_id, opts = {})
40
+ if @api_client.config.debugging
41
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_available_filters ...'
42
+ end
43
+ # verify the required parameter 'organization_id' is set
44
+ if @api_client.config.client_side_validation && organization_id.nil?
45
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_available_filters"
46
+ end
47
+ # verify the required parameter 'project_id' is set
48
+ if @api_client.config.client_side_validation && project_id.nil?
49
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_available_filters"
50
+ end
51
+ # resource path
52
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/available-filters'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
53
+
54
+ # query parameters
55
+ query_params = opts[:query_params] || {}
56
+
57
+ # header parameters
58
+ header_params = opts[:header_params] || {}
59
+ # HTTP header 'Accept' (if needed)
60
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
61
+
62
+ # form parameters
63
+ form_params = opts[:form_params] || {}
64
+
65
+ # http body (model)
66
+ post_body = opts[:debug_body]
67
+
68
+ # return_type
69
+ return_type = opts[:debug_return_type] || 'GetAvailableFilters200Response'
70
+
71
+ # auth_names
72
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
73
+
74
+ new_options = opts.merge(
75
+ :operation => :"AnalyticsApi.get_available_filters",
76
+ :header_params => header_params,
77
+ :query_params => query_params,
78
+ :form_params => form_params,
79
+ :body => post_body,
80
+ :auth_names => auth_names,
81
+ :return_type => return_type
82
+ )
83
+
84
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
85
+ if @api_client.config.debugging
86
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_available_filters\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
87
+ end
88
+ return data, status_code, headers
89
+ end
90
+
91
+ # Domains and pages the AI answers cited
92
+ # Minimum role: viewer. The sources the answer engines drew on across a project's AI answers over a date window, ranked by how often they were cited. Per source: citationCount, the total number of citations; distinctResponseCount and distinctQueryCount, how many captured answers and tracked queries it appeared in; avgPosition, its average 1-based rank inside the answers' citation lists, where LOWER is better. A null avgPosition means no citation in the window carried a position, not a rank of zero; a null domain or sampleTitle means the citation never carried one. `total` counts the distinct sources matching the window, before paging. The list is UNFILTERED by ownership: the brand's, competitors' and third-party sources sit in the same ranking. Only the AI answer engines (chatgpt, perplexity, google_ai_overview, google_ai_mode) produce citations, so filtering by google_serp or google_shopping is accepted and returns nothing. Citations still behind an answer engine's redirect (a google.com/goto link, whose URL names the engine rather than the source) are excluded, so a source cited only through such links is absent from this list rather than counted as zero.
93
+ # @param organization_id [String]
94
+ # @param project_id [String]
95
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
96
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
97
+ # @param [Hash] opts the optional parameters
98
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated. Only the AI engines carry citations.
99
+ # @option opts [String] :group_by Grain of the roll-up: \&quot;domain\&quot; by host, \&quot;page\&quot; by exact URL. (default to 'domain')
100
+ # @option opts [Integer] :limit Page size. A larger value is rejected, never silently reduced. (default to 20)
101
+ # @option opts [Integer] :offset Number of sources to skip. (default to 0)
102
+ # @return [nil]
103
+ def get_cited_sources(organization_id, project_id, date_from, date_to, opts = {})
104
+ get_cited_sources_with_http_info(organization_id, project_id, date_from, date_to, opts)
105
+ nil
106
+ end
107
+
108
+ # Domains and pages the AI answers cited
109
+ # Minimum role: viewer. The sources the answer engines drew on across a project&#39;s AI answers over a date window, ranked by how often they were cited. Per source: citationCount, the total number of citations; distinctResponseCount and distinctQueryCount, how many captured answers and tracked queries it appeared in; avgPosition, its average 1-based rank inside the answers&#39; citation lists, where LOWER is better. A null avgPosition means no citation in the window carried a position, not a rank of zero; a null domain or sampleTitle means the citation never carried one. &#x60;total&#x60; counts the distinct sources matching the window, before paging. The list is UNFILTERED by ownership: the brand&#39;s, competitors&#39; and third-party sources sit in the same ranking. Only the AI answer engines (chatgpt, perplexity, google_ai_overview, google_ai_mode) produce citations, so filtering by google_serp or google_shopping is accepted and returns nothing. Citations still behind an answer engine&#39;s redirect (a google.com/goto link, whose URL names the engine rather than the source) are excluded, so a source cited only through such links is absent from this list rather than counted as zero.
110
+ # @param organization_id [String]
111
+ # @param project_id [String]
112
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
113
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
114
+ # @param [Hash] opts the optional parameters
115
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated. Only the AI engines carry citations.
116
+ # @option opts [String] :group_by Grain of the roll-up: \&quot;domain\&quot; by host, \&quot;page\&quot; by exact URL. (default to 'domain')
117
+ # @option opts [Integer] :limit Page size. A larger value is rejected, never silently reduced. (default to 20)
118
+ # @option opts [Integer] :offset Number of sources to skip. (default to 0)
119
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
120
+ def get_cited_sources_with_http_info(organization_id, project_id, date_from, date_to, opts = {})
121
+ if @api_client.config.debugging
122
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_cited_sources ...'
123
+ end
124
+ # verify the required parameter 'organization_id' is set
125
+ if @api_client.config.client_side_validation && organization_id.nil?
126
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_cited_sources"
127
+ end
128
+ # verify the required parameter 'project_id' is set
129
+ if @api_client.config.client_side_validation && project_id.nil?
130
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_cited_sources"
131
+ end
132
+ # verify the required parameter 'date_from' is set
133
+ if @api_client.config.client_side_validation && date_from.nil?
134
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.get_cited_sources"
135
+ end
136
+ # verify the required parameter 'date_to' is set
137
+ if @api_client.config.client_side_validation && date_to.nil?
138
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.get_cited_sources"
139
+ end
140
+ allowable_values = ["chatgpt", "perplexity", "google_ai_overview", "google_ai_mode", "google_serp", "google_shopping"]
141
+ if @api_client.config.client_side_validation && opts[:'engines'] && !opts[:'engines'].all? { |item| allowable_values.include?(item) }
142
+ fail ArgumentError, "invalid value for \"engines\", must include one of #{allowable_values}"
143
+ end
144
+ allowable_values = ["domain", "page"]
145
+ if @api_client.config.client_side_validation && opts[:'group_by'] && !allowable_values.include?(opts[:'group_by'])
146
+ fail ArgumentError, "invalid value for \"group_by\", must be one of #{allowable_values}"
147
+ end
148
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100
149
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AnalyticsApi.get_cited_sources, must be smaller than or equal to 100.'
150
+ end
151
+
152
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1
153
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AnalyticsApi.get_cited_sources, must be greater than or equal to 1.'
154
+ end
155
+
156
+ if @api_client.config.client_side_validation && !opts[:'offset'].nil? && opts[:'offset'] < 0
157
+ fail ArgumentError, 'invalid value for "opts[:"offset"]" when calling AnalyticsApi.get_cited_sources, must be greater than or equal to 0.'
158
+ end
159
+
160
+ # resource path
161
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/cited-sources'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
162
+
163
+ # query parameters
164
+ query_params = opts[:query_params] || {}
165
+ query_params[:'dateFrom'] = date_from
166
+ query_params[:'dateTo'] = date_to
167
+ query_params[:'engines'] = @api_client.build_collection_param(opts[:'engines'], :multi) if !opts[:'engines'].nil?
168
+ query_params[:'groupBy'] = opts[:'group_by'] if !opts[:'group_by'].nil?
169
+ query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
170
+ query_params[:'offset'] = opts[:'offset'] if !opts[:'offset'].nil?
171
+
172
+ # header parameters
173
+ header_params = opts[:header_params] || {}
174
+
175
+ # form parameters
176
+ form_params = opts[:form_params] || {}
177
+
178
+ # http body (model)
179
+ post_body = opts[:debug_body]
180
+
181
+ # return_type
182
+ return_type = opts[:debug_return_type]
183
+
184
+ # auth_names
185
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
186
+
187
+ new_options = opts.merge(
188
+ :operation => :"AnalyticsApi.get_cited_sources",
189
+ :header_params => header_params,
190
+ :query_params => query_params,
191
+ :form_params => form_params,
192
+ :body => post_body,
193
+ :auth_names => auth_names,
194
+ :return_type => return_type
195
+ )
196
+
197
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
198
+ if @api_client.config.debugging
199
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_cited_sources\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
200
+ end
201
+ return data, status_code, headers
202
+ end
203
+
204
+ # Rank-tracking metrics per keyword cluster
205
+ # Minimum role: viewer. One row per keyword cluster over a date window, with its tracked query and keyword counts, average positions, rates, share of voice and sentiment split. A row whose clusterId is null is the ungrouped bucket: the tracked queries belonging to no cluster. Position metrics are 1-based and LOWER is better; rates, positivityIndex and shareOfVoice are 0-100 percentages where higher is better. A null metric means nothing was captured for that cluster in the window — it is not a zero, and averaging or charting it as one would misstate the period. dataDirtySince is non-null while a recalculation is pending, meaning the numbers predate the latest configuration change.
206
+ # @param organization_id [String]
207
+ # @param project_id [String]
208
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
209
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
210
+ # @param [Hash] opts the optional parameters
211
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated.
212
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
213
+ # @option opts [Array<String>] :query_cluster_ids Restrict to these clusters. Each must belong to the project.
214
+ # @option opts [Boolean] :include_ungrouped_queries Sent alone, returns only the ungrouped bucket rather than adding it to every cluster. (default to false)
215
+ # @return [nil]
216
+ def get_cluster_breakdown(organization_id, project_id, date_from, date_to, opts = {})
217
+ get_cluster_breakdown_with_http_info(organization_id, project_id, date_from, date_to, opts)
218
+ nil
219
+ end
220
+
221
+ # Rank-tracking metrics per keyword cluster
222
+ # Minimum role: viewer. One row per keyword cluster over a date window, with its tracked query and keyword counts, average positions, rates, share of voice and sentiment split. A row whose clusterId is null is the ungrouped bucket: the tracked queries belonging to no cluster. Position metrics are 1-based and LOWER is better; rates, positivityIndex and shareOfVoice are 0-100 percentages where higher is better. A null metric means nothing was captured for that cluster in the window — it is not a zero, and averaging or charting it as one would misstate the period. dataDirtySince is non-null while a recalculation is pending, meaning the numbers predate the latest configuration change.
223
+ # @param organization_id [String]
224
+ # @param project_id [String]
225
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
226
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
227
+ # @param [Hash] opts the optional parameters
228
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated.
229
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
230
+ # @option opts [Array<String>] :query_cluster_ids Restrict to these clusters. Each must belong to the project.
231
+ # @option opts [Boolean] :include_ungrouped_queries Sent alone, returns only the ungrouped bucket rather than adding it to every cluster. (default to false)
232
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
233
+ def get_cluster_breakdown_with_http_info(organization_id, project_id, date_from, date_to, opts = {})
234
+ if @api_client.config.debugging
235
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_cluster_breakdown ...'
236
+ end
237
+ # verify the required parameter 'organization_id' is set
238
+ if @api_client.config.client_side_validation && organization_id.nil?
239
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_cluster_breakdown"
240
+ end
241
+ # verify the required parameter 'project_id' is set
242
+ if @api_client.config.client_side_validation && project_id.nil?
243
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_cluster_breakdown"
244
+ end
245
+ # verify the required parameter 'date_from' is set
246
+ if @api_client.config.client_side_validation && date_from.nil?
247
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.get_cluster_breakdown"
248
+ end
249
+ # verify the required parameter 'date_to' is set
250
+ if @api_client.config.client_side_validation && date_to.nil?
251
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.get_cluster_breakdown"
252
+ end
253
+ allowable_values = ["chatgpt", "perplexity", "google_ai_overview", "google_ai_mode", "google_serp", "google_shopping"]
254
+ if @api_client.config.client_side_validation && opts[:'engines'] && !opts[:'engines'].all? { |item| allowable_values.include?(item) }
255
+ fail ArgumentError, "invalid value for \"engines\", must include one of #{allowable_values}"
256
+ end
257
+ # resource path
258
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/metrics/clusters'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
259
+
260
+ # query parameters
261
+ query_params = opts[:query_params] || {}
262
+ query_params[:'dateFrom'] = date_from
263
+ query_params[:'dateTo'] = date_to
264
+ query_params[:'engines'] = @api_client.build_collection_param(opts[:'engines'], :multi) if !opts[:'engines'].nil?
265
+ query_params[:'countries'] = @api_client.build_collection_param(opts[:'countries'], :multi) if !opts[:'countries'].nil?
266
+ query_params[:'queryClusterIds'] = @api_client.build_collection_param(opts[:'query_cluster_ids'], :multi) if !opts[:'query_cluster_ids'].nil?
267
+ query_params[:'includeUngroupedQueries'] = opts[:'include_ungrouped_queries'] if !opts[:'include_ungrouped_queries'].nil?
268
+
269
+ # header parameters
270
+ header_params = opts[:header_params] || {}
271
+
272
+ # form parameters
273
+ form_params = opts[:form_params] || {}
274
+
275
+ # http body (model)
276
+ post_body = opts[:debug_body]
277
+
278
+ # return_type
279
+ return_type = opts[:debug_return_type]
280
+
281
+ # auth_names
282
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
283
+
284
+ new_options = opts.merge(
285
+ :operation => :"AnalyticsApi.get_cluster_breakdown",
286
+ :header_params => header_params,
287
+ :query_params => query_params,
288
+ :form_params => form_params,
289
+ :body => post_body,
290
+ :auth_names => auth_names,
291
+ :return_type => return_type
292
+ )
293
+
294
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
295
+ if @api_client.config.debugging
296
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_cluster_breakdown\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
297
+ end
298
+ return data, status_code, headers
299
+ end
300
+
301
+ # Head-to-head record of the brand against each tracked competitor
302
+ # Minimum role: viewer. Restricted to the AI answers where the brand and a competitor are BOTH mentioned, one row per tracked competitor: sharedResponseCount is how many such answers there are, and brandWins / competitorWins / ties split them by who holds the better (lower) best mention position. winRate is the percentage 0-100 of those answers the brand wins; avgOwnPosition and avgCompetitorPosition are the average best mention position each side held, 1-based, so LOWER is better. exampleQueryText and exampleAiResponseId point at one representative shared answer. Every nullable field means \"not known yet\" rather than zero: a null winRate or average position is the absence of a shared answer in the window, not a record of losing. Tracked competitors only.
303
+ # @param organization_id [String]
304
+ # @param project_id [String]
305
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
306
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
307
+ # @param [Hash] opts the optional parameters
308
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated. SERP and Shopping carry no AI answer text, so they contribute no co-occurrence.
309
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
310
+ # @option opts [String] :competitor_id Restricts the answer to a single tracked competitor. Omit it for every tracked competitor. The available-filters endpoint lists the valid ids.
311
+ # @return [nil]
312
+ def get_competitor_co_occurrence(organization_id, project_id, date_from, date_to, opts = {})
313
+ get_competitor_co_occurrence_with_http_info(organization_id, project_id, date_from, date_to, opts)
314
+ nil
315
+ end
316
+
317
+ # Head-to-head record of the brand against each tracked competitor
318
+ # Minimum role: viewer. Restricted to the AI answers where the brand and a competitor are BOTH mentioned, one row per tracked competitor: sharedResponseCount is how many such answers there are, and brandWins / competitorWins / ties split them by who holds the better (lower) best mention position. winRate is the percentage 0-100 of those answers the brand wins; avgOwnPosition and avgCompetitorPosition are the average best mention position each side held, 1-based, so LOWER is better. exampleQueryText and exampleAiResponseId point at one representative shared answer. Every nullable field means \&quot;not known yet\&quot; rather than zero: a null winRate or average position is the absence of a shared answer in the window, not a record of losing. Tracked competitors only.
319
+ # @param organization_id [String]
320
+ # @param project_id [String]
321
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
322
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
323
+ # @param [Hash] opts the optional parameters
324
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated. SERP and Shopping carry no AI answer text, so they contribute no co-occurrence.
325
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
326
+ # @option opts [String] :competitor_id Restricts the answer to a single tracked competitor. Omit it for every tracked competitor. The available-filters endpoint lists the valid ids.
327
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
328
+ def get_competitor_co_occurrence_with_http_info(organization_id, project_id, date_from, date_to, opts = {})
329
+ if @api_client.config.debugging
330
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_competitor_co_occurrence ...'
331
+ end
332
+ # verify the required parameter 'organization_id' is set
333
+ if @api_client.config.client_side_validation && organization_id.nil?
334
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_competitor_co_occurrence"
335
+ end
336
+ # verify the required parameter 'project_id' is set
337
+ if @api_client.config.client_side_validation && project_id.nil?
338
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_competitor_co_occurrence"
339
+ end
340
+ # verify the required parameter 'date_from' is set
341
+ if @api_client.config.client_side_validation && date_from.nil?
342
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.get_competitor_co_occurrence"
343
+ end
344
+ # verify the required parameter 'date_to' is set
345
+ if @api_client.config.client_side_validation && date_to.nil?
346
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.get_competitor_co_occurrence"
347
+ end
348
+ allowable_values = ["chatgpt", "perplexity", "google_ai_overview", "google_ai_mode", "google_serp", "google_shopping"]
349
+ if @api_client.config.client_side_validation && opts[:'engines'] && !opts[:'engines'].all? { |item| allowable_values.include?(item) }
350
+ fail ArgumentError, "invalid value for \"engines\", must include one of #{allowable_values}"
351
+ end
352
+ # resource path
353
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/competitors/co-occurrence'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
354
+
355
+ # query parameters
356
+ query_params = opts[:query_params] || {}
357
+ query_params[:'dateFrom'] = date_from
358
+ query_params[:'dateTo'] = date_to
359
+ query_params[:'engines'] = @api_client.build_collection_param(opts[:'engines'], :multi) if !opts[:'engines'].nil?
360
+ query_params[:'countries'] = @api_client.build_collection_param(opts[:'countries'], :multi) if !opts[:'countries'].nil?
361
+ query_params[:'competitorId'] = opts[:'competitor_id'] if !opts[:'competitor_id'].nil?
362
+
363
+ # header parameters
364
+ header_params = opts[:header_params] || {}
365
+
366
+ # form parameters
367
+ form_params = opts[:form_params] || {}
368
+
369
+ # http body (model)
370
+ post_body = opts[:debug_body]
371
+
372
+ # return_type
373
+ return_type = opts[:debug_return_type]
374
+
375
+ # auth_names
376
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
377
+
378
+ new_options = opts.merge(
379
+ :operation => :"AnalyticsApi.get_competitor_co_occurrence",
380
+ :header_params => header_params,
381
+ :query_params => query_params,
382
+ :form_params => form_params,
383
+ :body => post_body,
384
+ :auth_names => auth_names,
385
+ :return_type => return_type
386
+ )
387
+
388
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
389
+ if @api_client.config.debugging
390
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_competitor_co_occurrence\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
391
+ end
392
+ return data, status_code, headers
393
+ end
394
+
395
+ # Composition of a project brand mentions in AI answers
396
+ # Minimum role: viewer. Counts of the project brand's own text mentions in AI answers over a date window, grouped three ways: byType (recommendation, comparison, listing, example, reference), byTone (positive, neutral, negative) and byQualifier (direct, conditional — a conditional mention is one the answer hedged with a condition). These are the inputs behind the Share of Voice weighted score. Every bucket is always present and is a plain count, never null: a zero means no mention of that kind was found in the window. The three groupings count the same mentions, so each one sums to the same total. Only the project brand is counted, never a competitor, and only mentions inside the answer text — a citation of the brand's URL is not a mention here. Only AI answer engines produce mentions, so restricting engines to google_serp or google_shopping alone returns all zeros. For the positive/neutral/negative split per engine and per competitor use the sentiment endpoint.
397
+ # @param organization_id [String]
398
+ # @param project_id [String]
399
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
400
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
401
+ # @param [Hash] opts the optional parameters
402
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated. Non-AI engines contribute no mentions.
403
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
404
+ # @return [nil]
405
+ def get_mention_mix(organization_id, project_id, date_from, date_to, opts = {})
406
+ get_mention_mix_with_http_info(organization_id, project_id, date_from, date_to, opts)
407
+ nil
408
+ end
409
+
410
+ # Composition of a project brand mentions in AI answers
411
+ # Minimum role: viewer. Counts of the project brand&#39;s own text mentions in AI answers over a date window, grouped three ways: byType (recommendation, comparison, listing, example, reference), byTone (positive, neutral, negative) and byQualifier (direct, conditional — a conditional mention is one the answer hedged with a condition). These are the inputs behind the Share of Voice weighted score. Every bucket is always present and is a plain count, never null: a zero means no mention of that kind was found in the window. The three groupings count the same mentions, so each one sums to the same total. Only the project brand is counted, never a competitor, and only mentions inside the answer text — a citation of the brand&#39;s URL is not a mention here. Only AI answer engines produce mentions, so restricting engines to google_serp or google_shopping alone returns all zeros. For the positive/neutral/negative split per engine and per competitor use the sentiment endpoint.
412
+ # @param organization_id [String]
413
+ # @param project_id [String]
414
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
415
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
416
+ # @param [Hash] opts the optional parameters
417
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated. Non-AI engines contribute no mentions.
418
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
419
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
420
+ def get_mention_mix_with_http_info(organization_id, project_id, date_from, date_to, opts = {})
421
+ if @api_client.config.debugging
422
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_mention_mix ...'
423
+ end
424
+ # verify the required parameter 'organization_id' is set
425
+ if @api_client.config.client_side_validation && organization_id.nil?
426
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_mention_mix"
427
+ end
428
+ # verify the required parameter 'project_id' is set
429
+ if @api_client.config.client_side_validation && project_id.nil?
430
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_mention_mix"
431
+ end
432
+ # verify the required parameter 'date_from' is set
433
+ if @api_client.config.client_side_validation && date_from.nil?
434
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.get_mention_mix"
435
+ end
436
+ # verify the required parameter 'date_to' is set
437
+ if @api_client.config.client_side_validation && date_to.nil?
438
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.get_mention_mix"
439
+ end
440
+ allowable_values = ["chatgpt", "perplexity", "google_ai_overview", "google_ai_mode", "google_serp", "google_shopping"]
441
+ if @api_client.config.client_side_validation && opts[:'engines'] && !opts[:'engines'].all? { |item| allowable_values.include?(item) }
442
+ fail ArgumentError, "invalid value for \"engines\", must include one of #{allowable_values}"
443
+ end
444
+ # resource path
445
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/mentions/mix'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
446
+
447
+ # query parameters
448
+ query_params = opts[:query_params] || {}
449
+ query_params[:'dateFrom'] = date_from
450
+ query_params[:'dateTo'] = date_to
451
+ query_params[:'engines'] = @api_client.build_collection_param(opts[:'engines'], :multi) if !opts[:'engines'].nil?
452
+ query_params[:'countries'] = @api_client.build_collection_param(opts[:'countries'], :multi) if !opts[:'countries'].nil?
453
+
454
+ # header parameters
455
+ header_params = opts[:header_params] || {}
456
+
457
+ # form parameters
458
+ form_params = opts[:form_params] || {}
459
+
460
+ # http body (model)
461
+ post_body = opts[:debug_body]
462
+
463
+ # return_type
464
+ return_type = opts[:debug_return_type]
465
+
466
+ # auth_names
467
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
468
+
469
+ new_options = opts.merge(
470
+ :operation => :"AnalyticsApi.get_mention_mix",
471
+ :header_params => header_params,
472
+ :query_params => query_params,
473
+ :form_params => form_params,
474
+ :body => post_body,
475
+ :auth_names => auth_names,
476
+ :return_type => return_type
477
+ )
478
+
479
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
480
+ if @api_client.config.debugging
481
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_mention_mix\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
482
+ end
483
+ return data, status_code, headers
484
+ end
485
+
486
+ # Sample of the raw AI mention texts of a project
487
+ # Minimum role: viewer. A paginated page of the individual mention texts behind the aggregate numbers, for qualitative review and for checking sentiment labels by eye. Each sample carries the mention text, the engine and country it was seen in, the tracked query that produced it, its sentiment and mention type, and mentionPosition — a 1-based rank inside the answer where LOWER is better, always present. `total` counts every mention matching the filters, not the size of the page returned. Only mentions inside the answer text are returned: a citation of the brand URL is not a mention here, and only AI answer engines produce mentions, so restricting engines to google_serp or google_shopping alone returns an empty page rather than an error. Two fields carry a \"not known\" rather than a zero: country is null and queryText is empty when the tracked query behind the mention has since been deleted, and competitorId is null when the mention row stores no competitor id — which is NOT an assertion that the mention is about your own brand, since an untracked competitor also stores none. Read mentionRelation instead: `own` and `tracked-competitor` are what the scraper resolved to a configured entity, `untracked-competitor` is a rival the project does not track, and null means the row predates the field. brandName carries the mentioned brand, and is the only way to name an untracked competitor, which has no competitor id to resolve one from. Omitting the competitorId filter returns exactly the rows with no competitor id stored — that is, own-brand AND untracked-competitor mentions together, not every competitor; pass a competitor UUID to restrict the page to that competitor, and use mentionRelation to separate the rest. For the aggregate positive/neutral/negative split use the sentiment endpoint, and for weighted mention-type counts the mention mix endpoint.
488
+ # @param organization_id [String]
489
+ # @param project_id [String]
490
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
491
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
492
+ # @param [Hash] opts the optional parameters
493
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated. Non-AI engines contribute no mentions.
494
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
495
+ # @option opts [String] :sentiment Restrict to one sentiment label. Omit for every sentiment.
496
+ # @option opts [String] :mention_type Restrict to one mention type. Omit for every type.
497
+ # @option opts [String] :competitor_id UUID of a single competitor, as listed by the available-filters endpoint (competitors[].id). Omitted, the page is restricted to mentions storing no competitor id, which is own-brand and untracked-competitor mentions together — see the operation description and read mentionRelation to tell them apart.
498
+ # @option opts [String] :sort_by recent: newest first. negative: negative sentiment first, then neutral, then positive, newest first inside each. engine and country: grouped alphabetically, newest first inside each group. An unknown value is rejected, not replaced by the default. (default to 'recent')
499
+ # @option opts [Integer] :limit Page size. A larger value is rejected, never silently reduced. (default to 20)
500
+ # @option opts [Integer] :offset Number of matching mentions to skip before the page starts. (default to 0)
501
+ # @return [nil]
502
+ def get_mention_samples(organization_id, project_id, date_from, date_to, opts = {})
503
+ get_mention_samples_with_http_info(organization_id, project_id, date_from, date_to, opts)
504
+ nil
505
+ end
506
+
507
+ # Sample of the raw AI mention texts of a project
508
+ # Minimum role: viewer. A paginated page of the individual mention texts behind the aggregate numbers, for qualitative review and for checking sentiment labels by eye. Each sample carries the mention text, the engine and country it was seen in, the tracked query that produced it, its sentiment and mention type, and mentionPosition — a 1-based rank inside the answer where LOWER is better, always present. &#x60;total&#x60; counts every mention matching the filters, not the size of the page returned. Only mentions inside the answer text are returned: a citation of the brand URL is not a mention here, and only AI answer engines produce mentions, so restricting engines to google_serp or google_shopping alone returns an empty page rather than an error. Two fields carry a \&quot;not known\&quot; rather than a zero: country is null and queryText is empty when the tracked query behind the mention has since been deleted, and competitorId is null when the mention row stores no competitor id — which is NOT an assertion that the mention is about your own brand, since an untracked competitor also stores none. Read mentionRelation instead: &#x60;own&#x60; and &#x60;tracked-competitor&#x60; are what the scraper resolved to a configured entity, &#x60;untracked-competitor&#x60; is a rival the project does not track, and null means the row predates the field. brandName carries the mentioned brand, and is the only way to name an untracked competitor, which has no competitor id to resolve one from. Omitting the competitorId filter returns exactly the rows with no competitor id stored — that is, own-brand AND untracked-competitor mentions together, not every competitor; pass a competitor UUID to restrict the page to that competitor, and use mentionRelation to separate the rest. For the aggregate positive/neutral/negative split use the sentiment endpoint, and for weighted mention-type counts the mention mix endpoint.
509
+ # @param organization_id [String]
510
+ # @param project_id [String]
511
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
512
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
513
+ # @param [Hash] opts the optional parameters
514
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated. Non-AI engines contribute no mentions.
515
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
516
+ # @option opts [String] :sentiment Restrict to one sentiment label. Omit for every sentiment.
517
+ # @option opts [String] :mention_type Restrict to one mention type. Omit for every type.
518
+ # @option opts [String] :competitor_id UUID of a single competitor, as listed by the available-filters endpoint (competitors[].id). Omitted, the page is restricted to mentions storing no competitor id, which is own-brand and untracked-competitor mentions together — see the operation description and read mentionRelation to tell them apart.
519
+ # @option opts [String] :sort_by recent: newest first. negative: negative sentiment first, then neutral, then positive, newest first inside each. engine and country: grouped alphabetically, newest first inside each group. An unknown value is rejected, not replaced by the default. (default to 'recent')
520
+ # @option opts [Integer] :limit Page size. A larger value is rejected, never silently reduced. (default to 20)
521
+ # @option opts [Integer] :offset Number of matching mentions to skip before the page starts. (default to 0)
522
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
523
+ def get_mention_samples_with_http_info(organization_id, project_id, date_from, date_to, opts = {})
524
+ if @api_client.config.debugging
525
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_mention_samples ...'
526
+ end
527
+ # verify the required parameter 'organization_id' is set
528
+ if @api_client.config.client_side_validation && organization_id.nil?
529
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_mention_samples"
530
+ end
531
+ # verify the required parameter 'project_id' is set
532
+ if @api_client.config.client_side_validation && project_id.nil?
533
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_mention_samples"
534
+ end
535
+ # verify the required parameter 'date_from' is set
536
+ if @api_client.config.client_side_validation && date_from.nil?
537
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.get_mention_samples"
538
+ end
539
+ # verify the required parameter 'date_to' is set
540
+ if @api_client.config.client_side_validation && date_to.nil?
541
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.get_mention_samples"
542
+ end
543
+ allowable_values = ["chatgpt", "perplexity", "google_ai_overview", "google_ai_mode", "google_serp", "google_shopping"]
544
+ if @api_client.config.client_side_validation && opts[:'engines'] && !opts[:'engines'].all? { |item| allowable_values.include?(item) }
545
+ fail ArgumentError, "invalid value for \"engines\", must include one of #{allowable_values}"
546
+ end
547
+ allowable_values = ["positive", "neutral", "negative"]
548
+ if @api_client.config.client_side_validation && opts[:'sentiment'] && !allowable_values.include?(opts[:'sentiment'])
549
+ fail ArgumentError, "invalid value for \"sentiment\", must be one of #{allowable_values}"
550
+ end
551
+ allowable_values = ["recommendation", "comparison", "listing", "example", "reference"]
552
+ if @api_client.config.client_side_validation && opts[:'mention_type'] && !allowable_values.include?(opts[:'mention_type'])
553
+ fail ArgumentError, "invalid value for \"mention_type\", must be one of #{allowable_values}"
554
+ end
555
+ allowable_values = ["recent", "negative", "engine", "country"]
556
+ if @api_client.config.client_side_validation && opts[:'sort_by'] && !allowable_values.include?(opts[:'sort_by'])
557
+ fail ArgumentError, "invalid value for \"sort_by\", must be one of #{allowable_values}"
558
+ end
559
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100
560
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AnalyticsApi.get_mention_samples, must be smaller than or equal to 100.'
561
+ end
562
+
563
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1
564
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AnalyticsApi.get_mention_samples, must be greater than or equal to 1.'
565
+ end
566
+
567
+ if @api_client.config.client_side_validation && !opts[:'offset'].nil? && opts[:'offset'] < 0
568
+ fail ArgumentError, 'invalid value for "opts[:"offset"]" when calling AnalyticsApi.get_mention_samples, must be greater than or equal to 0.'
569
+ end
570
+
571
+ # resource path
572
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/mentions'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
573
+
574
+ # query parameters
575
+ query_params = opts[:query_params] || {}
576
+ query_params[:'dateFrom'] = date_from
577
+ query_params[:'dateTo'] = date_to
578
+ query_params[:'engines'] = @api_client.build_collection_param(opts[:'engines'], :multi) if !opts[:'engines'].nil?
579
+ query_params[:'countries'] = @api_client.build_collection_param(opts[:'countries'], :multi) if !opts[:'countries'].nil?
580
+ query_params[:'sentiment'] = opts[:'sentiment'] if !opts[:'sentiment'].nil?
581
+ query_params[:'mentionType'] = opts[:'mention_type'] if !opts[:'mention_type'].nil?
582
+ query_params[:'competitorId'] = opts[:'competitor_id'] if !opts[:'competitor_id'].nil?
583
+ query_params[:'sortBy'] = opts[:'sort_by'] if !opts[:'sort_by'].nil?
584
+ query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
585
+ query_params[:'offset'] = opts[:'offset'] if !opts[:'offset'].nil?
586
+
587
+ # header parameters
588
+ header_params = opts[:header_params] || {}
589
+ # HTTP header 'Accept' (if needed)
590
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
591
+
592
+ # form parameters
593
+ form_params = opts[:form_params] || {}
594
+
595
+ # http body (model)
596
+ post_body = opts[:debug_body]
597
+
598
+ # return_type
599
+ return_type = opts[:debug_return_type]
600
+
601
+ # auth_names
602
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
603
+
604
+ new_options = opts.merge(
605
+ :operation => :"AnalyticsApi.get_mention_samples",
606
+ :header_params => header_params,
607
+ :query_params => query_params,
608
+ :form_params => form_params,
609
+ :body => post_body,
610
+ :auth_names => auth_names,
611
+ :return_type => return_type
612
+ )
613
+
614
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
615
+ if @api_client.config.debugging
616
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_mention_samples\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
617
+ end
618
+ return data, status_code, headers
619
+ end
620
+
621
+ # Map everyday wording to a metric and the operation that serves it
622
+ # Static reference, no project data. Each entry gives a metric, the everyday words people use for it, its unit and range, whether higher or lower is better, the operation that returns it, and example questions. Useful when turning a vague or non-technical request into the right call.
623
+ # @param [Hash] opts the optional parameters
624
+ # @return [GetMetricGlossary200Response]
625
+ def get_metric_glossary(opts = {})
626
+ data, _status_code, _headers = get_metric_glossary_with_http_info(opts)
627
+ data
628
+ end
629
+
630
+ # Map everyday wording to a metric and the operation that serves it
631
+ # Static reference, no project data. Each entry gives a metric, the everyday words people use for it, its unit and range, whether higher or lower is better, the operation that returns it, and example questions. Useful when turning a vague or non-technical request into the right call.
632
+ # @param [Hash] opts the optional parameters
633
+ # @return [Array<(GetMetricGlossary200Response, Integer, Hash)>] GetMetricGlossary200Response data, response status code and response headers
634
+ def get_metric_glossary_with_http_info(opts = {})
635
+ if @api_client.config.debugging
636
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_metric_glossary ...'
637
+ end
638
+ # resource path
639
+ local_var_path = '/api/v1/metric-glossary'
640
+
641
+ # query parameters
642
+ query_params = opts[:query_params] || {}
643
+
644
+ # header parameters
645
+ header_params = opts[:header_params] || {}
646
+ # HTTP header 'Accept' (if needed)
647
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
648
+
649
+ # form parameters
650
+ form_params = opts[:form_params] || {}
651
+
652
+ # http body (model)
653
+ post_body = opts[:debug_body]
654
+
655
+ # return_type
656
+ return_type = opts[:debug_return_type] || 'GetMetricGlossary200Response'
657
+
658
+ # auth_names
659
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
660
+
661
+ new_options = opts.merge(
662
+ :operation => :"AnalyticsApi.get_metric_glossary",
663
+ :header_params => header_params,
664
+ :query_params => query_params,
665
+ :form_params => form_params,
666
+ :body => post_body,
667
+ :auth_names => auth_names,
668
+ :return_type => return_type
669
+ )
670
+
671
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
672
+ if @api_client.config.debugging
673
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_metric_glossary\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
674
+ end
675
+ return data, status_code, headers
676
+ end
677
+
678
+ # Snapshot rank-health board across an organization active projects
679
+ # Minimum role: viewer. One row per active project — share of voice, mention rate, average mention position, positivity index and tracked-query count — ordered by share of voice, plus an organization-level aggregate of the same metrics. Archived projects are excluded. This is a current-state snapshot and takes no date window; for a date-ranged comparison call the per-project operations (getProjectMetrics, getProjectTimeSeries) for each projectId returned here. Every metric is nullable, and a null means the project has no rank data yet — not a score of zero. Higher is better for shareOfVoice, mentionRate and positivityIndex; avgMentionPosition is a 1-based rank, so LOWER is better.
680
+ # @param organization_id [String]
681
+ # @param [Hash] opts the optional parameters
682
+ # @return [GetOrganizationOverview200Response]
683
+ def get_organization_overview(organization_id, opts = {})
684
+ data, _status_code, _headers = get_organization_overview_with_http_info(organization_id, opts)
685
+ data
686
+ end
687
+
688
+ # Snapshot rank-health board across an organization active projects
689
+ # Minimum role: viewer. One row per active project — share of voice, mention rate, average mention position, positivity index and tracked-query count — ordered by share of voice, plus an organization-level aggregate of the same metrics. Archived projects are excluded. This is a current-state snapshot and takes no date window; for a date-ranged comparison call the per-project operations (getProjectMetrics, getProjectTimeSeries) for each projectId returned here. Every metric is nullable, and a null means the project has no rank data yet — not a score of zero. Higher is better for shareOfVoice, mentionRate and positivityIndex; avgMentionPosition is a 1-based rank, so LOWER is better.
690
+ # @param organization_id [String]
691
+ # @param [Hash] opts the optional parameters
692
+ # @return [Array<(GetOrganizationOverview200Response, Integer, Hash)>] GetOrganizationOverview200Response data, response status code and response headers
693
+ def get_organization_overview_with_http_info(organization_id, opts = {})
694
+ if @api_client.config.debugging
695
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_organization_overview ...'
696
+ end
697
+ # verify the required parameter 'organization_id' is set
698
+ if @api_client.config.client_side_validation && organization_id.nil?
699
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_organization_overview"
700
+ end
701
+ # resource path
702
+ local_var_path = '/api/v1/organizations/{organizationId}/overview'.sub('{organizationId}', CGI.escape(organization_id.to_s))
703
+
704
+ # query parameters
705
+ query_params = opts[:query_params] || {}
706
+
707
+ # header parameters
708
+ header_params = opts[:header_params] || {}
709
+ # HTTP header 'Accept' (if needed)
710
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
711
+
712
+ # form parameters
713
+ form_params = opts[:form_params] || {}
714
+
715
+ # http body (model)
716
+ post_body = opts[:debug_body]
717
+
718
+ # return_type
719
+ return_type = opts[:debug_return_type] || 'GetOrganizationOverview200Response'
720
+
721
+ # auth_names
722
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
723
+
724
+ new_options = opts.merge(
725
+ :operation => :"AnalyticsApi.get_organization_overview",
726
+ :header_params => header_params,
727
+ :query_params => query_params,
728
+ :form_params => form_params,
729
+ :body => post_body,
730
+ :auth_names => auth_names,
731
+ :return_type => return_type
732
+ )
733
+
734
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
735
+ if @api_client.config.debugging
736
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_organization_overview\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
737
+ end
738
+ return data, status_code, headers
739
+ end
740
+
741
+ # Headline visibility metrics of a project
742
+ # Minimum role: viewer. The project overview over a date window: share of voice (own and per competitor), mention / SERP / shopping rates, average and best positions, position stability, the sentiment split and the position-distribution buckets. Positions are 1-based, so a LOWER number is better; rates, the positivity index and share of voice are percentages from 0 to 100, where HIGHER is better. Every `trend*` field is the signed change against the immediately preceding window of the same length: negative means an improved position, positive means an improved rate or score. A null metric means \"not known yet\", never zero: a scalar is null when the window holds no checks at all, and a `trend*` field is null when there is no earlier window to compare against. The counters (`mentionCount`, `*TrackedQueryCount`, `*QueriesWithResult`, `sentiment*` and `mentionTypeCounts`) are genuine zeros instead, so an empty window reads as zero counts with null rates. `dataDirtySince` is non-null while a recalculation is pending, meaning the figures may still move for dates from then on.
743
+ # @param organization_id [String]
744
+ # @param project_id [String]
745
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
746
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
747
+ # @param [Hash] opts the optional parameters
748
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated.
749
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
750
+ # @option opts [Array<String>] :query_cluster_ids Restrict to these keyword clusters. Each must belong to this project.
751
+ # @option opts [Boolean] :include_ungrouped_queries Only meaningful together with queryClusterIds: also counts the tracked queries that belong to no cluster. (default to false)
752
+ # @return [nil]
753
+ def get_project_metrics(organization_id, project_id, date_from, date_to, opts = {})
754
+ get_project_metrics_with_http_info(organization_id, project_id, date_from, date_to, opts)
755
+ nil
756
+ end
757
+
758
+ # Headline visibility metrics of a project
759
+ # Minimum role: viewer. The project overview over a date window: share of voice (own and per competitor), mention / SERP / shopping rates, average and best positions, position stability, the sentiment split and the position-distribution buckets. Positions are 1-based, so a LOWER number is better; rates, the positivity index and share of voice are percentages from 0 to 100, where HIGHER is better. Every &#x60;trend*&#x60; field is the signed change against the immediately preceding window of the same length: negative means an improved position, positive means an improved rate or score. A null metric means \&quot;not known yet\&quot;, never zero: a scalar is null when the window holds no checks at all, and a &#x60;trend*&#x60; field is null when there is no earlier window to compare against. The counters (&#x60;mentionCount&#x60;, &#x60;*TrackedQueryCount&#x60;, &#x60;*QueriesWithResult&#x60;, &#x60;sentiment*&#x60; and &#x60;mentionTypeCounts&#x60;) are genuine zeros instead, so an empty window reads as zero counts with null rates. &#x60;dataDirtySince&#x60; is non-null while a recalculation is pending, meaning the figures may still move for dates from then on.
760
+ # @param organization_id [String]
761
+ # @param project_id [String]
762
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
763
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
764
+ # @param [Hash] opts the optional parameters
765
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated.
766
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
767
+ # @option opts [Array<String>] :query_cluster_ids Restrict to these keyword clusters. Each must belong to this project.
768
+ # @option opts [Boolean] :include_ungrouped_queries Only meaningful together with queryClusterIds: also counts the tracked queries that belong to no cluster. (default to false)
769
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
770
+ def get_project_metrics_with_http_info(organization_id, project_id, date_from, date_to, opts = {})
771
+ if @api_client.config.debugging
772
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_project_metrics ...'
773
+ end
774
+ # verify the required parameter 'organization_id' is set
775
+ if @api_client.config.client_side_validation && organization_id.nil?
776
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_project_metrics"
777
+ end
778
+ # verify the required parameter 'project_id' is set
779
+ if @api_client.config.client_side_validation && project_id.nil?
780
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_project_metrics"
781
+ end
782
+ # verify the required parameter 'date_from' is set
783
+ if @api_client.config.client_side_validation && date_from.nil?
784
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.get_project_metrics"
785
+ end
786
+ # verify the required parameter 'date_to' is set
787
+ if @api_client.config.client_side_validation && date_to.nil?
788
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.get_project_metrics"
789
+ end
790
+ allowable_values = ["chatgpt", "perplexity", "google_ai_overview", "google_ai_mode", "google_serp", "google_shopping"]
791
+ if @api_client.config.client_side_validation && opts[:'engines'] && !opts[:'engines'].all? { |item| allowable_values.include?(item) }
792
+ fail ArgumentError, "invalid value for \"engines\", must include one of #{allowable_values}"
793
+ end
794
+ # resource path
795
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/metrics'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
796
+
797
+ # query parameters
798
+ query_params = opts[:query_params] || {}
799
+ query_params[:'dateFrom'] = date_from
800
+ query_params[:'dateTo'] = date_to
801
+ query_params[:'engines'] = @api_client.build_collection_param(opts[:'engines'], :multi) if !opts[:'engines'].nil?
802
+ query_params[:'countries'] = @api_client.build_collection_param(opts[:'countries'], :multi) if !opts[:'countries'].nil?
803
+ query_params[:'queryClusterIds'] = @api_client.build_collection_param(opts[:'query_cluster_ids'], :multi) if !opts[:'query_cluster_ids'].nil?
804
+ query_params[:'includeUngroupedQueries'] = opts[:'include_ungrouped_queries'] if !opts[:'include_ungrouped_queries'].nil?
805
+
806
+ # header parameters
807
+ header_params = opts[:header_params] || {}
808
+
809
+ # form parameters
810
+ form_params = opts[:form_params] || {}
811
+
812
+ # http body (model)
813
+ post_body = opts[:debug_body]
814
+
815
+ # return_type
816
+ return_type = opts[:debug_return_type]
817
+
818
+ # auth_names
819
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
820
+
821
+ new_options = opts.merge(
822
+ :operation => :"AnalyticsApi.get_project_metrics",
823
+ :header_params => header_params,
824
+ :query_params => query_params,
825
+ :form_params => form_params,
826
+ :body => post_body,
827
+ :auth_names => auth_names,
828
+ :return_type => return_type
829
+ )
830
+
831
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
832
+ if @api_client.config.debugging
833
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_project_metrics\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
834
+ end
835
+ return data, status_code, headers
836
+ end
837
+
838
+ # Sentiment breakdown of a project brand mentions
839
+ # Minimum role: viewer. Positive, neutral and negative split of the brand mentions in AI answers over a date window, per engine and per competitor. A null positivityIndex means no mentions were found in the window, which is not the same as a score of zero.
840
+ # @param organization_id [String]
841
+ # @param project_id [String]
842
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
843
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
844
+ # @param [Hash] opts the optional parameters
845
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated.
846
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
847
+ # @option opts [Array<String>] :query_cluster_ids
848
+ # @option opts [Boolean] :include_ungrouped_queries (default to false)
849
+ # @return [nil]
850
+ def get_project_sentiment(organization_id, project_id, date_from, date_to, opts = {})
851
+ get_project_sentiment_with_http_info(organization_id, project_id, date_from, date_to, opts)
852
+ nil
853
+ end
854
+
855
+ # Sentiment breakdown of a project brand mentions
856
+ # Minimum role: viewer. Positive, neutral and negative split of the brand mentions in AI answers over a date window, per engine and per competitor. A null positivityIndex means no mentions were found in the window, which is not the same as a score of zero.
857
+ # @param organization_id [String]
858
+ # @param project_id [String]
859
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
860
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
861
+ # @param [Hash] opts the optional parameters
862
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated.
863
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
864
+ # @option opts [Array<String>] :query_cluster_ids
865
+ # @option opts [Boolean] :include_ungrouped_queries (default to false)
866
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
867
+ def get_project_sentiment_with_http_info(organization_id, project_id, date_from, date_to, opts = {})
868
+ if @api_client.config.debugging
869
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_project_sentiment ...'
870
+ end
871
+ # verify the required parameter 'organization_id' is set
872
+ if @api_client.config.client_side_validation && organization_id.nil?
873
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_project_sentiment"
874
+ end
875
+ # verify the required parameter 'project_id' is set
876
+ if @api_client.config.client_side_validation && project_id.nil?
877
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_project_sentiment"
878
+ end
879
+ # verify the required parameter 'date_from' is set
880
+ if @api_client.config.client_side_validation && date_from.nil?
881
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.get_project_sentiment"
882
+ end
883
+ # verify the required parameter 'date_to' is set
884
+ if @api_client.config.client_side_validation && date_to.nil?
885
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.get_project_sentiment"
886
+ end
887
+ allowable_values = ["chatgpt", "perplexity", "google_ai_overview", "google_ai_mode", "google_serp", "google_shopping"]
888
+ if @api_client.config.client_side_validation && opts[:'engines'] && !opts[:'engines'].all? { |item| allowable_values.include?(item) }
889
+ fail ArgumentError, "invalid value for \"engines\", must include one of #{allowable_values}"
890
+ end
891
+ # resource path
892
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/sentiment'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
893
+
894
+ # query parameters
895
+ query_params = opts[:query_params] || {}
896
+ query_params[:'dateFrom'] = date_from
897
+ query_params[:'dateTo'] = date_to
898
+ query_params[:'engines'] = @api_client.build_collection_param(opts[:'engines'], :multi) if !opts[:'engines'].nil?
899
+ query_params[:'countries'] = @api_client.build_collection_param(opts[:'countries'], :multi) if !opts[:'countries'].nil?
900
+ query_params[:'queryClusterIds'] = @api_client.build_collection_param(opts[:'query_cluster_ids'], :multi) if !opts[:'query_cluster_ids'].nil?
901
+ query_params[:'includeUngroupedQueries'] = opts[:'include_ungrouped_queries'] if !opts[:'include_ungrouped_queries'].nil?
902
+
903
+ # header parameters
904
+ header_params = opts[:header_params] || {}
905
+
906
+ # form parameters
907
+ form_params = opts[:form_params] || {}
908
+
909
+ # http body (model)
910
+ post_body = opts[:debug_body]
911
+
912
+ # return_type
913
+ return_type = opts[:debug_return_type]
914
+
915
+ # auth_names
916
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
917
+
918
+ new_options = opts.merge(
919
+ :operation => :"AnalyticsApi.get_project_sentiment",
920
+ :header_params => header_params,
921
+ :query_params => query_params,
922
+ :form_params => form_params,
923
+ :body => post_body,
924
+ :auth_names => auth_names,
925
+ :return_type => return_type
926
+ )
927
+
928
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
929
+ if @api_client.config.debugging
930
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_project_sentiment\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
931
+ end
932
+ return data, status_code, headers
933
+ end
934
+
935
+ # Rank-tracking metrics of a project over time
936
+ # Minimum role: viewer. One point per bucket over the date window, each carrying the brand metrics and one same-shaped entry per requested competitor. Rank metrics (serp, shopping, mention, link) are 1-based averages where LOWER is better; positivity (0-100), shareOfVoice (0-100), mentionRate (0-100) and serpRate (0-100) are scores where higher is better. Every metric is nullable, and a null means no data was collected for that bucket, which is not the same as a value of zero. A project with no tracked queries returns an empty points list. Prefer weekly or monthly granularity over a long window to keep the response compact.
937
+ # @param organization_id [String]
938
+ # @param project_id [String]
939
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
940
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
941
+ # @param [Hash] opts the optional parameters
942
+ # @option opts [String] :granularity Bucket size of each point. (default to 'daily')
943
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated.
944
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
945
+ # @option opts [Array<String>] :query_cluster_ids
946
+ # @option opts [Boolean] :include_ungrouped_queries On its own this NARROWS the series to tracked queries that belong to no cluster; combined with queryClusterIds it widens those clusters to also cover them. (default to false)
947
+ # @option opts [Array<String>] :competitor_ids Repeatable, or comma-separated. Each id adds one series under the competitors map of every point.
948
+ # @return [nil]
949
+ def get_project_time_series(organization_id, project_id, date_from, date_to, opts = {})
950
+ get_project_time_series_with_http_info(organization_id, project_id, date_from, date_to, opts)
951
+ nil
952
+ end
953
+
954
+ # Rank-tracking metrics of a project over time
955
+ # Minimum role: viewer. One point per bucket over the date window, each carrying the brand metrics and one same-shaped entry per requested competitor. Rank metrics (serp, shopping, mention, link) are 1-based averages where LOWER is better; positivity (0-100), shareOfVoice (0-100), mentionRate (0-100) and serpRate (0-100) are scores where higher is better. Every metric is nullable, and a null means no data was collected for that bucket, which is not the same as a value of zero. A project with no tracked queries returns an empty points list. Prefer weekly or monthly granularity over a long window to keep the response compact.
956
+ # @param organization_id [String]
957
+ # @param project_id [String]
958
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
959
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
960
+ # @param [Hash] opts the optional parameters
961
+ # @option opts [String] :granularity Bucket size of each point. (default to 'daily')
962
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated.
963
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
964
+ # @option opts [Array<String>] :query_cluster_ids
965
+ # @option opts [Boolean] :include_ungrouped_queries On its own this NARROWS the series to tracked queries that belong to no cluster; combined with queryClusterIds it widens those clusters to also cover them. (default to false)
966
+ # @option opts [Array<String>] :competitor_ids Repeatable, or comma-separated. Each id adds one series under the competitors map of every point.
967
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
968
+ def get_project_time_series_with_http_info(organization_id, project_id, date_from, date_to, opts = {})
969
+ if @api_client.config.debugging
970
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_project_time_series ...'
971
+ end
972
+ # verify the required parameter 'organization_id' is set
973
+ if @api_client.config.client_side_validation && organization_id.nil?
974
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_project_time_series"
975
+ end
976
+ # verify the required parameter 'project_id' is set
977
+ if @api_client.config.client_side_validation && project_id.nil?
978
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_project_time_series"
979
+ end
980
+ # verify the required parameter 'date_from' is set
981
+ if @api_client.config.client_side_validation && date_from.nil?
982
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.get_project_time_series"
983
+ end
984
+ # verify the required parameter 'date_to' is set
985
+ if @api_client.config.client_side_validation && date_to.nil?
986
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.get_project_time_series"
987
+ end
988
+ allowable_values = ["daily", "weekly", "monthly"]
989
+ if @api_client.config.client_side_validation && opts[:'granularity'] && !allowable_values.include?(opts[:'granularity'])
990
+ fail ArgumentError, "invalid value for \"granularity\", must be one of #{allowable_values}"
991
+ end
992
+ allowable_values = ["chatgpt", "perplexity", "google_ai_overview", "google_ai_mode", "google_serp", "google_shopping"]
993
+ if @api_client.config.client_side_validation && opts[:'engines'] && !opts[:'engines'].all? { |item| allowable_values.include?(item) }
994
+ fail ArgumentError, "invalid value for \"engines\", must include one of #{allowable_values}"
995
+ end
996
+ # resource path
997
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/timeseries'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
998
+
999
+ # query parameters
1000
+ query_params = opts[:query_params] || {}
1001
+ query_params[:'dateFrom'] = date_from
1002
+ query_params[:'dateTo'] = date_to
1003
+ query_params[:'granularity'] = opts[:'granularity'] if !opts[:'granularity'].nil?
1004
+ query_params[:'engines'] = @api_client.build_collection_param(opts[:'engines'], :multi) if !opts[:'engines'].nil?
1005
+ query_params[:'countries'] = @api_client.build_collection_param(opts[:'countries'], :multi) if !opts[:'countries'].nil?
1006
+ query_params[:'queryClusterIds'] = @api_client.build_collection_param(opts[:'query_cluster_ids'], :multi) if !opts[:'query_cluster_ids'].nil?
1007
+ query_params[:'includeUngroupedQueries'] = opts[:'include_ungrouped_queries'] if !opts[:'include_ungrouped_queries'].nil?
1008
+ query_params[:'competitorIds'] = @api_client.build_collection_param(opts[:'competitor_ids'], :multi) if !opts[:'competitor_ids'].nil?
1009
+
1010
+ # header parameters
1011
+ header_params = opts[:header_params] || {}
1012
+
1013
+ # form parameters
1014
+ form_params = opts[:form_params] || {}
1015
+
1016
+ # http body (model)
1017
+ post_body = opts[:debug_body]
1018
+
1019
+ # return_type
1020
+ return_type = opts[:debug_return_type]
1021
+
1022
+ # auth_names
1023
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
1024
+
1025
+ new_options = opts.merge(
1026
+ :operation => :"AnalyticsApi.get_project_time_series",
1027
+ :header_params => header_params,
1028
+ :query_params => query_params,
1029
+ :form_params => form_params,
1030
+ :body => post_body,
1031
+ :auth_names => auth_names,
1032
+ :return_type => return_type
1033
+ )
1034
+
1035
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
1036
+ if @api_client.config.debugging
1037
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_project_time_series\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1038
+ end
1039
+ return data, status_code, headers
1040
+ end
1041
+
1042
+ # Tracked queries ranked by how much a metric moved
1043
+ # Minimum role: viewer. One row per tracked query — a single engine plus country — carrying its current metric envelope and the signed change against the immediately preceding window of equal length: a 7-day window is compared with the 7 days before it. Every trend delta is signed so that POSITIVE means improved, including the position trends, where the underlying avgSerpPosition / avgShoppingPosition / avgMentionPosition / avgLinkPosition are 1-based ranks and therefore LOWER is better. shareOfVoice and positivityIndex are percentages from 0 to 100, where HIGHER is better. Sorting applies to the trend keys only: sortOrder=desc gives the top gainers, asc the top losers. Every nullable field means \"not known yet\" rather than zero — a null position or shareOfVoice is a query with no data in the window, and a null positivityIndex or trend is a period with no mentions to score, neither of which is a record of losing ground. total counts the tracked queries the filters match, not the rows on this page. dataDirtySince is a date from which the rank data is being recomputed, or null when nothing is pending; while it is non-null the deltas may still move.
1044
+ # @param organization_id [String]
1045
+ # @param project_id [String]
1046
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window. The comparison window is the equally long stretch immediately before it.
1047
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
1048
+ # @param [Hash] opts the optional parameters
1049
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated.
1050
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
1051
+ # @option opts [String] :sort_by Which trend delta ranks the rows. Positions improve as they fall, so a positive delta is always an improvement whichever key you pick. An unknown value is rejected, not replaced by the default. (default to 'trend_share_of_voice')
1052
+ # @option opts [String] :sort_order desc for the top gainers, asc for the top losers. (default to 'desc')
1053
+ # @option opts [Integer] :limit Page size. A value above the maximum is rejected, never clamped. (default to 20)
1054
+ # @option opts [Integer] :offset (default to 0)
1055
+ # @return [nil]
1056
+ def get_query_movers(organization_id, project_id, date_from, date_to, opts = {})
1057
+ get_query_movers_with_http_info(organization_id, project_id, date_from, date_to, opts)
1058
+ nil
1059
+ end
1060
+
1061
+ # Tracked queries ranked by how much a metric moved
1062
+ # Minimum role: viewer. One row per tracked query — a single engine plus country — carrying its current metric envelope and the signed change against the immediately preceding window of equal length: a 7-day window is compared with the 7 days before it. Every trend delta is signed so that POSITIVE means improved, including the position trends, where the underlying avgSerpPosition / avgShoppingPosition / avgMentionPosition / avgLinkPosition are 1-based ranks and therefore LOWER is better. shareOfVoice and positivityIndex are percentages from 0 to 100, where HIGHER is better. Sorting applies to the trend keys only: sortOrder&#x3D;desc gives the top gainers, asc the top losers. Every nullable field means \&quot;not known yet\&quot; rather than zero — a null position or shareOfVoice is a query with no data in the window, and a null positivityIndex or trend is a period with no mentions to score, neither of which is a record of losing ground. total counts the tracked queries the filters match, not the rows on this page. dataDirtySince is a date from which the rank data is being recomputed, or null when nothing is pending; while it is non-null the deltas may still move.
1063
+ # @param organization_id [String]
1064
+ # @param project_id [String]
1065
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window. The comparison window is the equally long stretch immediately before it.
1066
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
1067
+ # @param [Hash] opts the optional parameters
1068
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated.
1069
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
1070
+ # @option opts [String] :sort_by Which trend delta ranks the rows. Positions improve as they fall, so a positive delta is always an improvement whichever key you pick. An unknown value is rejected, not replaced by the default. (default to 'trend_share_of_voice')
1071
+ # @option opts [String] :sort_order desc for the top gainers, asc for the top losers. (default to 'desc')
1072
+ # @option opts [Integer] :limit Page size. A value above the maximum is rejected, never clamped. (default to 20)
1073
+ # @option opts [Integer] :offset (default to 0)
1074
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
1075
+ def get_query_movers_with_http_info(organization_id, project_id, date_from, date_to, opts = {})
1076
+ if @api_client.config.debugging
1077
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_query_movers ...'
1078
+ end
1079
+ # verify the required parameter 'organization_id' is set
1080
+ if @api_client.config.client_side_validation && organization_id.nil?
1081
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_query_movers"
1082
+ end
1083
+ # verify the required parameter 'project_id' is set
1084
+ if @api_client.config.client_side_validation && project_id.nil?
1085
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_query_movers"
1086
+ end
1087
+ # verify the required parameter 'date_from' is set
1088
+ if @api_client.config.client_side_validation && date_from.nil?
1089
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.get_query_movers"
1090
+ end
1091
+ # verify the required parameter 'date_to' is set
1092
+ if @api_client.config.client_side_validation && date_to.nil?
1093
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.get_query_movers"
1094
+ end
1095
+ allowable_values = ["chatgpt", "perplexity", "google_ai_overview", "google_ai_mode", "google_serp", "google_shopping"]
1096
+ if @api_client.config.client_side_validation && opts[:'engines'] && !opts[:'engines'].all? { |item| allowable_values.include?(item) }
1097
+ fail ArgumentError, "invalid value for \"engines\", must include one of #{allowable_values}"
1098
+ end
1099
+ allowable_values = ["trend_serp", "trend_shopping", "trend_mention", "trend_link", "trend_share_of_voice"]
1100
+ if @api_client.config.client_side_validation && opts[:'sort_by'] && !allowable_values.include?(opts[:'sort_by'])
1101
+ fail ArgumentError, "invalid value for \"sort_by\", must be one of #{allowable_values}"
1102
+ end
1103
+ allowable_values = ["asc", "desc"]
1104
+ if @api_client.config.client_side_validation && opts[:'sort_order'] && !allowable_values.include?(opts[:'sort_order'])
1105
+ fail ArgumentError, "invalid value for \"sort_order\", must be one of #{allowable_values}"
1106
+ end
1107
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100
1108
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AnalyticsApi.get_query_movers, must be smaller than or equal to 100.'
1109
+ end
1110
+
1111
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1
1112
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AnalyticsApi.get_query_movers, must be greater than or equal to 1.'
1113
+ end
1114
+
1115
+ if @api_client.config.client_side_validation && !opts[:'offset'].nil? && opts[:'offset'] < 0
1116
+ fail ArgumentError, 'invalid value for "opts[:"offset"]" when calling AnalyticsApi.get_query_movers, must be greater than or equal to 0.'
1117
+ end
1118
+
1119
+ # resource path
1120
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/metrics/movers'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
1121
+
1122
+ # query parameters
1123
+ query_params = opts[:query_params] || {}
1124
+ query_params[:'dateFrom'] = date_from
1125
+ query_params[:'dateTo'] = date_to
1126
+ query_params[:'engines'] = @api_client.build_collection_param(opts[:'engines'], :multi) if !opts[:'engines'].nil?
1127
+ query_params[:'countries'] = @api_client.build_collection_param(opts[:'countries'], :multi) if !opts[:'countries'].nil?
1128
+ query_params[:'sortBy'] = opts[:'sort_by'] if !opts[:'sort_by'].nil?
1129
+ query_params[:'sortOrder'] = opts[:'sort_order'] if !opts[:'sort_order'].nil?
1130
+ query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
1131
+ query_params[:'offset'] = opts[:'offset'] if !opts[:'offset'].nil?
1132
+
1133
+ # header parameters
1134
+ header_params = opts[:header_params] || {}
1135
+
1136
+ # form parameters
1137
+ form_params = opts[:form_params] || {}
1138
+
1139
+ # http body (model)
1140
+ post_body = opts[:debug_body]
1141
+
1142
+ # return_type
1143
+ return_type = opts[:debug_return_type]
1144
+
1145
+ # auth_names
1146
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
1147
+
1148
+ new_options = opts.merge(
1149
+ :operation => :"AnalyticsApi.get_query_movers",
1150
+ :header_params => header_params,
1151
+ :query_params => query_params,
1152
+ :form_params => form_params,
1153
+ :body => post_body,
1154
+ :auth_names => auth_names,
1155
+ :return_type => return_type
1156
+ )
1157
+
1158
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
1159
+ if @api_client.config.debugging
1160
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_query_movers\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1161
+ end
1162
+ return data, status_code, headers
1163
+ end
1164
+
1165
+ # The constants behind the Share of Voice score
1166
+ # Minimum role: viewer. Static reference data, the same for every project: the weights and multipliers that turn individual brand mentions into a Share of Voice score. Each mention is worth `mentionTypeWeights[type] * sentimentMultipliers[tone] * (conditional ? conditionalMultiplier : directMultiplier)`, and a competitor's Share of Voice is its share of the summed weights of every brand in the window, as a percentage. mentionTypeWeights is keyed by mention type (recommendation, comparison, listing, example, reference) and sentimentMultipliers by tone (positive, neutral, negative); a negative mention is discounted, not discarded, because it still evidences presence. A conditional mention is one the answer hedged with a condition (\"if you need X\"). Use this to explain a score, not to recompute one: the counts it applies to come from the mention mix endpoint. Every value is always present and is never null.
1167
+ # @param organization_id [String]
1168
+ # @param project_id [String]
1169
+ # @param [Hash] opts the optional parameters
1170
+ # @return [GetShareOfVoiceFormula200Response]
1171
+ def get_share_of_voice_formula(organization_id, project_id, opts = {})
1172
+ data, _status_code, _headers = get_share_of_voice_formula_with_http_info(organization_id, project_id, opts)
1173
+ data
1174
+ end
1175
+
1176
+ # The constants behind the Share of Voice score
1177
+ # Minimum role: viewer. Static reference data, the same for every project: the weights and multipliers that turn individual brand mentions into a Share of Voice score. Each mention is worth &#x60;mentionTypeWeights[type] * sentimentMultipliers[tone] * (conditional ? conditionalMultiplier : directMultiplier)&#x60;, and a competitor&#39;s Share of Voice is its share of the summed weights of every brand in the window, as a percentage. mentionTypeWeights is keyed by mention type (recommendation, comparison, listing, example, reference) and sentimentMultipliers by tone (positive, neutral, negative); a negative mention is discounted, not discarded, because it still evidences presence. A conditional mention is one the answer hedged with a condition (\&quot;if you need X\&quot;). Use this to explain a score, not to recompute one: the counts it applies to come from the mention mix endpoint. Every value is always present and is never null.
1178
+ # @param organization_id [String]
1179
+ # @param project_id [String]
1180
+ # @param [Hash] opts the optional parameters
1181
+ # @return [Array<(GetShareOfVoiceFormula200Response, Integer, Hash)>] GetShareOfVoiceFormula200Response data, response status code and response headers
1182
+ def get_share_of_voice_formula_with_http_info(organization_id, project_id, opts = {})
1183
+ if @api_client.config.debugging
1184
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_share_of_voice_formula ...'
1185
+ end
1186
+ # verify the required parameter 'organization_id' is set
1187
+ if @api_client.config.client_side_validation && organization_id.nil?
1188
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_share_of_voice_formula"
1189
+ end
1190
+ # verify the required parameter 'project_id' is set
1191
+ if @api_client.config.client_side_validation && project_id.nil?
1192
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_share_of_voice_formula"
1193
+ end
1194
+ # resource path
1195
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/metrics/share-of-voice-formula'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
1196
+
1197
+ # query parameters
1198
+ query_params = opts[:query_params] || {}
1199
+
1200
+ # header parameters
1201
+ header_params = opts[:header_params] || {}
1202
+ # HTTP header 'Accept' (if needed)
1203
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
1204
+
1205
+ # form parameters
1206
+ form_params = opts[:form_params] || {}
1207
+
1208
+ # http body (model)
1209
+ post_body = opts[:debug_body]
1210
+
1211
+ # return_type
1212
+ return_type = opts[:debug_return_type] || 'GetShareOfVoiceFormula200Response'
1213
+
1214
+ # auth_names
1215
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
1216
+
1217
+ new_options = opts.merge(
1218
+ :operation => :"AnalyticsApi.get_share_of_voice_formula",
1219
+ :header_params => header_params,
1220
+ :query_params => query_params,
1221
+ :form_params => form_params,
1222
+ :body => post_body,
1223
+ :auth_names => auth_names,
1224
+ :return_type => return_type
1225
+ )
1226
+
1227
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
1228
+ if @api_client.config.debugging
1229
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_share_of_voice_formula\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1230
+ end
1231
+ return data, status_code, headers
1232
+ end
1233
+
1234
+ # Rank-tracking time series of a single tracked query
1235
+ # Minimum role: viewer. One point per bucket over the date window, each carrying the brand metrics and one same-shaped entry per requested competitor. Positions (serp, shopping, mention, link) are 1-based, so a LOWER number is better; positivity, shareOfVoice, mentionRate and serpRate are percentages from 0 to 100, where higher is better. Every metric is nullable, and a null means nothing was captured for that entity in that bucket — it is not a zero: a null shareOfVoice means no measurement, a shareOfVoice of 0 means measured and never mentioned. The tracked query fixes its own engine and country, so no engine or country filter is accepted. A non-null dataDirtySince is a timestamp warning that tracked queries were deleted from the project and historical buckets may still include their contributions until the nightly refresh rebuilds them.
1236
+ # @param organization_id [String]
1237
+ # @param project_id [String]
1238
+ # @param tracked_query_id [String] Must belong to the project in the path.
1239
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
1240
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
1241
+ # @param [Hash] opts the optional parameters
1242
+ # @option opts [String] :granularity Bucket size. Prefer weekly or monthly for long windows. (default to 'daily')
1243
+ # @option opts [Array<String>] :competitor_ids Competitors to add as extra series, repeatable or comma-separated. Valid ids come from the available-filters endpoint.
1244
+ # @return [nil]
1245
+ def get_tracked_query_time_series(organization_id, project_id, tracked_query_id, date_from, date_to, opts = {})
1246
+ get_tracked_query_time_series_with_http_info(organization_id, project_id, tracked_query_id, date_from, date_to, opts)
1247
+ nil
1248
+ end
1249
+
1250
+ # Rank-tracking time series of a single tracked query
1251
+ # Minimum role: viewer. One point per bucket over the date window, each carrying the brand metrics and one same-shaped entry per requested competitor. Positions (serp, shopping, mention, link) are 1-based, so a LOWER number is better; positivity, shareOfVoice, mentionRate and serpRate are percentages from 0 to 100, where higher is better. Every metric is nullable, and a null means nothing was captured for that entity in that bucket — it is not a zero: a null shareOfVoice means no measurement, a shareOfVoice of 0 means measured and never mentioned. The tracked query fixes its own engine and country, so no engine or country filter is accepted. A non-null dataDirtySince is a timestamp warning that tracked queries were deleted from the project and historical buckets may still include their contributions until the nightly refresh rebuilds them.
1252
+ # @param organization_id [String]
1253
+ # @param project_id [String]
1254
+ # @param tracked_query_id [String] Must belong to the project in the path.
1255
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
1256
+ # @param date_to [Date] Inclusive end of the window, Y-m-d.
1257
+ # @param [Hash] opts the optional parameters
1258
+ # @option opts [String] :granularity Bucket size. Prefer weekly or monthly for long windows. (default to 'daily')
1259
+ # @option opts [Array<String>] :competitor_ids Competitors to add as extra series, repeatable or comma-separated. Valid ids come from the available-filters endpoint.
1260
+ # @return [Array<(nil, Integer, Hash)>] nil, response status code and response headers
1261
+ def get_tracked_query_time_series_with_http_info(organization_id, project_id, tracked_query_id, date_from, date_to, opts = {})
1262
+ if @api_client.config.debugging
1263
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_tracked_query_time_series ...'
1264
+ end
1265
+ # verify the required parameter 'organization_id' is set
1266
+ if @api_client.config.client_side_validation && organization_id.nil?
1267
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_tracked_query_time_series"
1268
+ end
1269
+ # verify the required parameter 'project_id' is set
1270
+ if @api_client.config.client_side_validation && project_id.nil?
1271
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_tracked_query_time_series"
1272
+ end
1273
+ # verify the required parameter 'tracked_query_id' is set
1274
+ if @api_client.config.client_side_validation && tracked_query_id.nil?
1275
+ fail ArgumentError, "Missing the required parameter 'tracked_query_id' when calling AnalyticsApi.get_tracked_query_time_series"
1276
+ end
1277
+ # verify the required parameter 'date_from' is set
1278
+ if @api_client.config.client_side_validation && date_from.nil?
1279
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.get_tracked_query_time_series"
1280
+ end
1281
+ # verify the required parameter 'date_to' is set
1282
+ if @api_client.config.client_side_validation && date_to.nil?
1283
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.get_tracked_query_time_series"
1284
+ end
1285
+ allowable_values = ["daily", "weekly", "monthly"]
1286
+ if @api_client.config.client_side_validation && opts[:'granularity'] && !allowable_values.include?(opts[:'granularity'])
1287
+ fail ArgumentError, "invalid value for \"granularity\", must be one of #{allowable_values}"
1288
+ end
1289
+ # resource path
1290
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/tracked-queries/{trackedQueryId}/timeseries'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s)).sub('{trackedQueryId}', CGI.escape(tracked_query_id.to_s))
1291
+
1292
+ # query parameters
1293
+ query_params = opts[:query_params] || {}
1294
+ query_params[:'dateFrom'] = date_from
1295
+ query_params[:'dateTo'] = date_to
1296
+ query_params[:'granularity'] = opts[:'granularity'] if !opts[:'granularity'].nil?
1297
+ query_params[:'competitorIds'] = @api_client.build_collection_param(opts[:'competitor_ids'], :multi) if !opts[:'competitor_ids'].nil?
1298
+
1299
+ # header parameters
1300
+ header_params = opts[:header_params] || {}
1301
+
1302
+ # form parameters
1303
+ form_params = opts[:form_params] || {}
1304
+
1305
+ # http body (model)
1306
+ post_body = opts[:debug_body]
1307
+
1308
+ # return_type
1309
+ return_type = opts[:debug_return_type]
1310
+
1311
+ # auth_names
1312
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
1313
+
1314
+ new_options = opts.merge(
1315
+ :operation => :"AnalyticsApi.get_tracked_query_time_series",
1316
+ :header_params => header_params,
1317
+ :query_params => query_params,
1318
+ :form_params => form_params,
1319
+ :body => post_body,
1320
+ :auth_names => auth_names,
1321
+ :return_type => return_type
1322
+ )
1323
+
1324
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
1325
+ if @api_client.config.debugging
1326
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_tracked_query_time_series\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1327
+ end
1328
+ return data, status_code, headers
1329
+ end
1330
+
1331
+ # Coverage and staleness of a project tracked queries
1332
+ # Minimum role: viewer. A current-state snapshot answering \"what is stale or not being tracked\": total is every tracked query on the project, active and paused split it by status, neverChecked counts the active queries that have never run, and overdue counts the active queries whose last check is older than their own check-frequency interval (daily, weekly or monthly). neverChecked and overdue are disjoint — a query that has never run is never also counted as overdue — and both ignore paused queries, which are not expected to be checked at all. sample lists up to 20 of the overdue queries, most stale first; it is a sample of overdue only, so it never contains a never-checked query and is empty when overdue is 0. Takes no date window: every count describes the project as it stands right now.
1333
+ # @param organization_id [String]
1334
+ # @param project_id [String]
1335
+ # @param [Hash] opts the optional parameters
1336
+ # @return [GetTrackingCoverage200Response]
1337
+ def get_tracking_coverage(organization_id, project_id, opts = {})
1338
+ data, _status_code, _headers = get_tracking_coverage_with_http_info(organization_id, project_id, opts)
1339
+ data
1340
+ end
1341
+
1342
+ # Coverage and staleness of a project tracked queries
1343
+ # Minimum role: viewer. A current-state snapshot answering \&quot;what is stale or not being tracked\&quot;: total is every tracked query on the project, active and paused split it by status, neverChecked counts the active queries that have never run, and overdue counts the active queries whose last check is older than their own check-frequency interval (daily, weekly or monthly). neverChecked and overdue are disjoint — a query that has never run is never also counted as overdue — and both ignore paused queries, which are not expected to be checked at all. sample lists up to 20 of the overdue queries, most stale first; it is a sample of overdue only, so it never contains a never-checked query and is empty when overdue is 0. Takes no date window: every count describes the project as it stands right now.
1344
+ # @param organization_id [String]
1345
+ # @param project_id [String]
1346
+ # @param [Hash] opts the optional parameters
1347
+ # @return [Array<(GetTrackingCoverage200Response, Integer, Hash)>] GetTrackingCoverage200Response data, response status code and response headers
1348
+ def get_tracking_coverage_with_http_info(organization_id, project_id, opts = {})
1349
+ if @api_client.config.debugging
1350
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.get_tracking_coverage ...'
1351
+ end
1352
+ # verify the required parameter 'organization_id' is set
1353
+ if @api_client.config.client_side_validation && organization_id.nil?
1354
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.get_tracking_coverage"
1355
+ end
1356
+ # verify the required parameter 'project_id' is set
1357
+ if @api_client.config.client_side_validation && project_id.nil?
1358
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.get_tracking_coverage"
1359
+ end
1360
+ # resource path
1361
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/coverage'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
1362
+
1363
+ # query parameters
1364
+ query_params = opts[:query_params] || {}
1365
+
1366
+ # header parameters
1367
+ header_params = opts[:header_params] || {}
1368
+ # HTTP header 'Accept' (if needed)
1369
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
1370
+
1371
+ # form parameters
1372
+ form_params = opts[:form_params] || {}
1373
+
1374
+ # http body (model)
1375
+ post_body = opts[:debug_body]
1376
+
1377
+ # return_type
1378
+ return_type = opts[:debug_return_type] || 'GetTrackingCoverage200Response'
1379
+
1380
+ # auth_names
1381
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
1382
+
1383
+ new_options = opts.merge(
1384
+ :operation => :"AnalyticsApi.get_tracking_coverage",
1385
+ :header_params => header_params,
1386
+ :query_params => query_params,
1387
+ :form_params => form_params,
1388
+ :body => post_body,
1389
+ :auth_names => auth_names,
1390
+ :return_type => return_type
1391
+ )
1392
+
1393
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
1394
+ if @api_client.config.debugging
1395
+ @api_client.config.logger.debug "API called: AnalyticsApi#get_tracking_coverage\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1396
+ end
1397
+ return data, status_code, headers
1398
+ end
1399
+
1400
+ # List a project's keywords with their windowed metrics
1401
+ # Minimum role: viewer. One row per distinct keyword text of the project — every tracked query asking that text, on any engine in any country, collapsed into a single row whose variantIds name the tracked queries behind it. Each row carries the metrics of the requested window and a signed trend against the window of equal length immediately before it, where POSITIVE ALWAYS MEANS BETTER whichever direction the metric itself runs. Positions are 1-based and lower is better; rates, positivityIndex and shareOfVoice are 0-100 and higher is better; mentionPositionStability is a day-to-day spread, so lower is steadier. A null metric means nothing was captured for that keyword in the window — it is not a zero. `total` counts the KEYWORDS matching the filters, not the rows on this page and not tracked queries; `totalVariantCount` counts the tracked queries behind the keywords matching every filter EXCEPT `search` — with a text search applied it still counts the project's variants, so do not size a force-check budget from it on a searched page. NEITHER IS THE PROJECT'S TRACKED-QUERY COUNT: the count operation reads the write model, while this listing reads projections refreshed in the background from it, so the numbers legitimately differ while those projections catch up. A project whose cached tracked-query count has not been refreshed yet answers an empty page with total 0 — the same answer as a project with no tracked queries at all — so treat an unexpected empty page right after creating queries as \"not projected yet\", not as \"no data\". dataDirtySince is non-null while a recalculation is pending, meaning the metrics predate the latest configuration change. Filters narrow which VARIANTS count towards a row, so engines, countries, statuses, checkFrequencies and nPassesValues each report what the filters selected rather than everything the keyword has. A filter or sort key this endpoint cannot honour is rejected by name, never ignored, and a limit above the maximum is rejected rather than quietly reduced. This operation answers JSON only: a keyword row carries a nested mentionTypeCounts object, so it is not part of the CSV family.
1402
+ # @param organization_id [String]
1403
+ # @param project_id [String]
1404
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
1405
+ # @param date_to [Date] Inclusive end of the window, Y-m-d. The trend compares against the equally long window ending the day before dateFrom.
1406
+ # @param [Hash] opts the optional parameters
1407
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated. Narrows which variants count towards each row.
1408
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
1409
+ # @option opts [Array<String>] :query_cluster_ids Restrict to keywords with a variant in these clusters. Each must belong to the project.
1410
+ # @option opts [Boolean] :include_ungrouped_queries Sent alone, restricts the listing to keywords whose variants belong to no cluster; sent with queryClusterIds, adds them to that selection. (default to false)
1411
+ # @option opts [String] :status Restrict to variants with this status. A keyword whose variants disagree still reports statusSummary \&quot;mixed\&quot;.
1412
+ # @option opts [Array<String>] :check_frequencies Repeatable, or comma-separated.
1413
+ # @option opts [Array<Integer>] :n_passes Repeatable, or comma-separated. Restrict to variants configured with these pass counts.
1414
+ # @option opts [String] :search Case-insensitive substring match on the keyword text.
1415
+ # @option opts [String] :sort_by Named after the field it orders by. Rows with no value for the chosen key sort last in either direction. An unknown key is rejected, not replaced by the default. (default to 'keyword')
1416
+ # @option opts [String] :sort_order (default to 'asc')
1417
+ # @option opts [Integer] :limit Page size. A larger value is rejected, never silently reduced. (default to 20)
1418
+ # @option opts [Integer] :offset Number of matching keywords to skip before the page starts. (default to 0)
1419
+ # @return [ListKeywordListings200Response]
1420
+ def list_keyword_listings(organization_id, project_id, date_from, date_to, opts = {})
1421
+ data, _status_code, _headers = list_keyword_listings_with_http_info(organization_id, project_id, date_from, date_to, opts)
1422
+ data
1423
+ end
1424
+
1425
+ # List a project&#39;s keywords with their windowed metrics
1426
+ # Minimum role: viewer. One row per distinct keyword text of the project — every tracked query asking that text, on any engine in any country, collapsed into a single row whose variantIds name the tracked queries behind it. Each row carries the metrics of the requested window and a signed trend against the window of equal length immediately before it, where POSITIVE ALWAYS MEANS BETTER whichever direction the metric itself runs. Positions are 1-based and lower is better; rates, positivityIndex and shareOfVoice are 0-100 and higher is better; mentionPositionStability is a day-to-day spread, so lower is steadier. A null metric means nothing was captured for that keyword in the window — it is not a zero. &#x60;total&#x60; counts the KEYWORDS matching the filters, not the rows on this page and not tracked queries; &#x60;totalVariantCount&#x60; counts the tracked queries behind the keywords matching every filter EXCEPT &#x60;search&#x60; — with a text search applied it still counts the project&#39;s variants, so do not size a force-check budget from it on a searched page. NEITHER IS THE PROJECT&#39;S TRACKED-QUERY COUNT: the count operation reads the write model, while this listing reads projections refreshed in the background from it, so the numbers legitimately differ while those projections catch up. A project whose cached tracked-query count has not been refreshed yet answers an empty page with total 0 — the same answer as a project with no tracked queries at all — so treat an unexpected empty page right after creating queries as \&quot;not projected yet\&quot;, not as \&quot;no data\&quot;. dataDirtySince is non-null while a recalculation is pending, meaning the metrics predate the latest configuration change. Filters narrow which VARIANTS count towards a row, so engines, countries, statuses, checkFrequencies and nPassesValues each report what the filters selected rather than everything the keyword has. A filter or sort key this endpoint cannot honour is rejected by name, never ignored, and a limit above the maximum is rejected rather than quietly reduced. This operation answers JSON only: a keyword row carries a nested mentionTypeCounts object, so it is not part of the CSV family.
1427
+ # @param organization_id [String]
1428
+ # @param project_id [String]
1429
+ # @param date_from [Date] Inclusive start of the window, Y-m-d. Must fall inside the data retention window.
1430
+ # @param date_to [Date] Inclusive end of the window, Y-m-d. The trend compares against the equally long window ending the day before dateFrom.
1431
+ # @param [Hash] opts the optional parameters
1432
+ # @option opts [Array<String>] :engines Repeatable, or comma-separated. Narrows which variants count towards each row.
1433
+ # @option opts [Array<String>] :countries ISO-3166 alpha-2 codes or English names. Must be configured on the project.
1434
+ # @option opts [Array<String>] :query_cluster_ids Restrict to keywords with a variant in these clusters. Each must belong to the project.
1435
+ # @option opts [Boolean] :include_ungrouped_queries Sent alone, restricts the listing to keywords whose variants belong to no cluster; sent with queryClusterIds, adds them to that selection. (default to false)
1436
+ # @option opts [String] :status Restrict to variants with this status. A keyword whose variants disagree still reports statusSummary \&quot;mixed\&quot;.
1437
+ # @option opts [Array<String>] :check_frequencies Repeatable, or comma-separated.
1438
+ # @option opts [Array<Integer>] :n_passes Repeatable, or comma-separated. Restrict to variants configured with these pass counts.
1439
+ # @option opts [String] :search Case-insensitive substring match on the keyword text.
1440
+ # @option opts [String] :sort_by Named after the field it orders by. Rows with no value for the chosen key sort last in either direction. An unknown key is rejected, not replaced by the default. (default to 'keyword')
1441
+ # @option opts [String] :sort_order (default to 'asc')
1442
+ # @option opts [Integer] :limit Page size. A larger value is rejected, never silently reduced. (default to 20)
1443
+ # @option opts [Integer] :offset Number of matching keywords to skip before the page starts. (default to 0)
1444
+ # @return [Array<(ListKeywordListings200Response, Integer, Hash)>] ListKeywordListings200Response data, response status code and response headers
1445
+ def list_keyword_listings_with_http_info(organization_id, project_id, date_from, date_to, opts = {})
1446
+ if @api_client.config.debugging
1447
+ @api_client.config.logger.debug 'Calling API: AnalyticsApi.list_keyword_listings ...'
1448
+ end
1449
+ # verify the required parameter 'organization_id' is set
1450
+ if @api_client.config.client_side_validation && organization_id.nil?
1451
+ fail ArgumentError, "Missing the required parameter 'organization_id' when calling AnalyticsApi.list_keyword_listings"
1452
+ end
1453
+ # verify the required parameter 'project_id' is set
1454
+ if @api_client.config.client_side_validation && project_id.nil?
1455
+ fail ArgumentError, "Missing the required parameter 'project_id' when calling AnalyticsApi.list_keyword_listings"
1456
+ end
1457
+ # verify the required parameter 'date_from' is set
1458
+ if @api_client.config.client_side_validation && date_from.nil?
1459
+ fail ArgumentError, "Missing the required parameter 'date_from' when calling AnalyticsApi.list_keyword_listings"
1460
+ end
1461
+ # verify the required parameter 'date_to' is set
1462
+ if @api_client.config.client_side_validation && date_to.nil?
1463
+ fail ArgumentError, "Missing the required parameter 'date_to' when calling AnalyticsApi.list_keyword_listings"
1464
+ end
1465
+ allowable_values = ["chatgpt", "perplexity", "google_ai_overview", "google_ai_mode", "google_serp", "google_shopping"]
1466
+ if @api_client.config.client_side_validation && opts[:'engines'] && !opts[:'engines'].all? { |item| allowable_values.include?(item) }
1467
+ fail ArgumentError, "invalid value for \"engines\", must include one of #{allowable_values}"
1468
+ end
1469
+ allowable_values = ["active", "paused"]
1470
+ if @api_client.config.client_side_validation && opts[:'status'] && !allowable_values.include?(opts[:'status'])
1471
+ fail ArgumentError, "invalid value for \"status\", must be one of #{allowable_values}"
1472
+ end
1473
+ allowable_values = ["daily", "weekly", "monthly"]
1474
+ if @api_client.config.client_side_validation && opts[:'check_frequencies'] && !opts[:'check_frequencies'].all? { |item| allowable_values.include?(item) }
1475
+ fail ArgumentError, "invalid value for \"check_frequencies\", must include one of #{allowable_values}"
1476
+ end
1477
+ if @api_client.config.client_side_validation && !opts[:'search'].nil? && opts[:'search'].to_s.length > 100
1478
+ fail ArgumentError, 'invalid value for "opts[:"search"]" when calling AnalyticsApi.list_keyword_listings, the character length must be smaller than or equal to 100.'
1479
+ end
1480
+
1481
+ allowable_values = ["keyword", "variantCount", "lastCheckedAt", "statusSummary", "positivityIndex", "shareOfVoice", "avgMentionPosition", "avgLinkPosition", "mentionPositionStability", "avgSerpPosition", "avgShoppingPosition"]
1482
+ if @api_client.config.client_side_validation && opts[:'sort_by'] && !allowable_values.include?(opts[:'sort_by'])
1483
+ fail ArgumentError, "invalid value for \"sort_by\", must be one of #{allowable_values}"
1484
+ end
1485
+ allowable_values = ["asc", "desc"]
1486
+ if @api_client.config.client_side_validation && opts[:'sort_order'] && !allowable_values.include?(opts[:'sort_order'])
1487
+ fail ArgumentError, "invalid value for \"sort_order\", must be one of #{allowable_values}"
1488
+ end
1489
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] > 100
1490
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AnalyticsApi.list_keyword_listings, must be smaller than or equal to 100.'
1491
+ end
1492
+
1493
+ if @api_client.config.client_side_validation && !opts[:'limit'].nil? && opts[:'limit'] < 1
1494
+ fail ArgumentError, 'invalid value for "opts[:"limit"]" when calling AnalyticsApi.list_keyword_listings, must be greater than or equal to 1.'
1495
+ end
1496
+
1497
+ if @api_client.config.client_side_validation && !opts[:'offset'].nil? && opts[:'offset'] < 0
1498
+ fail ArgumentError, 'invalid value for "opts[:"offset"]" when calling AnalyticsApi.list_keyword_listings, must be greater than or equal to 0.'
1499
+ end
1500
+
1501
+ # resource path
1502
+ local_var_path = '/api/v1/organizations/{organizationId}/projects/{projectId}/keyword-listings'.sub('{organizationId}', CGI.escape(organization_id.to_s)).sub('{projectId}', CGI.escape(project_id.to_s))
1503
+
1504
+ # query parameters
1505
+ query_params = opts[:query_params] || {}
1506
+ query_params[:'dateFrom'] = date_from
1507
+ query_params[:'dateTo'] = date_to
1508
+ query_params[:'engines'] = @api_client.build_collection_param(opts[:'engines'], :multi) if !opts[:'engines'].nil?
1509
+ query_params[:'countries'] = @api_client.build_collection_param(opts[:'countries'], :multi) if !opts[:'countries'].nil?
1510
+ query_params[:'queryClusterIds'] = @api_client.build_collection_param(opts[:'query_cluster_ids'], :multi) if !opts[:'query_cluster_ids'].nil?
1511
+ query_params[:'includeUngroupedQueries'] = opts[:'include_ungrouped_queries'] if !opts[:'include_ungrouped_queries'].nil?
1512
+ query_params[:'status'] = opts[:'status'] if !opts[:'status'].nil?
1513
+ query_params[:'checkFrequencies'] = @api_client.build_collection_param(opts[:'check_frequencies'], :multi) if !opts[:'check_frequencies'].nil?
1514
+ query_params[:'nPasses'] = @api_client.build_collection_param(opts[:'n_passes'], :multi) if !opts[:'n_passes'].nil?
1515
+ query_params[:'search'] = opts[:'search'] if !opts[:'search'].nil?
1516
+ query_params[:'sortBy'] = opts[:'sort_by'] if !opts[:'sort_by'].nil?
1517
+ query_params[:'sortOrder'] = opts[:'sort_order'] if !opts[:'sort_order'].nil?
1518
+ query_params[:'limit'] = opts[:'limit'] if !opts[:'limit'].nil?
1519
+ query_params[:'offset'] = opts[:'offset'] if !opts[:'offset'].nil?
1520
+
1521
+ # header parameters
1522
+ header_params = opts[:header_params] || {}
1523
+ # HTTP header 'Accept' (if needed)
1524
+ header_params['Accept'] = @api_client.select_header_accept(['application/json']) unless header_params['Accept']
1525
+
1526
+ # form parameters
1527
+ form_params = opts[:form_params] || {}
1528
+
1529
+ # http body (model)
1530
+ post_body = opts[:debug_body]
1531
+
1532
+ # return_type
1533
+ return_type = opts[:debug_return_type] || 'ListKeywordListings200Response'
1534
+
1535
+ # auth_names
1536
+ auth_names = opts[:debug_auth_names] || ['ApiKey']
1537
+
1538
+ new_options = opts.merge(
1539
+ :operation => :"AnalyticsApi.list_keyword_listings",
1540
+ :header_params => header_params,
1541
+ :query_params => query_params,
1542
+ :form_params => form_params,
1543
+ :body => post_body,
1544
+ :auth_names => auth_names,
1545
+ :return_type => return_type
1546
+ )
1547
+
1548
+ data, status_code, headers = @api_client.call_api(:GET, local_var_path, new_options)
1549
+ if @api_client.config.debugging
1550
+ @api_client.config.logger.debug "API called: AnalyticsApi#list_keyword_listings\nData: #{data.inspect}\nStatus code: #{status_code}\nHeaders: #{headers}"
1551
+ end
1552
+ return data, status_code, headers
1553
+ end
1554
+ end
1555
+ end