yandex_webmaster_api/
dto.rs

1use chrono::{DateTime, NaiveDate, Utc};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5// ============================================================================
6// User
7// ============================================================================
8
9/// Response from the user endpoint
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
11pub struct UserResponse {
12    /// ID of the user. Required to call any Yandex Webmaster API resources.
13    pub user_id: i64,
14}
15
16// ============================================================================
17// Hosts (Sites)
18// ============================================================================
19
20/// Response containing a list of hosts
21#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
22pub struct HostsResponse {
23    /// List of hosts
24    pub hosts: Vec<HostInfo>,
25}
26
27/// Site indexing status
28#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
29#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
30pub enum HostDataStatus {
31    /// The site isn't indexed yet.
32    NotIndexed,
33    /// The site data isn't uploaded to Yandex.Webmaster yet.
34    NotLoaded,
35    /// The site is indexed. The data is available in Yandex.Webmaster.
36    Ok,
37}
38
39/// Information about a host
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
41pub struct HostInfo {
42    /// Site identifier
43    pub host_id: String,
44    /// ASCII-encoded site URL
45    pub ascii_host_url: String,
46    /// UTF-8 encoded site URL
47    pub unicode_host_url: String,
48    /// Ownership verification status
49    pub verified: bool,
50    /// Primary site address (if applicable)
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub main_mirror: Option<Box<HostInfo>>,
53}
54
55/// Information about a host from `get_host` method
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
57pub struct FullHostInfo {
58    /// Site identifier
59    pub host_id: String,
60    /// ASCII-encoded site URL
61    pub ascii_host_url: String,
62    /// UTF-8 encoded site URL
63    pub unicode_host_url: String,
64    /// Ownership verification status
65    pub verified: bool,
66    /// Primary site address (if applicable)
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub main_mirror: Option<Box<HostInfo>>,
69    /// Information about the site (shown if the site is verified).
70    pub host_data_status: Option<HostDataStatus>,
71    /// The site name to display
72    pub host_display_name: Option<String>,
73}
74
75/// Response from adding a new host
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
77pub struct AddHostResponse {
78    /// Assigned host ID
79    pub host_id: String,
80}
81
82// ============================================================================
83// Host Verification
84// ============================================================================
85
86/// Error description if the VERIFICATION_FAILED status is received.
87#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
88pub struct FailInfo {
89    /// The reason why verification failed.
90    pub message: String,
91    /// Error description for users.
92    pub reason: VerificationFailReason,
93}
94/// Host verification status
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
96pub struct HostVerificationStatusResponse {
97    /// Verification state
98    pub verification_state: VerificationState,
99    /// Verification type
100    pub verification_type: VerificationType,
101    /// Verification token (for DNS and HTML methods)
102    pub verification_uin: String,
103    /// The verification methods applied for the given site.
104    pub applicable_verifiers: Vec<ExplicitVerificationType>,
105    /// The time of the last check (if verification_state isn't NONE).
106    pub latest_verification_time: Option<DateTime<Utc>>,
107    /// Error description if the VERIFICATION_FAILED status is received.
108    pub fail_info: Option<FailInfo>,
109}
110
111/// Host verification status
112#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
113pub struct HostVerificationResponse {
114    /// Verification state
115    pub verification_state: VerificationState,
116    /// Verification type
117    pub verification_type: VerificationType,
118    /// Verification token (for DNS and HTML methods)
119    pub verification_uin: String,
120    /// The verification methods applied for the given site.
121    pub applicable_verifiers: Vec<ExplicitVerificationType>,
122}
123
124/// Verification state
125#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
126#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
127pub enum VerificationState {
128    /// Not verified
129    None,
130    /// Rights confirmed
131    Verified,
132    /// Verification pending
133    InProgress,
134    /// Rights not confirmed
135    VerificationFailed,
136    /// System error during verification
137    InternalError,
138}
139
140/// Verification type
141#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
142#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
143pub enum ExplicitVerificationType {
144    /// DNS record verification
145    Dns,
146    /// Meta tag verification
147    MetaTag,
148    /// HTML file verification
149    HtmlFile,
150}
151
152/// Verification type
153#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
154#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
155pub enum VerificationType {
156    /// Automatic rights verification (deprecated; only for *.narod.ru sites).
157    Auto,
158    /// Rights were delegated.
159    Delegated,
160    /// Rights verification via Yandex.Mail for Domains.
161    Pdd,
162    /// Placing a text file in the site's root directory.
163    TxtFile,
164    /// DNS record verification
165    Dns,
166    /// Meta tag verification
167    MetaTag,
168    /// HTML file verification
169    HtmlFile,
170}
171
172/// Verification failure reason
173#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
174#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
175pub enum VerificationFailReason {
176    /// Rights delegation revoked
177    DelegationCancelled,
178    /// Missing DNS entry
179    DnsRecordNotFound,
180    /// Missing meta tag in homepage header
181    MetaTagNotFound,
182    /// Incorrect HTML file content
183    WrongHtmlPageContent,
184    /// Verification of site management rights via Yandex.Mail for Domain isn't allowed for this site.
185    PddVerificationCancelled,
186}
187
188/// List of verified owners
189#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
190pub struct OwnersResponse {
191    /// List of owners
192    pub users: Vec<Owner>,
193}
194
195/// Owner information
196#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
197pub struct Owner {
198    /// User login
199    pub user_login: String,
200    /// Confirmation code
201    pub verification_uin: String,
202    /// Rights verification method
203    pub verification_type: VerificationType,
204    /// Verification date
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub verification_date: Option<DateTime<Utc>>,
207}
208
209// ============================================================================
210// Host Summary and Statistics
211// ============================================================================
212
213/// Site statistics summary
214#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
215pub struct HostSummaryResponse {
216    /// Site quality index
217    #[serde(skip_serializing_if = "Option::is_none")]
218    pub sqi: Option<f64>,
219    /// Number of searchable pages
220    #[serde(default)]
221    pub searchable_pages_count: i64,
222    /// Number of excluded pages
223    #[serde(default)]
224    pub excluded_pages_count: i64,
225    /// Site problems grouped by severity
226    #[serde(default)]
227    pub site_problems: HashMap<SiteProblemSeverityEnum, i32>,
228}
229
230/// Excluded pages statistics by status
231#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
232pub struct ExcludedPagesStatistics {
233    /// Statistics by status
234    pub statuses: HashMap<ApiExcludedUrlStatus, i64>,
235}
236
237/// Site quality index history request
238#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
239pub struct SqiHistoryRequest {
240    pub date_from: Option<DateTime<Utc>>,
241    pub date_to: Option<DateTime<Utc>>,
242}
243
244/// Site quality index history
245#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
246pub struct SqiHistoryResponse {
247    /// History points
248    pub points: Vec<SqiPoint>,
249}
250
251/// Single SQI history point
252#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
253pub struct SqiPoint {
254    /// Date
255    pub date: DateTime<Utc>,
256    /// SQI value
257    pub value: f64,
258}
259
260// ============================================================================
261// Search Queries
262// ============================================================================
263
264/// Query sorting order field
265#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
266#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
267pub enum ApiQueryOrderField {
268    /// Sort by total shows
269    TotalShows,
270    /// Sort by total clicks
271    TotalClicks,
272}
273
274/// Query indicators
275#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
276#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
277pub enum ApiQueryIndicator {
278    /// Total number of shows
279    TotalShows,
280    /// Total number of clicks
281    TotalClicks,
282    /// Average show position
283    AvgShowPosition,
284    /// Average click position
285    AvgClickPosition,
286}
287
288/// Device type indicator
289#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
290#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
291pub enum ApiDeviceTypeIndicator {
292    /// All device types
293    #[default]
294    All,
295    /// Desktop computers
296    Desktop,
297    /// Mobile phones and tablets
298    MobileAndTablet,
299    /// Mobile phones only
300    Mobile,
301    /// Tablets only
302    Tablet,
303}
304
305/// Popular queries request parameters
306#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
307pub struct PopularQueriesRequest {
308    /// Indicator for sorting requests (required)
309    pub order_by: ApiQueryOrderField,
310    /// Indicators for displaying requests
311    #[serde(skip_serializing_if = "Option::is_none")]
312    pub query_indicator: Option<ApiQueryIndicator>,
313    /// Device type indicator (default: ALL)
314    #[serde(skip_serializing_if = "Option::is_none")]
315    pub device_type_indicator: Option<ApiDeviceTypeIndicator>,
316    /// Start date of the range
317    #[serde(skip_serializing_if = "Option::is_none")]
318    pub date_from: Option<NaiveDate>,
319    /// End date of the range
320    #[serde(skip_serializing_if = "Option::is_none")]
321    pub date_to: Option<NaiveDate>,
322    /// List offset (minimum: 0, default: 0)
323    #[serde(skip_serializing_if = "Option::is_none")]
324    pub offset: Option<i32>,
325    /// Page size (1-500, default: 500)
326    #[serde(skip_serializing_if = "Option::is_none")]
327    pub limit: Option<i32>,
328}
329
330/// Popular search queries response
331#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
332pub struct PopularQueriesResponse {
333    /// List of queries
334    pub queries: Vec<PopularQuery>,
335    /// Start date of the range
336    pub date_from: NaiveDate,
337    /// End date of the range
338    pub date_to: NaiveDate,
339    /// Total number of search queries available
340    pub count: i32,
341}
342
343/// Popular query information
344#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
345pub struct PopularQuery {
346    /// Query ID
347    pub query_id: String,
348    /// Query text
349    pub query_text: String,
350    /// Query indicators (e.g., TOTAL_SHOWS, TOTAL_CLICKS, etc.)
351    pub indicators: std::collections::HashMap<ApiQueryIndicator, f64>,
352}
353
354/// Query analytics request parameters
355#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
356pub struct QueryAnalyticsRequest {
357    /// Indicators for displaying requests (can specify multiple)
358    pub query_indicator: Vec<ApiQueryIndicator>,
359    /// Device type indicator (default: ALL)
360    #[serde(skip_serializing_if = "Option::is_none")]
361    pub device_type_indicator: Option<ApiDeviceTypeIndicator>,
362    /// Start date of the range
363    #[serde(skip_serializing_if = "Option::is_none")]
364    pub date_from: Option<DateTime<Utc>>,
365    /// End date of the range
366    #[serde(skip_serializing_if = "Option::is_none")]
367    pub date_to: Option<DateTime<Utc>>,
368}
369
370/// Query analytics response
371#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
372pub struct QueryAnalyticsResponse {
373    /// Map of indicators to their history points
374    pub indicators: std::collections::HashMap<ApiQueryIndicator, Vec<IndicatorPoint>>,
375}
376
377/// Single indicator history point
378#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
379pub struct IndicatorPoint {
380    /// Date
381    pub date: DateTime<Utc>,
382    /// Value
383    pub value: f64,
384}
385
386/// Query history request parameters
387#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
388pub struct QueryHistoryRequest {
389    /// Indicators for displaying requests (can specify multiple)
390    pub query_indicator: Vec<ApiQueryIndicator>,
391    /// Device type indicator (default: ALL)
392    #[serde(skip_serializing_if = "Option::is_none")]
393    pub device_type_indicator: Option<ApiDeviceTypeIndicator>,
394    /// Start date of the range
395    #[serde(skip_serializing_if = "Option::is_none")]
396    pub date_from: Option<NaiveDate>,
397    /// End date of the range
398    #[serde(skip_serializing_if = "Option::is_none")]
399    pub date_to: Option<NaiveDate>,
400}
401
402/// Query history response
403#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
404pub struct QueryHistoryResponse {
405    /// Search query ID
406    pub query_id: String,
407    /// Search query text
408    pub query_text: String,
409    /// Map of indicators to their history points
410    pub indicators: std::collections::HashMap<ApiQueryIndicator, Vec<IndicatorPoint>>,
411}
412
413// ============================================================================
414// Sitemaps
415// ============================================================================
416
417/// Source of the Sitemap file
418#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
419#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
420pub enum ApiSitemapSource {
421    /// Sitemap is specified in the site's robots.txt file
422    RobotsTxt,
423    /// The user added the Sitemap in Yandex.Webmaster
424    Webmaster,
425    /// Sitemap found in another (index) Sitemap file
426    IndexSitemap,
427}
428
429/// Type of Sitemap file
430#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
431#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
432pub enum ApiSitemapType {
433    /// Normal Sitemap file that contains the URLs of site pages
434    Sitemap,
435    /// The Sitemap index file that contains the URLs of other Sitemap files
436    IndexSitemap,
437}
438
439/// Request parameters for getting sitemaps
440#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
441pub struct GetSitemapsRequest {
442    /// Parent sitemap ID
443    #[serde(skip_serializing_if = "Option::is_none")]
444    pub parent_id: Option<String>,
445    /// Page size (1-100, default: 10)
446    #[serde(skip_serializing_if = "Option::is_none")]
447    pub limit: Option<i32>,
448    /// Get sitemaps starting after this ID (not including it)
449    #[serde(skip_serializing_if = "Option::is_none")]
450    pub from: Option<String>,
451}
452
453/// List of sitemaps
454#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
455pub struct SitemapsResponse {
456    /// Sitemaps
457    pub sitemaps: Vec<SitemapInfo>,
458}
459
460/// Sitemap information
461#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
462pub struct SitemapInfo {
463    /// Sitemap ID
464    pub sitemap_id: String,
465    /// Sitemap URL
466    pub sitemap_url: String,
467    /// Last access date
468    #[serde(skip_serializing_if = "Option::is_none")]
469    pub last_access_date: Option<DateTime<Utc>>,
470    /// Number of errors in the file
471    pub errors_count: i32,
472    /// Number of URLs in the file
473    pub urls_count: i64,
474    /// Number of child Sitemap files
475    pub children_count: i32,
476    /// Sources that led the robot to this file
477    pub sources: Vec<ApiSitemapSource>,
478    /// Type of the Sitemap file
479    pub sitemap_type: ApiSitemapType,
480}
481
482/// Request parameters for getting user-added sitemaps
483#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
484pub struct GetUserSitemapsRequest {
485    /// Get files starting from the specified one (not including it, default: 0)
486    #[serde(skip_serializing_if = "Option::is_none")]
487    pub offset: Option<i32>,
488    /// Page size (1-100, default: 100)
489    #[serde(skip_serializing_if = "Option::is_none")]
490    pub limit: Option<i32>,
491}
492
493/// List of user-added sitemaps
494#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
495pub struct UserSitemapsResponse {
496    /// Sitemaps
497    pub sitemaps: Vec<UserSitemapInfo>,
498    /// Total number of Sitemap files added by the user
499    pub count: i32,
500}
501
502/// User-added sitemap information
503#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
504pub struct UserSitemapInfo {
505    /// Sitemap ID
506    pub sitemap_id: String,
507    /// Sitemap URL
508    pub sitemap_url: String,
509    /// Date the file was added
510    pub added_date: DateTime<Utc>,
511}
512
513/// Response from adding a sitemap
514#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
515pub struct AddSitemapResponse {
516    /// Assigned sitemap ID
517    pub sitemap_id: String,
518}
519
520// ============================================================================
521// Indexing
522// ============================================================================
523
524/// Indexing status by HTTP code
525#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
526#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
527pub enum IndexingStatusEnum {
528    /// HTTP 2xx responses
529    #[serde(rename = "HTTP_2XX")]
530    Http2xx,
531    /// HTTP 3xx responses
532    #[serde(rename = "HTTP_3XX")]
533    Http3xx,
534    /// HTTP 4xx responses
535    #[serde(rename = "HTTP_4XX")]
536    Http4xx,
537    /// HTTP 5xx responses
538    #[serde(rename = "HTTP_5XX")]
539    Http5xx,
540    /// Other statuses
541    Other,
542}
543
544/// Site problem severity
545#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
546#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
547pub enum SiteProblemSeverityEnum {
548    /// Fatal problems
549    Fatal,
550    /// Critical problems
551    Critical,
552    /// Possible problems
553    PossibleProblem,
554    /// Recommendations
555    Recommendation,
556}
557
558/// Excluded URL status
559#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
560#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
561pub enum ApiExcludedUrlStatus {
562    /// No exclusion found - robot doesn't know about page or it was unavailable
563    NothingFound,
564    /// Could not connect to server when accessing site
565    HostError,
566    /// Page redirects to another page (target page is indexed)
567    RedirectNotsearchable,
568    /// HTTP error occurred when accessing page
569    HttpError,
570    /// Page indexed by canonical URL specified in rel="canonical"
571    NotCanonical,
572    /// Page belongs to secondary site mirror
573    NotMainMirror,
574    /// Robot couldn't get page content
575    ParserError,
576    /// Site indexing prohibited in robots.txt
577    RobotsHostError,
578    /// Page indexing prohibited in robots.txt
579    RobotsUrlError,
580    /// Page duplicates a site page already in search
581    Duplicate,
582    /// Page excluded after robot processed Clean-param directive
583    CleanParams,
584    /// Page excluded because robots meta tag has noindex value
585    NoIndex,
586    /// Forbidden by robots.txt (legacy)
587    ForbiddenByRobotsTxt,
588    /// URL not allowed (legacy)
589    UrlNotAllowed,
590    /// Contains noindex meta tag (legacy)
591    ContainsNoindexMetaTag,
592    /// Contains noindex X-Robots-Tag header (legacy)
593    ContainsNoindexXRobotsTagHeader,
594    /// Sitemap forbidden (legacy)
595    SitemapForbidden,
596    /// Sitemap not allowed (legacy)
597    SitemapNotAllowed,
598    /// Low quality - removed due to low quality
599    LowQuality,
600    /// Alternative duplicate (legacy)
601    AlternativeDuplicate,
602    /// User duplicate (legacy)
603    UserDuplicate,
604    /// Canonical duplicate (legacy)
605    CanonicalDuplicate,
606    /// Redirect duplicate (legacy)
607    RedirectDuplicate,
608    /// Moved permanently (legacy)
609    MovedPermanently,
610    /// Moved temporarily (legacy)
611    MovedTemporarily,
612    /// Malware detected (legacy)
613    MalwareDetected,
614    /// Phishing detected (legacy)
615    PhishingDetected,
616    /// Adult content (legacy)
617    AdultContent,
618    /// Other reason - robot doesn't have updated data
619    Other,
620}
621
622/// Important URL change indicator
623#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
624#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
625pub enum ApiImportantUrlChangeIndicator {
626    /// Indexing HTTP code
627    IndexingHttpCode,
628    /// Search status
629    SearchStatus,
630    /// Page title
631    Title,
632    /// Page description
633    Description,
634}
635
636/// Indexing history request
637#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
638pub struct IndexingHistoryRequest {
639    /// Date from
640    #[serde(skip_serializing_if = "Option::is_none")]
641    pub date_from: Option<DateTime<Utc>>,
642    /// Date to
643    #[serde(skip_serializing_if = "Option::is_none")]
644    pub date_to: Option<DateTime<Utc>>,
645}
646
647/// Indexing history response
648#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
649pub struct IndexingHistoryResponse {
650    /// History indicators by status
651    pub indicators: HashMap<IndexingStatusEnum, Vec<IndexingHistoryPoint>>,
652}
653
654/// Indexing history point
655#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
656pub struct IndexingHistoryPoint {
657    /// Date
658    pub date: DateTime<Utc>,
659    /// Value
660    pub value: f64,
661}
662
663/// Get indexing samples request
664#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
665pub struct GetIndexingSamplesRequest {
666    /// Offset for pagination
667    #[serde(skip_serializing_if = "Option::is_none")]
668    pub offset: Option<i32>,
669    /// Limit for pagination
670    #[serde(skip_serializing_if = "Option::is_none")]
671    pub limit: Option<i32>,
672}
673
674/// Indexing samples response
675#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
676pub struct IndexingSamplesResponse {
677    /// Sample URLs
678    pub samples: Vec<IndexingSample>,
679    /// Total count
680    pub count: i32,
681}
682
683/// Indexing sample
684#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
685pub struct IndexingSample {
686    /// URL
687    pub url: String,
688    /// HTTP status code
689    pub http_code: i32,
690    /// Last access date
691    pub access_date: DateTime<Utc>,
692}
693
694// ============================================================================
695// Search URLs (Pages in Search)
696// ============================================================================
697
698/// Search event type
699#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
700#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
701pub enum ApiSearchEventEnum {
702    /// Page appeared in search results
703    AppearedInSearch,
704    /// Page removed from search results
705    RemovedFromSearch,
706}
707
708/// Search URLs history response
709#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
710pub struct SearchUrlsHistoryResponse {
711    /// History points
712    pub history: Vec<SearchUrlsHistoryPoint>,
713}
714
715/// Search URLs history point
716#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
717pub struct SearchUrlsHistoryPoint {
718    /// Date and time when search output was updated
719    pub date: DateTime<Utc>,
720    /// Number of pages in search
721    pub value: i64,
722}
723
724/// Get search URLs samples request
725#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
726pub struct GetSearchUrlsSamplesRequest {
727    /// Offset for pagination
728    #[serde(skip_serializing_if = "Option::is_none")]
729    pub offset: Option<i32>,
730    /// Limit for pagination (1-100, default 50)
731    #[serde(skip_serializing_if = "Option::is_none")]
732    pub limit: Option<i32>,
733}
734
735/// Search URLs samples response
736#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
737pub struct SearchUrlsSamplesResponse {
738    /// Total number of available examples
739    pub count: i32,
740    /// Sample pages
741    pub samples: Vec<SearchUrlsSample>,
742}
743
744/// Search URLs sample
745#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
746pub struct SearchUrlsSample {
747    /// Page URL
748    pub url: String,
749    /// Date of the page version in search
750    pub last_access: DateTime<Utc>,
751    /// Page heading
752    pub title: String,
753}
754
755/// Search events history response
756#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
757pub struct SearchEventsHistoryResponse {
758    /// History indicators by event type
759    pub indicators: HashMap<ApiSearchEventEnum, Vec<SearchUrlsHistoryPoint>>,
760}
761
762/// Get search events samples request
763#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
764pub struct GetSearchEventsSamplesRequest {
765    /// Offset for pagination
766    #[serde(skip_serializing_if = "Option::is_none")]
767    pub offset: Option<i32>,
768    /// Limit for pagination (1-100, default 50)
769    #[serde(skip_serializing_if = "Option::is_none")]
770    pub limit: Option<i32>,
771}
772
773/// Search events samples response
774#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
775pub struct SearchEventsSamplesResponse {
776    /// Total number of available examples
777    pub count: i32,
778    /// Sample pages
779    pub samples: Vec<SearchEventsSample>,
780}
781
782/// Search events sample
783#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
784pub struct SearchEventsSample {
785    /// Page URL
786    pub url: String,
787    /// Page heading
788    pub title: String,
789    /// Date when page appeared or was excluded
790    pub event_date: DateTime<Utc>,
791    /// Date when page was last crawled before appearing or being excluded
792    pub last_access: DateTime<Utc>,
793    /// The appearance or removal of the page
794    pub event: ApiSearchEventEnum,
795    /// Reason the page was excluded
796    #[serde(skip_serializing_if = "Option::is_none")]
797    pub excluded_url_status: Option<ApiExcludedUrlStatus>,
798    /// Page's HTTP response code for HTTP_ERROR status
799    #[serde(skip_serializing_if = "Option::is_none")]
800    pub bad_http_status: Option<i32>,
801    /// Another address of the page (redirect target, canonical, or duplicate)
802    #[serde(skip_serializing_if = "Option::is_none")]
803    pub target_url: Option<String>,
804}
805
806// ============================================================================
807// Recrawl (Reindexing)
808// ============================================================================
809
810/// Response from recrawl request
811#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
812pub struct RecrawlResponse {
813    /// Task ID
814    pub task_id: String,
815}
816
817/// Get recrawl tasks request
818#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
819pub struct GetRecrawlTasksRequest {
820    /// Offset in the list
821    #[serde(skip_serializing_if = "Option::is_none")]
822    pub offset: Option<i32>,
823    /// Page size (minimum 1, default 50)
824    #[serde(skip_serializing_if = "Option::is_none")]
825    pub limit: Option<i32>,
826    /// Start of the date range
827    #[serde(skip_serializing_if = "Option::is_none")]
828    pub date_from: Option<DateTime<Utc>>,
829    /// End of the date range
830    #[serde(skip_serializing_if = "Option::is_none")]
831    pub date_to: Option<DateTime<Utc>>,
832}
833
834/// Recrawl task list response
835#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
836pub struct RecrawlTasksResponse {
837    /// Tasks
838    pub tasks: Vec<RecrawlTask>,
839}
840
841/// Recrawl task information
842#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
843pub struct RecrawlTask {
844    /// Task ID
845    pub task_id: String,
846    /// URL of the page sent for reindexing
847    pub url: String,
848    /// Date the reindexing request was created
849    #[serde(skip_serializing_if = "Option::is_none")]
850    pub added_time: Option<DateTime<Utc>>,
851    /// Status of the reindexing request
852    pub state: RecrawlTaskState,
853}
854
855/// Recrawl task state (reindexing request status)
856#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
857#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
858pub enum RecrawlTaskState {
859    /// Request is being processed
860    InProgress,
861    /// Robot crawled the URL
862    Done,
863    /// Robot failed to crawl the page
864    Failed,
865}
866
867/// Recrawl quota response
868#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
869pub struct RecrawlQuotaResponse {
870    /// Daily quota
871    pub daily_quota: i32,
872    /// Remainder of daily quota
873    pub quota_remainder: i32,
874}
875
876// ============================================================================
877// Links
878// ============================================================================
879
880/// Internal link indicators
881#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
882#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
883pub enum ApiInternalLinksBrokenIndicator {
884    /// The total number of known external links to the site
885    SiteError,
886    /// The page doesn't exist or is prohibited from indexing
887    DisallowedByUser,
888    /// Not supported by the main Search indexing robot
889    UnsupportedByRobot,
890}
891
892/// Query analytics request parameters
893#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
894pub struct BrokenLinksRequest {
895    /// The broken link indicator — the reason the link doesn't work (ApiInternalLinksBrokenIndicator). You can specify multiple indicators. If the indicator is omitted, the report will contain all link types.
896    #[serde(skip_serializing_if = "Option::is_none")]
897    pub indicator: Option<Vec<ApiInternalLinksBrokenIndicator>>,
898    /// List offset (minimum: 0, default: 0)
899    #[serde(skip_serializing_if = "Option::is_none")]
900    pub offset: Option<i32>,
901    /// Page size (1-500, default: 500)
902    #[serde(skip_serializing_if = "Option::is_none")]
903    pub limit: Option<i32>,
904}
905
906/// Broken internal links samples
907#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
908pub struct BrokenLinksResponse {
909    /// The number of example links available
910    pub count: i32,
911    /// The URL of the page that contains the link to the site
912    pub links: Vec<BrokenLink>,
913}
914
915/// Broken link information
916#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
917pub struct BrokenLink {
918    /// Source URL
919    pub source_url: String,
920    /// Destination URL
921    pub destination_url: String,
922    /// The date when the link was detected
923    pub discovery_date: NaiveDate,
924    /// The date when the robot last visited the target page
925    pub source_last_access_date: NaiveDate,
926}
927
928/// Broken link history request
929#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
930pub struct BrokenLinkHistoryRequest {
931    /// Date from
932    #[serde(skip_serializing_if = "Option::is_none")]
933    pub date_from: Option<DateTime<Utc>>,
934    /// Date to
935    #[serde(skip_serializing_if = "Option::is_none")]
936    pub date_to: Option<DateTime<Utc>>,
937}
938
939/// Broken link history point
940#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
941pub struct BrokenLinkHistoryPoint {
942    /// Date
943    pub date: DateTime<Utc>,
944    /// Value
945    pub value: f64,
946}
947
948/// Broken link history response
949#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
950pub struct BrokenLinkHistoryResponse {
951    /// The reason the link doesn't work
952    pub indicators: HashMap<ApiInternalLinksBrokenIndicator, Vec<BrokenLinkHistoryPoint>>,
953}
954
955/// External links request parameter
956#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
957pub struct ExternalLinksRequest {
958    /// List offset (minimum: 0, default: 0)
959    #[serde(skip_serializing_if = "Option::is_none")]
960    pub offset: Option<i32>,
961    /// Page size (1-500, default: 500)
962    #[serde(skip_serializing_if = "Option::is_none")]
963    pub limit: Option<i32>,
964}
965
966/// External backlinks samples
967#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
968pub struct ExternalLinksResponse {
969    /// The number of example links available
970    pub count: i32,
971    /// Samples
972    pub links: Vec<ExternalLink>,
973}
974
975/// External link information
976#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
977pub struct ExternalLink {
978    /// Source URL
979    pub source_url: String,
980    /// Destination URL
981    pub destination_url: String,
982    /// The date when the link was detected
983    pub discovery_date: NaiveDate,
984    /// The date when the robot last visited the target page
985    pub source_last_access_date: NaiveDate,
986}
987
988/// Indicators of external links
989#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
990#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
991pub enum ApiExternalLinksIndicator {
992    /// The total number of known external links to the host
993    LinksTotalCount,
994}
995
996/// Indexing history response
997#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
998pub struct ExternalLinksHistoryResponse {
999    /// History indicators by status
1000    pub indicators: HashMap<ApiExternalLinksIndicator, Vec<ExternalLinksHistoryPoint>>,
1001}
1002
1003/// Indexing history point
1004#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1005pub struct ExternalLinksHistoryPoint {
1006    /// Date
1007    pub date: DateTime<Utc>,
1008    /// Value
1009    pub value: f64,
1010}
1011
1012// ============================================================================
1013// Diagnostics
1014// ============================================================================
1015
1016/// Site problem type
1017#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
1018#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1019pub enum ApiSiteProblemTypeEnum {
1020    // FATAL
1021    /// Robots couldn't visit the site (server settings or high load)
1022    ConnectFailed,
1023    /// Site prohibited for indexing in robots.txt
1024    DisallowedInRobots,
1025    /// Failed to connect to server due to DNS error
1026    DnsError,
1027    /// Site's home page returns an error
1028    MainPageError,
1029    /// Security threats or issues detected
1030    Threats,
1031
1032    // CRITICAL
1033    /// Some pages with GET parameters duplicate content of other pages
1034    InsignificantCgiParameter,
1035    /// Slow server response
1036    SlowAvgResponseTime,
1037    /// Invalid SSL certificate settings
1038    SslCertificateError,
1039    /// Some pages respond with 4xx HTTP code for an hour
1040    #[serde(rename = "URL_ALERT_4XX")]
1041    UrlAlert4xx,
1042    /// Some pages respond with 5xx HTTP code for an hour
1043    #[serde(rename = "URL_ALERT_5XX")]
1044    UrlAlert5xx,
1045
1046    // POSSIBLE_PROBLEM
1047    /// Useful pages found that are closed from indexing
1048    DisallowedUrlsAlert,
1049    /// Many pages missing Description meta tag
1050    DocumentsMissingDescription,
1051    /// Title element missing on many pages
1052    DocumentsMissingTitle,
1053    /// Some pages have identical title and Description
1054    DuplicateContentAttrs,
1055    /// Some pages contain identical content
1056    DuplicatePages,
1057    /// Errors in robots.txt file
1058    ErrorInRobotsTxt,
1059    /// Errors found in Sitemap file
1060    ErrorsInSitemaps,
1061    /// Favicon file unavailable on site
1062    FaviconError,
1063    /// Main mirror doesn't use HTTPS protocol
1064    MainMirrorIsNotHttps,
1065    /// Main page redirects to another site
1066    MainPageRedirects,
1067    /// No Yandex.Metrica counter linked to site
1068    NoMetrikaCounterBinding,
1069    /// Site crawling using Yandex.Metrica counters not enabled
1070    NoMetrikaCounterCrawlEnabled,
1071    /// robots.txt file not found
1072    NoRobotsTxt,
1073    /// No Sitemap files used by robot
1074    NoSitemaps,
1075    /// Sitemap files haven't been updated for a long time
1076    NoSitemapModifications,
1077    /// Robot failed to index marked videos on site
1078    NonWorkingVideo,
1079    /// Display of non-existent files and pages configured incorrectly
1080    #[serde(rename = "SOFT_404")]
1081    Soft404,
1082    /// Site subdomains found in search results
1083    TooManyDomainsOnSearch,
1084    /// User agreement for video display added to Webmaster was rejected
1085    VideohostOfferFailed,
1086    /// User agreement for video display missing for site
1087    VideohostOfferIsNeeded,
1088    /// Special agreement with Yandex needed for site cooperation
1089    VideohostOfferNeedPaper,
1090
1091    // RECOMMENDATION
1092    /// Add favicon in SVG format or 120×120 pixels size
1093    BigFaviconAbsent,
1094    /// Favicon file not found - robot couldn't load image for browser tab
1095    FaviconProblem,
1096    /// Yandex.Metrica counter error
1097    NoMetrikaCounter,
1098    /// Site region not set
1099    NoRegions,
1100    /// Site not registered in Yandex.Directory
1101    NotInSprav,
1102    /// Site not optimized for mobile devices
1103    NotMobileFriendly,
1104    /// Yandex.Vygoda not connected to site
1105    VygodaPossibleActivation,
1106}
1107
1108/// Site problem state
1109#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
1110#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1111pub enum ApiSiteProblemState {
1112    /// Present on the site
1113    Present,
1114    /// Missing
1115    Absent,
1116    /// Not enough data to determine if there are issues
1117    Undefined,
1118}
1119
1120/// Site diagnostics response
1121#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1122pub struct DiagnosticsResponse {
1123    /// Problems by type
1124    pub problems: HashMap<ApiSiteProblemTypeEnum, SiteProblemInfo>,
1125}
1126
1127/// Site problem information
1128#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1129pub struct SiteProblemInfo {
1130    /// Issue type (severity)
1131    pub severity: SiteProblemSeverityEnum,
1132    /// State of the issue
1133    pub state: ApiSiteProblemState,
1134    /// Date the issue status was last changed
1135    pub last_state_update: Option<DateTime<Utc>>,
1136}
1137
1138// ============================================================================
1139// Important URLs
1140// ============================================================================
1141
1142/// Important URLs response
1143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1144pub struct ImportantUrlsResponse {
1145    /// URLs
1146    pub urls: Vec<ImportantUrl>,
1147}
1148
1149/// Important URL information
1150#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1151pub struct ImportantUrl {
1152    /// Site page URL
1153    pub url: String,
1154    /// Date and time the page status information was updated
1155    #[serde(skip_serializing_if = "Option::is_none")]
1156    pub update_date: Option<DateTime<Utc>>,
1157    /// Indicator of changes from previous check
1158    #[serde(default)]
1159    pub change_indicators: Vec<ApiImportantUrlChangeIndicator>,
1160    /// Information about page indexing by the robot
1161    #[serde(skip_serializing_if = "Option::is_none")]
1162    pub indexing_status: Option<IndexingStatus>,
1163    /// State of the page in search results
1164    #[serde(skip_serializing_if = "Option::is_none")]
1165    pub search_status: Option<SearchStatus>,
1166}
1167
1168/// Page indexing status
1169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1170pub struct IndexingStatus {
1171    /// Generalized status of the HTTP code
1172    pub status: IndexingStatusEnum,
1173    /// HTTP code
1174    #[serde(skip_serializing_if = "Option::is_none")]
1175    pub http_code: Option<i32>,
1176    /// Date the page was crawled
1177    pub access_date: DateTime<Utc>,
1178}
1179
1180/// Page search status
1181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1182pub struct SearchStatus {
1183    /// Page heading
1184    pub title: String,
1185    /// Description meta tag content
1186    #[serde(skip_serializing_if = "Option::is_none")]
1187    pub description: Option<String>,
1188    /// Date when page was last crawled before appearing or being excluded
1189    pub last_access: DateTime<Utc>,
1190    /// Reason the page was excluded
1191    #[serde(skip_serializing_if = "Option::is_none")]
1192    pub excluded_url_status: Option<ApiExcludedUrlStatus>,
1193    /// Page's HTTP response code for HTTP_ERROR status
1194    #[serde(skip_serializing_if = "Option::is_none")]
1195    pub bad_http_status: Option<i32>,
1196    /// Whether page is present in search results
1197    pub searchable: bool,
1198    /// Another address of the page (redirect target, canonical, or duplicate)
1199    #[serde(skip_serializing_if = "Option::is_none")]
1200    pub target_url: Option<String>,
1201}
1202
1203/// Important URL history response
1204#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1205pub struct ImportantUrlHistoryResponse {
1206    /// History of changes to the page
1207    pub history: Vec<ImportantUrl>,
1208}