Skip to main content

pagesift_api/types/
response.rs

1//! Firecrawl v1/v2 response types and metadata structures.
2
3use std::collections::BTreeMap;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7/// Explanation when a query-directed crawl completes (`pagesift` field in job status).
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9#[serde(rename_all = "camelCase")]
10pub struct CrawlSummary {
11    /// `coverage_reached`, `saturated`, `limit_reached`, `exhausted`, `cancelled`.
12    pub stop_reason: String,
13    /// Coverage percentage (0.0 to 1.0) when adaptive crawl completes.
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub coverage: Option<f64>,
16    /// Number of distinct evidence blocks gathered for each term.
17    #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
18    pub term_evidence: BTreeMap<String, usize>,
19    /// Actual pages fetched.
20    pub pages: usize,
21}
22
23/// PageSift extensions inside `metadata.pagesift`.
24#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct PagesiftMeta {
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub content_hash: Option<String>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub raw_hash: Option<String>,
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub text_hash: Option<String>,
33    #[serde(skip_serializing_if = "Vec::is_empty", default)]
34    pub redirect_chain: Vec<String>,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub etag: Option<String>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub last_modified: Option<String>,
39    #[serde(skip_serializing_if = "std::ops::Not::not", default)]
40    pub not_modified: bool,
41    /// `http` or `browser`.
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub engine: Option<String>,
44    /// `lightpanda`, `moli`, or `chrome`.
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub browser_engine: Option<String>,
47    #[serde(skip_serializing_if = "Vec::is_empty", default)]
48    pub warnings: Vec<String>,
49    /// Timing per phase in milliseconds.
50    #[serde(skip_serializing_if = "BTreeMap::is_empty", default)]
51    pub timings: BTreeMap<String, f64>,
52    /// Duplicate canonical reference URL.
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub duplicate_of: Option<String>,
55    /// Query relevance score (0.0 to 1.0).
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub relevance: Option<f32>,
58    /// Query filter statistics.
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub query_filter: Option<QueryFilterStats>,
61}
62
63#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
64#[serde(rename_all = "camelCase")]
65pub struct QueryFilterStats {
66    pub kept_blocks: usize,
67    pub total_blocks: usize,
68    pub tokens_before: usize,
69    pub tokens_after: usize,
70}
71
72/// Firecrawl `changeTracking` response object.
73#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
74#[serde(rename_all = "camelCase")]
75pub struct ChangeTracking {
76    pub previous_scrape_at: Option<String>,
77    /// `new`, `same`, `changed`, `removed`.
78    pub change_status: String,
79    /// `visible` or `hidden`.
80    pub visibility: String,
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub diff: Option<Value>,
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub json: Option<Value>,
85    #[serde(skip_serializing_if = "Option::is_none")]
86    pub pagesift: Option<Value>,
87}
88
89#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
90#[serde(rename_all = "camelCase")]
91pub struct Metadata {
92    #[serde(skip_serializing_if = "Option::is_none")]
93    pub title: Option<String>,
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub description: Option<String>,
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub language: Option<String>,
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub keywords: Option<String>,
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub robots: Option<String>,
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub favicon: Option<String>,
104    #[serde(skip_serializing_if = "Option::is_none")]
105    pub published_time: Option<String>,
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub author: Option<String>,
108    #[serde(skip_serializing_if = "Option::is_none")]
109    pub modified_time: Option<String>,
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub url: Option<String>,
112    #[serde(rename = "sourceURL", skip_serializing_if = "Option::is_none")]
113    pub source_url: Option<String>,
114    pub status_code: u16,
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub content_type: Option<String>,
117    #[serde(skip_serializing_if = "Option::is_none")]
118    pub error: Option<String>,
119    pub proxy_used: String,
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub cache_state: Option<String>,
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub cached_at: Option<String>,
124    #[serde(flatten)]
125    pub extra: BTreeMap<String, Value>,
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub pagesift: Option<PagesiftMeta>,
128}
129
130#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
131#[serde(rename_all = "camelCase")]
132pub struct Document {
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub markdown: Option<String>,
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub html: Option<String>,
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub raw_html: Option<String>,
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub links: Option<Vec<String>>,
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub images: Option<Vec<String>>,
143    #[serde(skip_serializing_if = "Option::is_none")]
144    pub screenshot: Option<String>,
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub json: Option<Value>,
147    #[serde(skip_serializing_if = "Option::is_none")]
148    pub summary: Option<String>,
149    #[serde(skip_serializing_if = "Option::is_none")]
150    pub actions: Option<Value>,
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub warning: Option<String>,
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub change_tracking: Option<ChangeTracking>,
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub chunks: Option<Vec<DocChunk>>,
157    pub metadata: Metadata,
158}
159
160#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
161#[serde(rename_all = "camelCase")]
162pub struct DocChunk {
163    pub text: String,
164    pub heading_path: Vec<String>,
165    pub start: usize,
166    pub end: usize,
167    pub tokens: usize,
168}
169
170#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
171pub struct MapLink {
172    pub url: String,
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub title: Option<String>,
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub description: Option<String>,
177}
178
179#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
180pub struct ScrapeResponse {
181    pub success: bool,
182    pub data: Document,
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub warning: Option<String>,
185}
186
187#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
188#[serde(rename_all = "camelCase")]
189pub struct JobStarted {
190    pub success: bool,
191    pub id: String,
192    pub url: String,
193    #[serde(rename = "invalidURLs", skip_serializing_if = "Option::is_none")]
194    pub invalid_urls: Option<Vec<String>>,
195}
196
197#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
198#[serde(rename_all = "camelCase")]
199pub struct JobStatus {
200    pub success: bool,
201    pub status: String,
202    pub completed: usize,
203    pub total: usize,
204    pub credits_used: usize,
205    pub expires_at: String,
206    #[serde(skip_serializing_if = "Option::is_none")]
207    pub next: Option<String>,
208    pub data: Vec<Document>,
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub pagesift: Option<CrawlSummary>,
211}
212
213#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
214pub struct JobError {
215    pub id: String,
216    pub timestamp: String,
217    pub url: String,
218    pub code: String,
219    pub error: String,
220}
221
222#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
223#[serde(rename_all = "camelCase")]
224pub struct JobErrors {
225    pub errors: Vec<JobError>,
226    pub robots_blocked: Vec<String>,
227}
228
229#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
230pub struct MapResponse {
231    pub success: bool,
232    pub links: Vec<MapLink>,
233}