yandex_webmaster_api/
dto.rs

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