minifetch-api 1.6.0 → 1.7.1
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 +28 -20
- package/dist/esm/client.js +112 -23
- package/dist/esm/types/errors.js +44 -0
- package/dist/esm/utils/validation.js +27 -1
- package/dist/types/client.d.ts +58 -15
- package/dist/types/types/errors.d.ts +32 -0
- package/dist/types/types/responses.d.ts +35 -0
- package/dist/types/utils/validation.d.ts +9 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
</a>
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
|
-
**[Minifetch](https://minifetch.com) is a hosted toolkit
|
|
8
|
+
**[Search, scrape, extract & SEO audit web pages. Minifetch](https://minifetch.com)** is a hosted toolkit for web developers and AI agents. Pay per fetch, no subscription.
|
|
9
9
|
|
|
10
10
|
- ✅ **Always pay-per-fetch at competitive prices.**
|
|
11
11
|
- ✅ [Sign up](https://minifetch.com/dashboard) for an account & get free starter credits. 🎉🎉
|
|
@@ -72,19 +72,18 @@ After the Quick Start, you have the following methods to use.
|
|
|
72
72
|
|
|
73
73
|
**Wrap** these methods in a **try/catch** just like in the Quick Start example above. **Code examples** can be also found in the [Github repository /example- directories](https://github.com/Niche-Networks/minifetch-api/).
|
|
74
74
|
|
|
75
|
-
The `checkAndExtract` methods check the target URL's `robots.txt` file to ensure its not blocked and tell us your preferred crawl delay (defaults to 1 second between requests to your domain). So fetching 10 URLs takes at least 10 seconds to complete by default. This is by design, so Minifetch never hammers your server or slows it down for your real users. If you own the site and want to allow Minifetch access or to set custom rules for it, read [How To Unblock Minifetch](https://minifetch.com/tutorials/unblock-minifetch).
|
|
76
|
-
|
|
77
75
|
All API methods default to POST unless you set options to `{ method: 'GET' }` and pass in as the second argument.
|
|
78
76
|
|
|
77
|
+
The `checkAndExtract` methods check the target URL's `robots.txt` file to ensure its not blocked and tell us your preferred crawl delay (defaults to 1 second between requests to your domain). So fetching 10 URLs takes at least 10 seconds to complete by default. This is by design, so Minifetch never hammers your server or slows it down for your real users. If you own the site and want to allow Minifetch access or to set custom rules for it, read [How To Unblock Minifetch](https://minifetch.com/tutorials/unblock-minifetch).
|
|
78
|
+
|
|
79
79
|
```js
|
|
80
|
-
await client.
|
|
81
|
-
// Price: $0.
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
// https://minifetch.com/skills/seo-page-audit/SKILL.md
|
|
80
|
+
await client.searchByKeyword("green tea", options);
|
|
81
|
+
// Price: $0.002
|
|
82
|
+
// Keyword web search by Ceramic.ai that returns ranked results as
|
|
83
|
+
// structured JSON: a title, URL, and text snippet per result.
|
|
84
|
+
// Options:
|
|
85
|
+
// { limit: 10 } - defaults to 10 results, set to 1-10
|
|
86
|
+
// { descriptionLength: 750 } - defaults to 750, set to 0-5000
|
|
88
87
|
|
|
89
88
|
await client.checkAndExtractUrlMetadata(url, options);
|
|
90
89
|
// Price: $0.002
|
|
@@ -108,36 +107,45 @@ await client.checkAndExtractUrlLinks(url);
|
|
|
108
107
|
// anchor text variants used for each) and top external domains by
|
|
109
108
|
// link count.
|
|
110
109
|
|
|
111
|
-
await client.checkAndExtractUrlPreview(url);
|
|
112
|
-
// Price: $0.002
|
|
113
|
-
// Extracts all fields used for a page's share previews: the lightweight
|
|
114
|
-
// cards that represent the page on social platforms, chat apps, and AI.
|
|
115
|
-
|
|
116
110
|
await client.checkAndExtractUrlContent(url, options);
|
|
117
111
|
// Price: $0.002
|
|
118
112
|
// For site owners auditing AI readability: returns the clean markdown
|
|
119
113
|
// an LLM extracts from your page after nav, ads, & scripts are stripped.
|
|
120
114
|
// See what survives for AEO and AI search; respects robots.txt.
|
|
121
115
|
// Options: { includeMediaUrls: true } - defaults to false.
|
|
116
|
+
|
|
117
|
+
await client.checkAndExtractUrlPreview(url);
|
|
118
|
+
// Price: $0.002
|
|
119
|
+
// Extracts all fields used for a page's share previews: the lightweight
|
|
120
|
+
// cards that represent the page on social platforms, chat apps, and AI.
|
|
121
|
+
|
|
122
|
+
await client.checkAndRunSeoPageAudit(url);
|
|
123
|
+
// Price: $0.01
|
|
124
|
+
// Runs a full technical SEO audit on your URL. Combines data from
|
|
125
|
+
// the other API endpoints and runs checks that each return a PASS/
|
|
126
|
+
// WARN/ FAIL result with no black-box scoring. Just deterministic,
|
|
127
|
+
// composable signal you can act on or pipe into an agent.
|
|
128
|
+
// Audit rules are documented in the skill file:
|
|
129
|
+
// https://minifetch.com/skills/seo-page-audit/SKILL.md
|
|
122
130
|
```
|
|
123
131
|
|
|
124
132
|
For max control, you can also use the following methods directly:
|
|
125
133
|
```js
|
|
126
134
|
await client.preflightCheck(url, options);
|
|
127
|
-
// Free: check if `minfetch` user agent can access target via robots.txt
|
|
135
|
+
// Free: check if `minfetch` user agent can access target URL via robots.txt
|
|
128
136
|
// Options: { "fresh": true } - bypass 24hr robots.txt cache, defaults to false
|
|
129
137
|
|
|
130
138
|
// Paid methods:
|
|
131
|
-
await client.runSeoPageAudit(url);
|
|
132
139
|
await client.extractUrlMetadata(url, options); // same options as above
|
|
133
140
|
await client.extractUrlLinks(url);
|
|
134
|
-
await client.extractUrlPreview(url);
|
|
135
141
|
await client.extractUrlContent(url, options); // same options as above
|
|
142
|
+
await client.extractUrlPreview(url);
|
|
143
|
+
await client.runSeoPageAudit(url);
|
|
136
144
|
```
|
|
137
145
|
---
|
|
138
146
|
|
|
139
147
|
### Error Types
|
|
140
|
-
When you wrap the functions above in a try/catch, here are some of the errors you may encounter. You are never charged for URLs that are blocked or error.
|
|
148
|
+
When you wrap the functions above in a try/catch, here are some of the errors you may encounter. You are never charged for target URLs that are blocked or error.
|
|
141
149
|
|
|
142
150
|
- **"InvalidURLError: Invalid url ${url}"**
|
|
143
151
|
- The URL is malformed in some way, correct it and try again.
|
package/dist/esm/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { initConfig } from "./init.js";
|
|
2
|
-
import { validateAndNormalizeUrl } from "./utils/validation.js";
|
|
2
|
+
import { validateAndNormalizeUrl, validateAndNormalizeQuery } from "./utils/validation.js";
|
|
3
3
|
import { handlePayment, handleApiKeyRequest } from "./utils/payment.js";
|
|
4
|
-
import { InvalidUrlError, RobotsBlockedError, PaymentFailedError, ExtractionFailedError, NetworkError, ConfigurationError, } from "./types/errors.js";
|
|
4
|
+
import { InvalidUrlError, InvalidQueryError, RobotsBlockedError, PaymentFailedError, ExtractionFailedError, SearchFailedError, NetworkError, ConfigurationError, } from "./types/errors.js";
|
|
5
5
|
/** Every endpoint accepts GET (query string) or POST (JSON body). POST is the default. */
|
|
6
6
|
const DEFAULT_METHOD = "POST";
|
|
7
7
|
/**
|
|
@@ -24,6 +24,39 @@ export class MinifetchClient {
|
|
|
24
24
|
this.config = initConfig(config);
|
|
25
25
|
this.baseUrl = this.config.apiBaseUrl;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Search the web by keyword (paid endpoint).
|
|
29
|
+
*
|
|
30
|
+
* Unlike the URL-based methods, this takes a search query rather than a URL and
|
|
31
|
+
* proxies to Minifetch's keyword search. Returns ranked results, each with a
|
|
32
|
+
* title, URL, and text snippet. `limit` and `descriptionLength` are convenience
|
|
33
|
+
* knobs the server clamps into range; the effective values (after clamping) are
|
|
34
|
+
* echoed back on `queryParameters`.
|
|
35
|
+
*
|
|
36
|
+
* @param query - keyword(s) to search for (1-50 characters)
|
|
37
|
+
* @param options
|
|
38
|
+
* @param options.limit - number of results, 1-10 (default 10). Out-of-range values are clamped.
|
|
39
|
+
* @param options.descriptionLength - max characters per snippet, 0-5000 (default 750, 0 = titles/URLs only). Out-of-range values are clamped.
|
|
40
|
+
* @param options.method - "GET" or "POST" (default POST)
|
|
41
|
+
* @throws {InvalidQueryError} if the query is empty or exceeds 50 characters
|
|
42
|
+
* @throws {SearchFailedError} if the search request fails
|
|
43
|
+
* @throws {PaymentFailedError} if x402 payment fails
|
|
44
|
+
* @throws {NetworkError} various reasons, check README
|
|
45
|
+
*/
|
|
46
|
+
async searchByKeyword(query, options) {
|
|
47
|
+
try {
|
|
48
|
+
const cleanQuery = validateAndNormalizeQuery(query);
|
|
49
|
+
const params = { query: cleanQuery };
|
|
50
|
+
if (options?.limit !== undefined)
|
|
51
|
+
params.limit = options.limit;
|
|
52
|
+
if (options?.descriptionLength !== undefined)
|
|
53
|
+
params.descriptionLength = options.descriptionLength;
|
|
54
|
+
return await this._makeSearchRequest(params, options?.method);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
return this._rethrowSearchError(error, query, "Keyword search");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
27
60
|
/**
|
|
28
61
|
* Check if URL is allowed by robots.txt (free preflight check — no auth required)
|
|
29
62
|
*
|
|
@@ -85,27 +118,6 @@ export class MinifetchClient {
|
|
|
85
118
|
return this._rethrowError(error, url, "Paid URL check");
|
|
86
119
|
}
|
|
87
120
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Run SEO page audit (paid endpoint)
|
|
90
|
-
*
|
|
91
|
-
* @param url
|
|
92
|
-
* @param options
|
|
93
|
-
* @param options.method - "GET" or "POST" (default POST)
|
|
94
|
-
* @throws {InvalidUrlError} if URL is invalid
|
|
95
|
-
* @throws {ExtractionFailedError} various reasons, check README
|
|
96
|
-
* @throws {PaymentFailedError} if x402 payment fails
|
|
97
|
-
* @throws {NetworkError} various reasons, check README
|
|
98
|
-
*/
|
|
99
|
-
async runSeoPageAudit(url, options) {
|
|
100
|
-
try {
|
|
101
|
-
const normalizedUrl = validateAndNormalizeUrl(url);
|
|
102
|
-
const params = { url: normalizedUrl };
|
|
103
|
-
return await this._makeRequest("/run/seo-page-audit", normalizedUrl, "Run SEO page audit", params, options?.method);
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
return this._rethrowError(error, url, "Run SEO page audit");
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
121
|
/**
|
|
110
122
|
* Extract URL metadata (paid endpoint)
|
|
111
123
|
*
|
|
@@ -202,6 +214,27 @@ export class MinifetchClient {
|
|
|
202
214
|
return this._rethrowError(error, url, "Content extraction");
|
|
203
215
|
}
|
|
204
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Run SEO page audit (paid endpoint)
|
|
219
|
+
*
|
|
220
|
+
* @param url
|
|
221
|
+
* @param options
|
|
222
|
+
* @param options.method - "GET" or "POST" (default POST)
|
|
223
|
+
* @throws {InvalidUrlError} if URL is invalid
|
|
224
|
+
* @throws {ExtractionFailedError} various reasons, check README
|
|
225
|
+
* @throws {PaymentFailedError} if x402 payment fails
|
|
226
|
+
* @throws {NetworkError} various reasons, check README
|
|
227
|
+
*/
|
|
228
|
+
async runSeoPageAudit(url, options) {
|
|
229
|
+
try {
|
|
230
|
+
const normalizedUrl = validateAndNormalizeUrl(url);
|
|
231
|
+
const params = { url: normalizedUrl };
|
|
232
|
+
return await this._makeRequest("/run/seo-page-audit", normalizedUrl, "Run SEO page audit", params, options?.method);
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
return this._rethrowError(error, url, "Run SEO page audit");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
205
238
|
/**
|
|
206
239
|
* Check URL then run SEO page audit in one call.
|
|
207
240
|
* Throws RobotsBlockedError if robots.txt blocks the URL.
|
|
@@ -336,6 +369,45 @@ export class MinifetchClient {
|
|
|
336
369
|
return { success: data.success, results: data.results };
|
|
337
370
|
}
|
|
338
371
|
}
|
|
372
|
+
/**
|
|
373
|
+
* Search-specific request path. Mirrors {@link _makeRequest} but for the
|
|
374
|
+
* keyword search endpoint: there is no URL, non-OK responses surface as
|
|
375
|
+
* SearchFailedError (carrying the query), and the server's clamped
|
|
376
|
+
* `queryParameters` echo is preserved rather than dropped.
|
|
377
|
+
*
|
|
378
|
+
* @param params - request params (query + optional limit/descriptionLength)
|
|
379
|
+
* @param method - "GET" or "POST" (default POST)
|
|
380
|
+
*/
|
|
381
|
+
async _makeSearchRequest(params, method = DEFAULT_METHOD) {
|
|
382
|
+
const query = String(params.query);
|
|
383
|
+
const { url, init } = this._buildRequest(this._paidPath("/search/keyword"), params, method);
|
|
384
|
+
if (this.config.authMode === "x402") {
|
|
385
|
+
const { response, payment } = await handlePayment(url, this.config, init);
|
|
386
|
+
if (!response.ok) {
|
|
387
|
+
throw new SearchFailedError(query, `Keyword search failed: ${response.status} ${response.statusText}`, response.status);
|
|
388
|
+
}
|
|
389
|
+
const data = (await response.json());
|
|
390
|
+
return {
|
|
391
|
+
success: data.success,
|
|
392
|
+
queryParameters: data.queryParameters,
|
|
393
|
+
results: data.results,
|
|
394
|
+
payment,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
const { response } = await handleApiKeyRequest(url, this.config, init);
|
|
399
|
+
if (!response.ok) {
|
|
400
|
+
throw new SearchFailedError(query, `Keyword search failed: ${response.status} ${response.statusText}`, response.status);
|
|
401
|
+
}
|
|
402
|
+
const data = (await response.json());
|
|
403
|
+
// payment field intentionally omitted for apiKey auth — not applicable
|
|
404
|
+
return {
|
|
405
|
+
success: data.success,
|
|
406
|
+
queryParameters: data.queryParameters,
|
|
407
|
+
results: data.results,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
}
|
|
339
411
|
/**
|
|
340
412
|
* Preflight check helper — throws RobotsBlockedError if not allowed
|
|
341
413
|
*
|
|
@@ -364,4 +436,21 @@ export class MinifetchClient {
|
|
|
364
436
|
}
|
|
365
437
|
throw new ExtractionFailedError(url, `${label} failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
366
438
|
}
|
|
439
|
+
/**
|
|
440
|
+
* Search sibling of {@link _rethrowError}: re-throw known search error types,
|
|
441
|
+
* wrapping anything else in SearchFailedError (which carries the query, not a URL).
|
|
442
|
+
*
|
|
443
|
+
* @param error
|
|
444
|
+
* @param query
|
|
445
|
+
* @param label
|
|
446
|
+
*/
|
|
447
|
+
_rethrowSearchError(error, query, label) {
|
|
448
|
+
if (error instanceof InvalidQueryError ||
|
|
449
|
+
error instanceof SearchFailedError ||
|
|
450
|
+
error instanceof PaymentFailedError ||
|
|
451
|
+
error instanceof NetworkError) {
|
|
452
|
+
throw error;
|
|
453
|
+
}
|
|
454
|
+
throw new SearchFailedError(query, `${label} failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
455
|
+
}
|
|
367
456
|
}
|
package/dist/esm/types/errors.js
CHANGED
|
@@ -120,3 +120,47 @@ export class NetworkError extends MinifetchError {
|
|
|
120
120
|
Object.setPrototypeOf(this, NetworkError.prototype);
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Thrown when keyword-search query validation fails (empty, or over 50 chars).
|
|
125
|
+
* The query sibling of {@link InvalidUrlError} — searchByKeyword takes a query,
|
|
126
|
+
* not a URL, so bad input surfaces here instead.
|
|
127
|
+
*/
|
|
128
|
+
export class InvalidQueryError extends MinifetchError {
|
|
129
|
+
query;
|
|
130
|
+
/**
|
|
131
|
+
*
|
|
132
|
+
* @param query
|
|
133
|
+
* @param message
|
|
134
|
+
*/
|
|
135
|
+
constructor(query, message) {
|
|
136
|
+
super(message || `Invalid query: ${query}`);
|
|
137
|
+
this.name = "InvalidQueryError";
|
|
138
|
+
this.query = query;
|
|
139
|
+
Object.setPrototypeOf(this, InvalidQueryError.prototype);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Thrown when a keyword search fails (non-OK response or unexpected error).
|
|
144
|
+
* The search sibling of {@link ExtractionFailedError}: it carries the `query`
|
|
145
|
+
* rather than a `url`, since search has no target URL.
|
|
146
|
+
*/
|
|
147
|
+
export class SearchFailedError extends MinifetchError {
|
|
148
|
+
query;
|
|
149
|
+
statusCode;
|
|
150
|
+
originalError;
|
|
151
|
+
/**
|
|
152
|
+
*
|
|
153
|
+
* @param query
|
|
154
|
+
* @param message
|
|
155
|
+
* @param statusCode
|
|
156
|
+
* @param originalError
|
|
157
|
+
*/
|
|
158
|
+
constructor(query, message, statusCode, originalError) {
|
|
159
|
+
super(message);
|
|
160
|
+
this.name = "SearchFailedError";
|
|
161
|
+
this.query = query;
|
|
162
|
+
this.statusCode = statusCode;
|
|
163
|
+
this.originalError = originalError;
|
|
164
|
+
Object.setPrototypeOf(this, SearchFailedError.prototype);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
import { InvalidUrlError } from "../types/errors.js";
|
|
1
|
+
import { InvalidUrlError, InvalidQueryError } from "../types/errors.js";
|
|
2
2
|
/**
|
|
3
3
|
* Maximum allowed URL length
|
|
4
4
|
*/
|
|
5
5
|
const MAX_URL_LENGTH = 2048;
|
|
6
|
+
/**
|
|
7
|
+
* Maximum allowed search-query length. Mirrors Ceramic's hard cap on the server
|
|
8
|
+
* so we fail fast client-side instead of spending a paid call on a 400.
|
|
9
|
+
*/
|
|
10
|
+
const MAX_QUERY_LENGTH = 50;
|
|
6
11
|
/**
|
|
7
12
|
* Allowed URL protocols
|
|
8
13
|
*/
|
|
@@ -77,6 +82,27 @@ export function validateAndNormalizeUrl(url) {
|
|
|
77
82
|
}
|
|
78
83
|
return normalized;
|
|
79
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Validate and normalize a keyword-search query.
|
|
87
|
+
* Mirrors the server: strips null bytes, normalizes unicode, trims, then enforces
|
|
88
|
+
* the same non-empty / 50-char rules so bad input fails before a paid call.
|
|
89
|
+
*
|
|
90
|
+
* @param query
|
|
91
|
+
* @throws {InvalidQueryError} if the query is empty or exceeds 50 characters
|
|
92
|
+
*/
|
|
93
|
+
export function validateAndNormalizeQuery(query) {
|
|
94
|
+
if (!query || typeof query !== "string") {
|
|
95
|
+
throw new InvalidQueryError(String(query ?? ""), "Query must be a non-empty string");
|
|
96
|
+
}
|
|
97
|
+
const cleaned = query.replace(/\0/g, "").normalize("NFC").trim();
|
|
98
|
+
if (!cleaned) {
|
|
99
|
+
throw new InvalidQueryError(query, "Query must not be empty");
|
|
100
|
+
}
|
|
101
|
+
if (cleaned.length > MAX_QUERY_LENGTH) {
|
|
102
|
+
throw new InvalidQueryError(cleaned, `Query exceeds maximum length of ${MAX_QUERY_LENGTH} characters`);
|
|
103
|
+
}
|
|
104
|
+
return cleaned;
|
|
105
|
+
}
|
|
80
106
|
/**
|
|
81
107
|
* Check if hostname is localhost or private IP
|
|
82
108
|
*
|
package/dist/types/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ClientConfig, HttpMethod } from "./types/config.js";
|
|
2
|
-
import type { PreflightCheckResponse, PaidEndpointResponse } from "./types/responses.js";
|
|
2
|
+
import type { PreflightCheckResponse, PaidEndpointResponse, SearchKeywordResponse } from "./types/responses.js";
|
|
3
3
|
/**
|
|
4
4
|
* Main Minifetch API client.
|
|
5
5
|
* Supports two auth modes:
|
|
@@ -17,6 +17,30 @@ export declare class MinifetchClient {
|
|
|
17
17
|
* @param config - Either { network, privateKey } for x402 or { apiKey } for API key auth
|
|
18
18
|
*/
|
|
19
19
|
constructor(config: ClientConfig);
|
|
20
|
+
/**
|
|
21
|
+
* Search the web by keyword (paid endpoint).
|
|
22
|
+
*
|
|
23
|
+
* Unlike the URL-based methods, this takes a search query rather than a URL and
|
|
24
|
+
* proxies to Minifetch's keyword search. Returns ranked results, each with a
|
|
25
|
+
* title, URL, and text snippet. `limit` and `descriptionLength` are convenience
|
|
26
|
+
* knobs the server clamps into range; the effective values (after clamping) are
|
|
27
|
+
* echoed back on `queryParameters`.
|
|
28
|
+
*
|
|
29
|
+
* @param query - keyword(s) to search for (1-50 characters)
|
|
30
|
+
* @param options
|
|
31
|
+
* @param options.limit - number of results, 1-10 (default 10). Out-of-range values are clamped.
|
|
32
|
+
* @param options.descriptionLength - max characters per snippet, 0-5000 (default 750, 0 = titles/URLs only). Out-of-range values are clamped.
|
|
33
|
+
* @param options.method - "GET" or "POST" (default POST)
|
|
34
|
+
* @throws {InvalidQueryError} if the query is empty or exceeds 50 characters
|
|
35
|
+
* @throws {SearchFailedError} if the search request fails
|
|
36
|
+
* @throws {PaymentFailedError} if x402 payment fails
|
|
37
|
+
* @throws {NetworkError} various reasons, check README
|
|
38
|
+
*/
|
|
39
|
+
searchByKeyword(query: string, options?: {
|
|
40
|
+
limit?: number;
|
|
41
|
+
descriptionLength?: number;
|
|
42
|
+
method?: HttpMethod;
|
|
43
|
+
}): Promise<SearchKeywordResponse>;
|
|
20
44
|
/**
|
|
21
45
|
* Check if URL is allowed by robots.txt (free preflight check — no auth required)
|
|
22
46
|
*
|
|
@@ -51,20 +75,6 @@ export declare class MinifetchClient {
|
|
|
51
75
|
fresh?: boolean;
|
|
52
76
|
method?: HttpMethod;
|
|
53
77
|
}): Promise<PaidEndpointResponse>;
|
|
54
|
-
/**
|
|
55
|
-
* Run SEO page audit (paid endpoint)
|
|
56
|
-
*
|
|
57
|
-
* @param url
|
|
58
|
-
* @param options
|
|
59
|
-
* @param options.method - "GET" or "POST" (default POST)
|
|
60
|
-
* @throws {InvalidUrlError} if URL is invalid
|
|
61
|
-
* @throws {ExtractionFailedError} various reasons, check README
|
|
62
|
-
* @throws {PaymentFailedError} if x402 payment fails
|
|
63
|
-
* @throws {NetworkError} various reasons, check README
|
|
64
|
-
*/
|
|
65
|
-
runSeoPageAudit(url: string, options?: {
|
|
66
|
-
method?: HttpMethod;
|
|
67
|
-
}): Promise<PaidEndpointResponse>;
|
|
68
78
|
/**
|
|
69
79
|
* Extract URL metadata (paid endpoint)
|
|
70
80
|
*
|
|
@@ -129,6 +139,20 @@ export declare class MinifetchClient {
|
|
|
129
139
|
includeMediaUrls?: boolean;
|
|
130
140
|
method?: HttpMethod;
|
|
131
141
|
}): Promise<PaidEndpointResponse>;
|
|
142
|
+
/**
|
|
143
|
+
* Run SEO page audit (paid endpoint)
|
|
144
|
+
*
|
|
145
|
+
* @param url
|
|
146
|
+
* @param options
|
|
147
|
+
* @param options.method - "GET" or "POST" (default POST)
|
|
148
|
+
* @throws {InvalidUrlError} if URL is invalid
|
|
149
|
+
* @throws {ExtractionFailedError} various reasons, check README
|
|
150
|
+
* @throws {PaymentFailedError} if x402 payment fails
|
|
151
|
+
* @throws {NetworkError} various reasons, check README
|
|
152
|
+
*/
|
|
153
|
+
runSeoPageAudit(url: string, options?: {
|
|
154
|
+
method?: HttpMethod;
|
|
155
|
+
}): Promise<PaidEndpointResponse>;
|
|
132
156
|
/**
|
|
133
157
|
* Check URL then run SEO page audit in one call.
|
|
134
158
|
* Throws RobotsBlockedError if robots.txt blocks the URL.
|
|
@@ -223,6 +247,16 @@ export declare class MinifetchClient {
|
|
|
223
247
|
* @param method - "GET" or "POST" (default POST)
|
|
224
248
|
*/
|
|
225
249
|
private _makeRequest;
|
|
250
|
+
/**
|
|
251
|
+
* Search-specific request path. Mirrors {@link _makeRequest} but for the
|
|
252
|
+
* keyword search endpoint: there is no URL, non-OK responses surface as
|
|
253
|
+
* SearchFailedError (carrying the query), and the server's clamped
|
|
254
|
+
* `queryParameters` echo is preserved rather than dropped.
|
|
255
|
+
*
|
|
256
|
+
* @param params - request params (query + optional limit/descriptionLength)
|
|
257
|
+
* @param method - "GET" or "POST" (default POST)
|
|
258
|
+
*/
|
|
259
|
+
private _makeSearchRequest;
|
|
226
260
|
/**
|
|
227
261
|
* Preflight check helper — throws RobotsBlockedError if not allowed
|
|
228
262
|
*
|
|
@@ -237,4 +271,13 @@ export declare class MinifetchClient {
|
|
|
237
271
|
* @param label
|
|
238
272
|
*/
|
|
239
273
|
private _rethrowError;
|
|
274
|
+
/**
|
|
275
|
+
* Search sibling of {@link _rethrowError}: re-throw known search error types,
|
|
276
|
+
* wrapping anything else in SearchFailedError (which carries the query, not a URL).
|
|
277
|
+
*
|
|
278
|
+
* @param error
|
|
279
|
+
* @param query
|
|
280
|
+
* @param label
|
|
281
|
+
*/
|
|
282
|
+
private _rethrowSearchError;
|
|
240
283
|
}
|
|
@@ -84,3 +84,35 @@ export declare class NetworkError extends MinifetchError {
|
|
|
84
84
|
*/
|
|
85
85
|
constructor(message: string, originalError?: Error);
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Thrown when keyword-search query validation fails (empty, or over 50 chars).
|
|
89
|
+
* The query sibling of {@link InvalidUrlError} — searchByKeyword takes a query,
|
|
90
|
+
* not a URL, so bad input surfaces here instead.
|
|
91
|
+
*/
|
|
92
|
+
export declare class InvalidQueryError extends MinifetchError {
|
|
93
|
+
readonly query: string;
|
|
94
|
+
/**
|
|
95
|
+
*
|
|
96
|
+
* @param query
|
|
97
|
+
* @param message
|
|
98
|
+
*/
|
|
99
|
+
constructor(query: string, message?: string);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Thrown when a keyword search fails (non-OK response or unexpected error).
|
|
103
|
+
* The search sibling of {@link ExtractionFailedError}: it carries the `query`
|
|
104
|
+
* rather than a `url`, since search has no target URL.
|
|
105
|
+
*/
|
|
106
|
+
export declare class SearchFailedError extends MinifetchError {
|
|
107
|
+
readonly query: string;
|
|
108
|
+
readonly statusCode?: number;
|
|
109
|
+
readonly originalError?: Error;
|
|
110
|
+
/**
|
|
111
|
+
*
|
|
112
|
+
* @param query
|
|
113
|
+
* @param message
|
|
114
|
+
* @param statusCode
|
|
115
|
+
* @param originalError
|
|
116
|
+
*/
|
|
117
|
+
constructor(query: string, message: string, statusCode?: number, originalError?: Error);
|
|
118
|
+
}
|
|
@@ -48,3 +48,38 @@ export interface PaymentInfo {
|
|
|
48
48
|
/** Link to view transaction on block explorer **/
|
|
49
49
|
explorerLink: string;
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* A single keyword-search result.
|
|
53
|
+
*/
|
|
54
|
+
export interface SearchKeywordResult {
|
|
55
|
+
/** Title of the result page */
|
|
56
|
+
title: string;
|
|
57
|
+
/** URL of the result page */
|
|
58
|
+
url: string;
|
|
59
|
+
/** Text snippet from the result page, trimmed to the requested descriptionLength */
|
|
60
|
+
description: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Response from the keyword search endpoint.
|
|
64
|
+
*
|
|
65
|
+
* Unlike PaidEndpointResponse, this preserves `queryParameters` — the effective
|
|
66
|
+
* request params after the server clamps `limit`/`descriptionLength` into range.
|
|
67
|
+
* That echo is how a caller learns what actually ran (no black box).
|
|
68
|
+
*/
|
|
69
|
+
export interface SearchKeywordResponse {
|
|
70
|
+
/** Minifetch API success (200, ok) */
|
|
71
|
+
success: boolean;
|
|
72
|
+
/** Effective params after server-side clamping. */
|
|
73
|
+
queryParameters: {
|
|
74
|
+
query: string;
|
|
75
|
+
limit: number;
|
|
76
|
+
descriptionLength: number;
|
|
77
|
+
};
|
|
78
|
+
/** Ranked search results. */
|
|
79
|
+
results: Array<{
|
|
80
|
+
data: SearchKeywordResult;
|
|
81
|
+
error?: Record<string, any>;
|
|
82
|
+
}>;
|
|
83
|
+
/** Payment information — only present for paid x402 requests. */
|
|
84
|
+
payment?: PaymentInfo;
|
|
85
|
+
}
|
|
@@ -6,3 +6,12 @@
|
|
|
6
6
|
* @throws {InvalidUrlError} if URL is invalid
|
|
7
7
|
*/
|
|
8
8
|
export declare function validateAndNormalizeUrl(url: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Validate and normalize a keyword-search query.
|
|
11
|
+
* Mirrors the server: strips null bytes, normalizes unicode, trims, then enforces
|
|
12
|
+
* the same non-empty / 50-char rules so bad input fails before a paid call.
|
|
13
|
+
*
|
|
14
|
+
* @param query
|
|
15
|
+
* @throws {InvalidQueryError} if the query is empty or exceeds 50 characters
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateAndNormalizeQuery(query: string): string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "minifetch-api",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Minifetch.com API Client.
|
|
3
|
+
"version": "1.7.1",
|
|
4
|
+
"description": "Minifetch.com API Client. Search, scrape, extract and SEO audit web pages. Pay per fetch, no subscription.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/esm/index.js",
|
|
7
7
|
"types": "./dist/types/index.d.ts",
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
"metadata extraction",
|
|
26
26
|
"content extraction",
|
|
27
27
|
"link extraction",
|
|
28
|
+
"search",
|
|
29
|
+
"keyword search",
|
|
28
30
|
"hosted",
|
|
29
31
|
"meta tags",
|
|
30
32
|
"open graph",
|