minifetch-api 1.3.4 → 1.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
@@ -5,19 +5,17 @@
5
5
  </a>
6
6
  </div>
7
7
 
8
- **[Minifetch](https://minifetch.com) is a hosted toolkit of extraction primitives for SEO/ GEO/ AEO.** Run them as a full technical audit or call one at a time for a fraction of the price — and a fraction of the LLM tokens. No subscription.
8
+ **[Minifetch](https://minifetch.com) is a hosted toolkit of web page extraction primitives.** Run them as a full technical SEO audit or call one at a time for a fraction of the price — and a fraction of the LLM tokens. 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. 🎉🎉
12
12
  - ⛔ No charge for blocked pages (403 errors).
13
13
 
14
14
  ---
15
+
15
16
  **👉 [Full API docs](https://minifetch.com/docs/api) | [LLMs.txt](https://minifetch.com/llms.txt) and [SKILL.md](https://minifetch.com/SKILL.md) for agents | Questions? Join our [Discord](https://discord.gg/EM6ET8Dshm)**
16
17
 
17
18
  ---
18
- **Payments.** Two ways to pay:
19
- 1. Credit card + API key. Get started free - [visit our dashboard to Sign Up](https://minifetch.com/dashboard). Create a Minifetch account and it will be auto-loaded with 25 free technical SEO page audits. Top up with your credit card later.
20
- 2. USDC on Base or Solana. Just load your wallet with USDC, you're ready. No "gas token" (ETH or SOL) required. You don't need a Minifetch account either, just load your wallet and go!
21
19
 
22
20
  ## Prerequisites
23
21
 
@@ -25,9 +23,15 @@
25
23
  - A valid API key from our [dashboard](https://minifetch.com/dashboard)
26
24
  - ***Or*** an Ethereum or Solana private key for making USDC payments on Base or Solana networks.
27
25
 
28
- ## Install
26
+ **Payments.** Two ways to pay:
27
+ 1. Credit card + API key. Get started free - [visit our dashboard to Sign Up](https://minifetch.com/dashboard). Create a Minifetch account and it will be auto-loaded with 25 free technical SEO page audits. Top up with your credit card later.
28
+ 2. USDC on Base or Solana. Just load your wallet with USDC, you're ready. No "gas token" (ETH or SOL) required. You don't need a Minifetch account either, just load your wallet and go!
29
29
 
30
- `npm install minifetch-api --save`
30
+ ## Install
31
+ Add to your project:
32
+ ```
33
+ npm install minifetch-api --save`
34
+ ```
31
35
 
32
36
  ## Quick Start
33
37
 
@@ -66,9 +70,9 @@ try {
66
70
 
67
71
  After the Quick Start, you have the following methods to use.
68
72
 
69
- **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](https://github.com/Niche-Networks/minifetch-api/) /example- directories.
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/).
70
74
 
71
- 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 the URL is blocked, read [How To Unblock Minifetch](https://minifetch.com/tutorials/unblock-minifetch).
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).
72
76
 
73
77
  ```js
74
78
  await client.checkAndRunSeoPageAudit(url);
@@ -82,15 +86,15 @@ await client.checkAndRunSeoPageAudit(url);
82
86
 
83
87
  await client.checkAndExtractUrlMetadata(url, options);
84
88
  // Price: $0.002
85
- // Extracts rich structured metadata from your URL: favicons, title,
86
- // description, canonical, SEO-related meta tags, Open Graph and
87
- // Twitter card tags, JSON-LD and more.
88
- // Set verbosity option to full for more fields: redirect chain,
89
- // relevant response headers, performance metrics, headings, images
90
- // and every meta tag found on the page; verbosity "full" is the
91
- // drop-in replacement for the npm `url-metadata` package.
89
+ // Extracts rich structured metadata from your URL: redirects, favicons,
90
+ // title, description, canonical, SEO-related meta tags, Open Graph and
91
+ // Twitter card tags, JSON-LD and more. Returns everything by default;
92
+ // narrow the response with the `fields` query param (pass atomic field
93
+ // names, ex: ?fields=title,description or the named groups: network,meta,
94
+ // og,twitter and/or meta:<name> for a specific page meta tag)
92
95
  // Options:
93
- // { verbosity: "full" } - defaults to "standard"
96
+ // { fields: ['canonical', 'og:url'] } - narrows to custom selected fields
97
+ // { omitEmpty: true } - defaults to false
94
98
  // { includeResponseBody: true } - defaults to false
95
99
 
96
100
  await client.checkAndExtractUrlLinks(url);
@@ -104,9 +108,8 @@ await client.checkAndExtractUrlLinks(url);
104
108
 
105
109
  await client.checkAndExtractUrlPreview(url);
106
110
  // Price: $0.002
107
- // For checking how your web page unfurls when shared: Extracts all
108
- // fields for the lightweight cards that represent your page on social
109
- // platforms, chat apps and AI.
111
+ // Extracts all fields used for a page's share previews: the lightweight
112
+ // cards that represent the page on social platforms, chat apps, and AI.
110
113
 
111
114
  await client.checkAndExtractUrlContent(url, options);
112
115
  // Price: $0.002
@@ -114,10 +117,14 @@ await client.checkAndExtractUrlContent(url, options);
114
117
  // an LLM extracts from your page after nav, ads, & scripts are stripped.
115
118
  // See what survives for AEO and AI search; respects robots.txt.
116
119
  // Options: { includeMediaUrls: true } - defaults to false.
120
+ ```
121
+
122
+ For max control, you can also use the following methods directly:
123
+ ```js
124
+ await client.preflightCheck(url, options);
125
+ // Free: check if `minfetch` user agent can access target via robots.txt
126
+ // Options: { "fresh": true } - bypass 24hr robots.txt cache, defaults to false
117
127
 
118
- // For max control, you can also use the following methods directly.
119
- // Free: check if `minfetch` user agent can access target via robots.txt:
120
- await client.preflightCheck(url);
121
128
  // Paid methods:
122
129
  await client.runSeoPageAudit(url);
123
130
  await client.extractUrlMetadata(url, options); // same options as above
@@ -126,9 +133,12 @@ await client.extractUrlPreview(url);
126
133
  await client.extractUrlContent(url, options); // same options as above
127
134
  ```
128
135
  ---
136
+
129
137
  ### Error Types
130
- When you wrap the functions above in a try/catch, here are the errors you may encounter. You are never charged for URLs that are blocked or error.
138
+ 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.
131
139
 
140
+ - **"InvalidURLError: Invalid url ${url}"**
141
+ - The URL is malformed in some way, correct it and try again.
132
142
  - **"RobotsBlockedError: URL is blocked by robots.txt"**
133
143
  - Minifetch is explicitly blocked by the website's `robots.txt`, cannot be fetched. If this is your site, read our tutorial [How To Unblock Minifetch](https://minifetch.com/tutorials/unblock-minifetch)
134
144
  - **"Network Error: 402 Payment Required"**
@@ -139,9 +149,9 @@ When you wrap the functions above in a try/catch, here are the errors you may en
139
149
  - URLs that pass their robots.txt check but are blocked anyway via 403 or other tactics may error like this. No charge.
140
150
  - **"503 Service Temporarily Unavailable"**
141
151
  - Likely encountering upstream timeout errors on the target URL.
142
- - **"InvalidURLError: Invalid url ${url}"**
143
- - The URL is malformed in some way, correct it and try again.
152
+
144
153
  ---
154
+
145
155
  ### Service Limitations
146
156
  Minifetch only extracts publicly available metadata and content from pages accessible without authentication and javascript execution.
147
157
 
@@ -156,6 +166,7 @@ What Minifetch does NOT do *currently* but may offer in the future as an add-on:
156
166
  - Access authenticated or logged-in content
157
167
 
158
168
  ---
169
+
159
170
  ### USDC Best Practices
160
171
  - Uses the [x402 Protocol](https://www.x402.org/). No "gas token" (ETH or SOL) required, just load your wallet with USDC on Base or Solana network.
161
172
  - LLMs & Agents should *never* have direct access to your private key. They *will* expose it!
@@ -164,6 +175,7 @@ What Minifetch does NOT do *currently* but may offer in the future as an add-on:
164
175
  - Pass your private key into the Minifetch API Client with an [environment variable](https://developer.vonage.com/en/blog/how-to-use-environment-variables-in-javascript-with-dotenv).
165
176
 
166
177
  ---
178
+
167
179
  **License**
168
180
 
169
181
  MIT / Copyright (c) 2026 Lauren Garcia
@@ -22,13 +22,18 @@ export class MinifetchClient {
22
22
  * Check if URL is allowed by robots.txt (free preflight check — no auth required)
23
23
  *
24
24
  * @param url
25
+ * @param options
26
+ * @param options.fresh
25
27
  * @throws {InvalidUrlError} if URL is invalid
26
28
  * @throws {NetworkError} if request fails
27
29
  */
28
- async preflightUrlCheck(url) {
30
+ async preflightUrlCheck(url, options) {
29
31
  try {
30
32
  const normalizedUrl = validateAndNormalizeUrl(url);
31
- const requestUrl = `${this.baseUrl}/api/v1/free/preflight/url-check?url=${encodeURIComponent(normalizedUrl)}`;
33
+ const params = new URLSearchParams({ url: normalizedUrl });
34
+ if (options?.fresh)
35
+ params.set("fresh", "true");
36
+ const requestUrl = `${this.baseUrl}/api/v1/free/preflight/url-check?${params.toString()}`;
32
37
  const response = await fetch(requestUrl);
33
38
  if (!response.ok) {
34
39
  throw new NetworkError(`Preflight check failed: ${response.status} ${response.statusText}`);
@@ -67,7 +72,8 @@ export class MinifetchClient {
67
72
  *
68
73
  * @param url
69
74
  * @param options
70
- * @param options.verbosity - "standard" (default) or "full"
75
+ * @param options.fields
76
+ * @param options.omitEmpty
71
77
  * @param options.includeResponseBody
72
78
  * @throws {InvalidUrlError} if URL is invalid
73
79
  * @throws {ExtractionFailedError} various reasons, check README
@@ -78,8 +84,10 @@ export class MinifetchClient {
78
84
  try {
79
85
  const normalizedUrl = validateAndNormalizeUrl(url);
80
86
  const params = new URLSearchParams({ url: normalizedUrl });
81
- if (options?.verbosity)
82
- params.set("verbosity", options.verbosity);
87
+ if (options?.fields?.length)
88
+ params.set("fields", options.fields.join(","));
89
+ if (options?.omitEmpty)
90
+ params.set("omitEmpty", "true");
83
91
  if (options?.includeResponseBody)
84
92
  params.set("includeResponseBody", "true");
85
93
  const requestUrl = `${this.baseUrl}${this._paidPath("/extract/url-metadata")}?${params.toString()}`;
@@ -167,7 +175,8 @@ export class MinifetchClient {
167
175
  *
168
176
  * @param url
169
177
  * @param options
170
- * @param options.verbosity - "standard" (default) or "full"
178
+ * @param options.fields
179
+ * @param options.omitEmpty
171
180
  * @param options.includeResponseBody
172
181
  */
173
182
  async checkAndExtractUrlMetadata(url, options) {
@@ -217,9 +226,7 @@ export class MinifetchClient {
217
226
  * @param endpoint
218
227
  */
219
228
  _paidPath(endpoint) {
220
- return this.config.authMode === "x402"
221
- ? `/api/v1/x402${endpoint}`
222
- : `/api/v1${endpoint}`;
229
+ return this.config.authMode === "x402" ? `/api/v1/x402${endpoint}` : `/api/v1${endpoint}`;
223
230
  }
224
231
  /**
225
232
  * Dispatch to the correct request handler based on auth mode, then
@@ -29,7 +29,7 @@ export async function handlePayment(url, config) {
29
29
  }
30
30
  else if (isSolana) {
31
31
  if (!config.privateKey)
32
- throw new PaymentFailedError('privateKey is required for Solana payments');
32
+ throw new PaymentFailedError("privateKey is required for Solana payments");
33
33
  const privateKeyBytes = bs58.decode(config.privateKey);
34
34
  const signer = await createKeyPairSignerFromBytes(privateKeyBytes);
35
35
  const svmSigner = signer;
@@ -42,7 +42,8 @@ export async function handlePayment(url, config) {
42
42
  const fetchWithPayment = wrapFetchWithPayment(fetch, _x402Client);
43
43
  const response = await fetchWithPayment(url, { method: "GET" });
44
44
  if (!response.ok) {
45
- throw new NetworkError(`Request failed: ${response.status} ${response.statusText}`);
45
+ const serverMessage = await readServerErrorMessage(response);
46
+ throw new NetworkError(`Request failed: ${response.status} ${response.statusText}${serverMessage ? ` — ${serverMessage}` : ""}`);
46
47
  }
47
48
  const httpClient = new x402HTTPClient(_x402Client);
48
49
  const paymentResponse = httpClient.getPaymentSettleResponse(name => response.headers.get(name));
@@ -79,7 +80,8 @@ export async function handleApiKeyRequest(url, config) {
79
80
  },
80
81
  });
81
82
  if (!response.ok) {
82
- throw new NetworkError(`Request failed: ${response.status} ${response.statusText}`);
83
+ const serverMessage = await readServerErrorMessage(response);
84
+ throw new NetworkError(`Request failed: ${response.status} ${response.statusText}${serverMessage ? ` — ${serverMessage}` : ""}`);
83
85
  }
84
86
  return { response };
85
87
  }
@@ -101,3 +103,19 @@ function getExplorerLink(config, txHash) {
101
103
  return "";
102
104
  }
103
105
  }
106
+ /**
107
+ * Best-effort read of the rich server error message from a non-ok response.
108
+ * Minifetch error bodies carry it at results[0].error.message.
109
+ *
110
+ * @param response - non-ok Response (body is consumed)
111
+ */
112
+ async function readServerErrorMessage(response) {
113
+ try {
114
+ const body = (await response.json());
115
+ // TODO: revisit this approach when we scale up to multiple results per request
116
+ return body?.results?.[0]?.error?.message;
117
+ }
118
+ catch {
119
+ return undefined;
120
+ }
121
+ }
@@ -17,10 +17,14 @@ export declare class MinifetchClient {
17
17
  * Check if URL is allowed by robots.txt (free preflight check — no auth required)
18
18
  *
19
19
  * @param url
20
+ * @param options
21
+ * @param options.fresh
20
22
  * @throws {InvalidUrlError} if URL is invalid
21
23
  * @throws {NetworkError} if request fails
22
24
  */
23
- preflightUrlCheck(url: string): Promise<PreflightCheckResponse>;
25
+ preflightUrlCheck(url: string, options?: {
26
+ fresh?: boolean;
27
+ }): Promise<PreflightCheckResponse>;
24
28
  /**
25
29
  * Run SEO page audit (paid endpoint)
26
30
  *
@@ -36,7 +40,8 @@ export declare class MinifetchClient {
36
40
  *
37
41
  * @param url
38
42
  * @param options
39
- * @param options.verbosity - "standard" (default) or "full"
43
+ * @param options.fields
44
+ * @param options.omitEmpty
40
45
  * @param options.includeResponseBody
41
46
  * @throws {InvalidUrlError} if URL is invalid
42
47
  * @throws {ExtractionFailedError} various reasons, check README
@@ -44,7 +49,8 @@ export declare class MinifetchClient {
44
49
  * @throws {NetworkError} various reasons, check README
45
50
  */
46
51
  extractUrlMetadata(url: string, options?: {
47
- verbosity?: "standard" | "full";
52
+ fields?: string[];
53
+ omitEmpty?: boolean;
48
54
  includeResponseBody?: boolean;
49
55
  }): Promise<PaidEndpointResponse>;
50
56
  /**
@@ -94,11 +100,13 @@ export declare class MinifetchClient {
94
100
  *
95
101
  * @param url
96
102
  * @param options
97
- * @param options.verbosity - "standard" (default) or "full"
103
+ * @param options.fields
104
+ * @param options.omitEmpty
98
105
  * @param options.includeResponseBody
99
106
  */
100
107
  checkAndExtractUrlMetadata(url: string, options?: {
101
- verbosity?: "standard" | "full";
108
+ fields?: string[];
109
+ omitEmpty?: boolean;
102
110
  includeResponseBody?: boolean;
103
111
  }): Promise<PaidEndpointResponse>;
104
112
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minifetch-api",
3
- "version": "1.3.4",
3
+ "version": "1.5.0",
4
4
  "description": "Minifetch.com API Client. Pay-per-URL SEO audits. Composable toolkit for AI agents and automation pipelines.",
5
5
  "type": "module",
6
6
  "main": "./dist/esm/index.js",