data_for_seo/api/ai_optimization/llm_scraper.rs
1//! LLM Scraper sub-API (ChatGPT, Gemini).
2//!
3//! See <https://docs.dataforseo.com/v3/ai_optimization/chat_gpt/llm_scraper/overview/>.
4
5use crate::entity::{
6 AiOptimizationLanguage, AiOptimizationLlmScraper, AiOptimizationLlmScraperHtml,
7 AiOptimizationScraperLocation, AiOptimizationTaskReady,
8};
9use crate::{DataForSeoApiResponse, DataForSeoClient};
10use serde::{Deserialize, Serialize};
11
12/// LLM Scraper endpoints for a single provider (`chat_gpt`, `gemini`).
13pub struct AiOptimizationApiLlmScraper<'a> {
14 pub(crate) client: &'a DataForSeoClient,
15 pub(crate) provider: &'static str,
16}
17
18impl AiOptimizationApiLlmScraper<'_> {
19 /// Lists the locations supported by this provider's scraper.
20 ///
21 /// `GET /v3/ai_optimization/{provider}/llm_scraper/locations`
22 /// See <https://docs.dataforseo.com/v3/ai_optimization/chat_gpt/llm_scraper/locations/>.
23 pub async fn locations(&self) -> DataForSeoApiResponse<AiOptimizationScraperLocation> {
24 self.client
25 .http_get(&format!(
26 "/v3/ai_optimization/{}/llm_scraper/locations",
27 self.provider
28 ))
29 .await
30 }
31
32 /// Lists the languages supported by this provider's scraper.
33 ///
34 /// `GET /v3/ai_optimization/{provider}/llm_scraper/languages`
35 /// See <https://docs.dataforseo.com/v3/ai_optimization/chat_gpt/llm_scraper/languages/>.
36 pub async fn languages(&self) -> DataForSeoApiResponse<AiOptimizationLanguage> {
37 self.client
38 .http_get(&format!(
39 "/v3/ai_optimization/{}/llm_scraper/languages",
40 self.provider
41 ))
42 .await
43 }
44
45 /// Queues a scraper task (Standard method).
46 ///
47 /// `POST /v3/ai_optimization/{provider}/llm_scraper/task_post`
48 /// See <https://docs.dataforseo.com/v3/ai_optimization/chat_gpt/llm_scraper/task_post/>.
49 pub async fn task_post(
50 &self,
51 data: Vec<AiOptimizationLlmScraperPost>,
52 ) -> DataForSeoApiResponse<AiOptimizationLlmScraper> {
53 self.client
54 .http_post(
55 &format!(
56 "/v3/ai_optimization/{}/llm_scraper/task_post",
57 self.provider
58 ),
59 &data,
60 )
61 .await
62 }
63
64 /// Lists scraper tasks that have completed and are ready to collect.
65 ///
66 /// `GET /v3/ai_optimization/{provider}/llm_scraper/tasks_ready`
67 /// See <https://docs.dataforseo.com/v3/ai_optimization/chat_gpt/llm_scraper/tasks_ready/>.
68 pub async fn tasks_ready(&self) -> DataForSeoApiResponse<AiOptimizationTaskReady> {
69 self.client
70 .http_get(&format!(
71 "/v3/ai_optimization/{}/llm_scraper/tasks_ready",
72 self.provider
73 ))
74 .await
75 }
76
77 /// Collects a completed task's parsed result by its `id`.
78 ///
79 /// `GET /v3/ai_optimization/{provider}/llm_scraper/task_get/advanced/{id}`
80 /// See <https://docs.dataforseo.com/v3/ai_optimization/chat_gpt/llm_scraper/task_get/advanced/>.
81 pub async fn task_get_advanced(
82 &self,
83 id: &str,
84 ) -> DataForSeoApiResponse<AiOptimizationLlmScraper> {
85 self.client
86 .http_get(&format!(
87 "/v3/ai_optimization/{}/llm_scraper/task_get/advanced/{}",
88 self.provider, id
89 ))
90 .await
91 }
92
93 /// Collects a completed task's raw HTML by its `id`.
94 ///
95 /// `GET /v3/ai_optimization/{provider}/llm_scraper/task_get/html/{id}`
96 /// See <https://docs.dataforseo.com/v3/ai_optimization/chat_gpt/llm_scraper/task_get/html/>.
97 pub async fn task_get_html(
98 &self,
99 id: &str,
100 ) -> DataForSeoApiResponse<AiOptimizationLlmScraperHtml> {
101 self.client
102 .http_get(&format!(
103 "/v3/ai_optimization/{}/llm_scraper/task_get/html/{}",
104 self.provider, id
105 ))
106 .await
107 }
108
109 /// Scrapes and parses a result synchronously.
110 ///
111 /// `POST /v3/ai_optimization/{provider}/llm_scraper/live/advanced`
112 /// See <https://docs.dataforseo.com/v3/ai_optimization/chat_gpt/llm_scraper/live/advanced/>.
113 pub async fn live_advanced(
114 &self,
115 data: Vec<AiOptimizationLlmScraperPost>,
116 ) -> DataForSeoApiResponse<AiOptimizationLlmScraper> {
117 self.client
118 .http_post(
119 &format!(
120 "/v3/ai_optimization/{}/llm_scraper/live/advanced",
121 self.provider
122 ),
123 &data,
124 )
125 .await
126 }
127
128 /// Scrapes and returns the raw HTML synchronously.
129 ///
130 /// `POST /v3/ai_optimization/{provider}/llm_scraper/live/html`
131 /// See <https://docs.dataforseo.com/v3/ai_optimization/chat_gpt/llm_scraper/live/html/>.
132 pub async fn live_html(
133 &self,
134 data: Vec<AiOptimizationLlmScraperPost>,
135 ) -> DataForSeoApiResponse<AiOptimizationLlmScraperHtml> {
136 self.client
137 .http_post(
138 &format!(
139 "/v3/ai_optimization/{}/llm_scraper/live/html",
140 self.provider
141 ),
142 &data,
143 )
144 .await
145 }
146}
147
148/// Request body for the LLM Scraper `live/*` and `task_post` endpoints.
149///
150/// `postback_url` and `pingback_url` apply only to `task_post`.
151#[derive(Debug, Default, Serialize, Deserialize, Clone)]
152pub struct AiOptimizationLlmScraperPost {
153 /// Keyword to scrape (max 2000 characters).
154 pub keyword: String,
155 /// Full name of the search location (alternative to `location_code`).
156 #[serde(skip_serializing_if = "Option::is_none")]
157 pub location_name: Option<String>,
158 /// Search location code (alternative to `location_name`).
159 #[serde(skip_serializing_if = "Option::is_none")]
160 pub location_code: Option<i32>,
161 /// Full name of the search language (alternative to `language_code`).
162 #[serde(skip_serializing_if = "Option::is_none")]
163 pub language_name: Option<String>,
164 /// Search language code (alternative to `language_name`).
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub language_code: Option<String>,
167 /// Whether to force the model to access and cite current web information.
168 #[serde(skip_serializing_if = "Option::is_none")]
169 pub force_web_search: Option<bool>,
170 /// URL to POST the result to when the task completes (`task_post` only).
171 #[serde(skip_serializing_if = "Option::is_none")]
172 pub postback_url: Option<String>,
173 /// URL to ping when the task completes (`task_post` only).
174 #[serde(skip_serializing_if = "Option::is_none")]
175 pub pingback_url: Option<String>,
176 /// User-defined task identifier (max 255 characters).
177 #[serde(skip_serializing_if = "Option::is_none")]
178 pub tag: Option<String>,
179}
180
181impl AiOptimizationLlmScraperPost {
182 /// Creates a request for the given keyword.
183 pub fn new(keyword: &str) -> AiOptimizationLlmScraperPost {
184 AiOptimizationLlmScraperPost {
185 keyword: keyword.to_string(),
186 ..Default::default()
187 }
188 }
189}