@hydrafetch/node-sdk 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,7 +25,7 @@ const page = await hf.scrape('https://example.com/article');
25
25
  console.log(page.markdown);
26
26
  ```
27
27
 
28
- Create a key at [app.hydrafetch.com](https://app.hydrafetch.com). The constructor reads `HYDRAFETCH_API_KEY` when no key is passed.
28
+ Copy your key from [app.hydrafetch.com](https://app.hydrafetch.com). The constructor reads `HYDRAFETCH_API_KEY` when no key is passed.
29
29
 
30
30
  ## Scraping
31
31
 
@@ -82,6 +82,16 @@ out.results.forEach((item) => console.log(item.url, item.data?.name));
82
82
 
83
83
  Pass `prompt` instead of, or alongside, `schema` to describe the fields in plain language.
84
84
 
85
+ Sites that price or stock by delivery area serve a different page for each area. Pass `location` to fetch as if from a country and language, and `headers` to send the cookie that picks the store or area, the same options `scrape` takes.
86
+
87
+ ```ts
88
+ const out = await hf.extract(['https://shop.example/search?q=ghee'], {
89
+ prompt: 'every product with its size and price',
90
+ location: { country: 'in' },
91
+ headers: { Cookie: 'store_id=42' },
92
+ });
93
+ ```
94
+
85
95
  ### Zod schemas
86
96
 
87
97
  If you already use Zod, pass the schema directly. Zod stays optional and is imported only when you
@@ -167,6 +177,24 @@ await hf.styleguide('https://stripe.com/pricing', { maxAge: 0 }); // one page, r
167
177
 
168
178
  For logos in a browser use [`@hydrafetch/client-sdk`](https://github.com/Hydrafetch/client-sdk) or [`@hydrafetch/react`](https://github.com/Hydrafetch/react-sdk) with a publishable key. Those bill against logo pulls rather than credits.
169
179
 
180
+ ## Rating results
181
+
182
+ Tell us when a result was wrong, so it gets looked at. It is free, and rating the same request again replaces the earlier rating.
183
+
184
+ ```ts
185
+ const page = await hf.scrape('https://stripe.com/pricing');
186
+ await hf.rateResult({
187
+ requestId: hf.lastRequestId,
188
+ rating: 'bad',
189
+ reasons: ['missing_content'],
190
+ note: 'The enterprise tier is missing',
191
+ });
192
+
193
+ await hf.rateResult({ url: 'https://stripe.com/pricing', rating: 'good' }); // by URL, last 24 hours
194
+ ```
195
+
196
+ Reasons are `missing_content`, `wrong_content`, `blocked`, `slow`, `formatting` and `other`.
197
+
170
198
  ## Error handling
171
199
 
172
200
  All failures throw `HydrafetchError`, carrying the API's error code, HTTP status and request id.
@@ -235,6 +263,7 @@ const hf = new Hydrafetch({
235
263
  | `startCrawl(url, options?)` | `string` | 1 per page |
236
264
  | `startBatch(urls, options?)` | `string` | 1 per page |
237
265
  | `crawlStatus(id)`, `batchStatus(id)` | `JobResult` | free |
266
+ | `rateResult(input)` | `RateResultResponse` | free |
238
267
 
239
268
  Failed requests are not billed. Pricing does not vary with page difficulty, so there is no render, stealth or proxy option to set.
240
269
 
package/dist/index.cjs CHANGED
@@ -129,6 +129,10 @@ var Hydrafetch = class {
129
129
  timeoutMs;
130
130
  maxRetries;
131
131
  fetchImpl;
132
+ latestRequestId;
133
+ get lastRequestId() {
134
+ return this.latestRequestId;
135
+ }
132
136
  constructor(options = {}) {
133
137
  const opts = typeof options === "string" ? { apiKey: options } : options;
134
138
  const env = globalThis.process?.env;
@@ -136,7 +140,7 @@ var Hydrafetch = class {
136
140
  if (!apiKey) {
137
141
  throw new HydrafetchError({
138
142
  code: "MISSING_API_KEY",
139
- message: "No API key. Pass one to the constructor or set HYDRAFETCH_API_KEY. Create a key at https://app.hydrafetch.com.",
143
+ message: "No API key. Pass one to the constructor or set HYDRAFETCH_API_KEY. Copy your key from https://app.hydrafetch.com.",
140
144
  status: 401
141
145
  });
142
146
  }
@@ -174,6 +178,7 @@ var Hydrafetch = class {
174
178
  body: body === void 0 ? void 0 : JSON.stringify(body),
175
179
  signal: controller.signal
176
180
  });
181
+ this.latestRequestId = res.headers.get("x-request-id") ?? this.latestRequestId;
177
182
  const text = await res.text();
178
183
  let parsed;
179
184
  try {
@@ -221,6 +226,9 @@ var Hydrafetch = class {
221
226
  const page = await this.scrape(url, { ...options, formats: ["markdown"] });
222
227
  return page.markdown;
223
228
  }
229
+ rateResult(input) {
230
+ return this.request("POST", "/v1/web/feedback", input);
231
+ }
224
232
  map(url, options = {}) {
225
233
  return this.request("POST", "/v1/web/map", { url, ...options });
226
234
  }
package/dist/index.d.cts CHANGED
@@ -34,6 +34,10 @@ interface ScrapeBase {
34
34
  type ScrapeResult<F extends Format = 'markdown'> = ScrapeBase & {
35
35
  [K in F]: ScrapeContent[K];
36
36
  } & Partial<Omit<ScrapeContent, F>>;
37
+ interface Location {
38
+ country?: string;
39
+ languages?: string[];
40
+ }
37
41
  interface ScrapeOptions<F extends readonly Format[] = readonly Format[]> {
38
42
  formats?: F;
39
43
  maxAge?: number;
@@ -48,6 +52,7 @@ interface ScrapeOptions<F extends readonly Format[] = readonly Format[]> {
48
52
  includeLinks?: boolean;
49
53
  waitFor?: number;
50
54
  timeout?: number;
55
+ location?: Location;
51
56
  headers?: Record<string, string>;
52
57
  jsonOptions?: Record<string, unknown>;
53
58
  }
@@ -114,6 +119,8 @@ interface ExtractOptions {
114
119
  showConfidence?: boolean;
115
120
  mergeEntities?: boolean;
116
121
  maxAge?: number;
122
+ location?: Location;
123
+ headers?: Record<string, string>;
117
124
  }
118
125
  interface ExtractItem<T = unknown> {
119
126
  url: string;
@@ -231,6 +238,20 @@ interface WaitOptions {
231
238
  pollIntervalMs?: number;
232
239
  onProgress?: (job: JobResult) => void;
233
240
  }
241
+ type ResultRating = 'good' | 'bad';
242
+ type ResultFeedbackReason = 'missing_content' | 'wrong_content' | 'blocked' | 'slow' | 'formatting' | 'other';
243
+ interface RateResultInput {
244
+ requestId?: string;
245
+ url?: string;
246
+ rating: ResultRating;
247
+ reasons?: ResultFeedbackReason[];
248
+ note?: string;
249
+ }
250
+ interface RateResultResponse {
251
+ id: string;
252
+ requestId: string;
253
+ rating: ResultRating;
254
+ }
234
255
 
235
256
  interface HydrafetchOptions {
236
257
  apiKey?: string;
@@ -245,10 +266,13 @@ declare class Hydrafetch {
245
266
  private readonly timeoutMs;
246
267
  private readonly maxRetries;
247
268
  private readonly fetchImpl;
269
+ private latestRequestId;
270
+ get lastRequestId(): string | undefined;
248
271
  constructor(options?: HydrafetchOptions | string);
249
272
  private request;
250
273
  scrape<const F extends readonly Format[] = ['markdown']>(url: string, options?: ScrapeOptions<F>): Promise<ScrapeResult<F[number]>>;
251
274
  markdown(url: string, options?: Omit<ScrapeOptions, 'formats'>): Promise<string>;
275
+ rateResult(input: RateResultInput): Promise<RateResultResponse>;
252
276
  map(url: string, options?: MapOptions): Promise<MapResult>;
253
277
  search(query: string, options?: SearchOptions): Promise<SearchResult>;
254
278
  extract<T = unknown>(urls: string | string[], options?: ExtractOptions): Promise<ExtractResult<T>>;
@@ -290,4 +314,4 @@ declare class HydrafetchTimeoutError extends HydrafetchError {
290
314
  constructor(message: string);
291
315
  }
292
316
 
293
- export { type BatchOptions, type BrandOptions, type BrandResult, type CrawlOptions, type ExtractItem, type ExtractOptions, type ExtractResult, type ExtractSchema, type Format, Hydrafetch, HydrafetchError, type HydrafetchOptions, HydrafetchTimeoutError, type ImagesResult, type JobPage, type JobResult, type JobStatus, type LogoResult, type MapOptions, type MapResult, type PageImage, type PageMetadata, type ScrapeBase, type ScrapeContent, type ScrapeOptions, type ScrapeResult, type ScreenshotOptions, type ScreenshotResult, type SearchOptions, type SearchResult, type SearchResultItem, type StyleguideOptions, type StyleguideResult, type Usage, type WaitOptions, type ZodLike };
317
+ export { type BatchOptions, type BrandOptions, type BrandResult, type CrawlOptions, type ExtractItem, type ExtractOptions, type ExtractResult, type ExtractSchema, type Format, Hydrafetch, HydrafetchError, type HydrafetchOptions, HydrafetchTimeoutError, type ImagesResult, type JobPage, type JobResult, type JobStatus, type Location, type LogoResult, type MapOptions, type MapResult, type PageImage, type PageMetadata, type RateResultInput, type RateResultResponse, type ResultFeedbackReason, type ResultRating, type ScrapeBase, type ScrapeContent, type ScrapeOptions, type ScrapeResult, type ScreenshotOptions, type ScreenshotResult, type SearchOptions, type SearchResult, type SearchResultItem, type StyleguideOptions, type StyleguideResult, type Usage, type WaitOptions, type ZodLike };
package/dist/index.d.ts CHANGED
@@ -34,6 +34,10 @@ interface ScrapeBase {
34
34
  type ScrapeResult<F extends Format = 'markdown'> = ScrapeBase & {
35
35
  [K in F]: ScrapeContent[K];
36
36
  } & Partial<Omit<ScrapeContent, F>>;
37
+ interface Location {
38
+ country?: string;
39
+ languages?: string[];
40
+ }
37
41
  interface ScrapeOptions<F extends readonly Format[] = readonly Format[]> {
38
42
  formats?: F;
39
43
  maxAge?: number;
@@ -48,6 +52,7 @@ interface ScrapeOptions<F extends readonly Format[] = readonly Format[]> {
48
52
  includeLinks?: boolean;
49
53
  waitFor?: number;
50
54
  timeout?: number;
55
+ location?: Location;
51
56
  headers?: Record<string, string>;
52
57
  jsonOptions?: Record<string, unknown>;
53
58
  }
@@ -114,6 +119,8 @@ interface ExtractOptions {
114
119
  showConfidence?: boolean;
115
120
  mergeEntities?: boolean;
116
121
  maxAge?: number;
122
+ location?: Location;
123
+ headers?: Record<string, string>;
117
124
  }
118
125
  interface ExtractItem<T = unknown> {
119
126
  url: string;
@@ -231,6 +238,20 @@ interface WaitOptions {
231
238
  pollIntervalMs?: number;
232
239
  onProgress?: (job: JobResult) => void;
233
240
  }
241
+ type ResultRating = 'good' | 'bad';
242
+ type ResultFeedbackReason = 'missing_content' | 'wrong_content' | 'blocked' | 'slow' | 'formatting' | 'other';
243
+ interface RateResultInput {
244
+ requestId?: string;
245
+ url?: string;
246
+ rating: ResultRating;
247
+ reasons?: ResultFeedbackReason[];
248
+ note?: string;
249
+ }
250
+ interface RateResultResponse {
251
+ id: string;
252
+ requestId: string;
253
+ rating: ResultRating;
254
+ }
234
255
 
235
256
  interface HydrafetchOptions {
236
257
  apiKey?: string;
@@ -245,10 +266,13 @@ declare class Hydrafetch {
245
266
  private readonly timeoutMs;
246
267
  private readonly maxRetries;
247
268
  private readonly fetchImpl;
269
+ private latestRequestId;
270
+ get lastRequestId(): string | undefined;
248
271
  constructor(options?: HydrafetchOptions | string);
249
272
  private request;
250
273
  scrape<const F extends readonly Format[] = ['markdown']>(url: string, options?: ScrapeOptions<F>): Promise<ScrapeResult<F[number]>>;
251
274
  markdown(url: string, options?: Omit<ScrapeOptions, 'formats'>): Promise<string>;
275
+ rateResult(input: RateResultInput): Promise<RateResultResponse>;
252
276
  map(url: string, options?: MapOptions): Promise<MapResult>;
253
277
  search(query: string, options?: SearchOptions): Promise<SearchResult>;
254
278
  extract<T = unknown>(urls: string | string[], options?: ExtractOptions): Promise<ExtractResult<T>>;
@@ -290,4 +314,4 @@ declare class HydrafetchTimeoutError extends HydrafetchError {
290
314
  constructor(message: string);
291
315
  }
292
316
 
293
- export { type BatchOptions, type BrandOptions, type BrandResult, type CrawlOptions, type ExtractItem, type ExtractOptions, type ExtractResult, type ExtractSchema, type Format, Hydrafetch, HydrafetchError, type HydrafetchOptions, HydrafetchTimeoutError, type ImagesResult, type JobPage, type JobResult, type JobStatus, type LogoResult, type MapOptions, type MapResult, type PageImage, type PageMetadata, type ScrapeBase, type ScrapeContent, type ScrapeOptions, type ScrapeResult, type ScreenshotOptions, type ScreenshotResult, type SearchOptions, type SearchResult, type SearchResultItem, type StyleguideOptions, type StyleguideResult, type Usage, type WaitOptions, type ZodLike };
317
+ export { type BatchOptions, type BrandOptions, type BrandResult, type CrawlOptions, type ExtractItem, type ExtractOptions, type ExtractResult, type ExtractSchema, type Format, Hydrafetch, HydrafetchError, type HydrafetchOptions, HydrafetchTimeoutError, type ImagesResult, type JobPage, type JobResult, type JobStatus, type Location, type LogoResult, type MapOptions, type MapResult, type PageImage, type PageMetadata, type RateResultInput, type RateResultResponse, type ResultFeedbackReason, type ResultRating, type ScrapeBase, type ScrapeContent, type ScrapeOptions, type ScrapeResult, type ScreenshotOptions, type ScreenshotResult, type SearchOptions, type SearchResult, type SearchResultItem, type StyleguideOptions, type StyleguideResult, type Usage, type WaitOptions, type ZodLike };
package/dist/index.js CHANGED
@@ -101,6 +101,10 @@ var Hydrafetch = class {
101
101
  timeoutMs;
102
102
  maxRetries;
103
103
  fetchImpl;
104
+ latestRequestId;
105
+ get lastRequestId() {
106
+ return this.latestRequestId;
107
+ }
104
108
  constructor(options = {}) {
105
109
  const opts = typeof options === "string" ? { apiKey: options } : options;
106
110
  const env = globalThis.process?.env;
@@ -108,7 +112,7 @@ var Hydrafetch = class {
108
112
  if (!apiKey) {
109
113
  throw new HydrafetchError({
110
114
  code: "MISSING_API_KEY",
111
- message: "No API key. Pass one to the constructor or set HYDRAFETCH_API_KEY. Create a key at https://app.hydrafetch.com.",
115
+ message: "No API key. Pass one to the constructor or set HYDRAFETCH_API_KEY. Copy your key from https://app.hydrafetch.com.",
112
116
  status: 401
113
117
  });
114
118
  }
@@ -146,6 +150,7 @@ var Hydrafetch = class {
146
150
  body: body === void 0 ? void 0 : JSON.stringify(body),
147
151
  signal: controller.signal
148
152
  });
153
+ this.latestRequestId = res.headers.get("x-request-id") ?? this.latestRequestId;
149
154
  const text = await res.text();
150
155
  let parsed;
151
156
  try {
@@ -193,6 +198,9 @@ var Hydrafetch = class {
193
198
  const page = await this.scrape(url, { ...options, formats: ["markdown"] });
194
199
  return page.markdown;
195
200
  }
201
+ rateResult(input) {
202
+ return this.request("POST", "/v1/web/feedback", input);
203
+ }
196
204
  map(url, options = {}) {
197
205
  return this.request("POST", "/v1/web/map", { url, ...options });
198
206
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydrafetch/node-sdk",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Official Node/TypeScript client for the Hydrafetch web data API. Turn any URL into clean Markdown and structured data.",
5
5
  "keywords": [
6
6
  "hydrafetch",