@scrapa/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.
@@ -14,9 +14,10 @@ export interface ScrapeParams {
14
14
  * always use "fetch", so leave it out for them. */
15
15
  engine?: "auto" | "fetch" | "browser";
16
16
  /** Whether to try again a different way when the first attempt is blocked. On by default.
17
- * `false` stops after one try. The site's refusal is your answer, and it costs nothing. It is
18
- * how you bound a call: with `auto`, trying again is what charges the browser price instead of
19
- * the fetch one. A POST is sent once, so it is off for a POST. */
17
+ * `false` stops after one try. The site's refusal is your answer, and it costs nothing, unless
18
+ * your own `browser.actions` script ran in the page first. It is how you bound a call: with
19
+ * `auto`, trying again is what charges the browser price instead of the fetch one. A POST is
20
+ * sent once, so it is off for a POST. */
20
21
  escalate?: boolean;
21
22
  /** The HTTP method. "POST" sends `body` to the URL, for a search form or a JSON query. It is
22
23
  * sent once: never retried and never sent again another way, because sending it twice could
@@ -31,15 +32,35 @@ export interface ScrapeParams {
31
32
  /** How many extra tries to give a page the site blocks, each from a different address. 0 (the
32
33
  * default) takes the site's first no as the answer. A blocked page costs nothing, so a try
33
34
  * that is blocked again is free, but it takes as long as the first one did, so the call can
34
- * run that many times longer. */
35
+ * run that many times longer. Once your `browser.actions` steps click, type, press a key or
36
+ * run a script on a page, it is not tried again, so nothing is done twice. If one of your
37
+ * scripts ran in it, the block is charged. */
35
38
  retry?: number;
36
39
  /** What to hand back. "screenshot" and "pdf" come back as base64 and need the browser engine;
37
40
  * asking for both costs no more than one. "extract" is the page read into fields by a model,
38
- * and needs an `extract` block saying which ones. "raw" is the file exactly as the site sent
39
- * it, as base64 with its content type: a PDF report, an image, a spreadsheet. It uses the
40
- * fetch engine and holds up to 5 MB; a larger file answers FILE_TOO_LARGE at no charge, never
41
- * cut short. */
41
+ * and needs an `extract` block saying which ones. A link to a PDF or a Word (.docx) file is
42
+ * read into text, so the text formats work on it too. We read files up to 25 MB, and from a
43
+ * PDF the first 200 pages and the first 5 million characters. Reading a file costs what the
44
+ * engine costs: 1 credit with fetch, 5 with the browser. A Word file that holds more than 32
45
+ * MB once unzipped answers FILE_TOO_LARGE, and a file link that answers a short web page
46
+ * instead of the file answers UPSTREAM_BLOCKED, both at no charge. A dead link is the
47
+ * exception: its 404 or 410 page is the site's answer, and comes back with that status at the
48
+ * fetch price. "raw" is the file exactly as the site sent it, as base64 with its content type:
49
+ * a PDF report, an image, a spreadsheet. It uses the fetch engine and holds up to 5 MB; a
50
+ * larger file answers FILE_TOO_LARGE at no charge, never cut short. */
42
51
  formats?: ("markdown" | "html" | "text" | "links" | "screenshot" | "pdf" | "extract" | "raw")[];
52
+ /** Read only the page's main content into `markdown`, `text` and `extract`: the article or the
53
+ * product, without the menus, ads and related links around it. On by default. Set `false` for
54
+ * the whole page. When the main content is under 50 words, you get the whole page instead.
55
+ * `html` and `links` always hold the whole page. */
56
+ onlyMainContent?: boolean;
57
+ /** CSS selectors for the parts of the page to keep, such as `["h1", ".price"]`. `markdown`,
58
+ * `text` and `extract` then hold only what they match, in page order, and `onlyMainContent` no
59
+ * longer applies. Hidden parts are never kept. Up to 20 selectors, 256 characters each. */
60
+ includeTags?: string[];
61
+ /** CSS selectors for the parts of the page to leave out of `markdown`, `text` and `extract`,
62
+ * such as `[".comments", "#newsletter"]`. Up to 20 selectors, 256 characters each. */
63
+ excludeTags?: string[];
43
64
  /** What the page should answer with. Set `"json"` when you are reading an API: anything else
44
65
  * that comes back (a maintenance notice, a "checking your browser" page, a login form) is then
45
66
  * not your answer. It costs nothing, and we try again from another address instead of handing
@@ -68,8 +89,10 @@ export interface ScrapeParams {
68
89
  server?: string;
69
90
  /** Where the call should look like it comes from, ISO 3166-1 alpha-2 (`us`, `br`). Name one
70
91
  * and the managed pool leaves from that country. Leave it out and the page sees a US
71
- * browser, from an address we pick. The browser's clock and languages follow the country
72
- * on every pool. */
92
+ * browser, from an address we pick. A scrape or a map on the managed pool makes one
93
+ * exception: a site whose address ends in `.br`, `.de`, `.es`, `.fr`, `.it`, `.jp`, `.nl`,
94
+ * `.pl` or `.uk` is read from its own country. The browser's clock and languages follow
95
+ * the country on every pool. */
73
96
  country?: string;
74
97
  /** A state or region inside `country`, by name: `california`, `são paulo`. Managed pool
75
98
  * only. Spell the name out, since an abbreviation like `sp` finds nobody. In the US and
@@ -82,7 +105,7 @@ export interface ScrapeParams {
82
105
  city?: string;
83
106
  };
84
107
  /** How to render it. Only the browser engine reads these. With `auto` they apply if the page
85
- * turns out to need one. */
108
+ * turns out to need one, except `actions`, which always use the browser. */
86
109
  browser?: {
87
110
  /** When the page counts as loaded. "load" (the default) waits for images and stylesheets;
88
111
  * "domcontentloaded" is the fastest and stops at the HTML; "networkidle" waits until the
@@ -105,13 +128,89 @@ export interface ScrapeParams {
105
128
  /** Screenshot the whole scrollable page instead of just the window. A long page makes a
106
129
  * large answer: the image comes back as base64 inside the response. */
107
130
  fullPage?: boolean;
131
+ /** Steps to run in the page before it is read, in the order you list them. A step can wait,
132
+ * click, type, press a key, scroll, take a screenshot, keep the HTML or run a script.
133
+ * Steps always use the browser engine, at the browser price. The formats you ask for read
134
+ * the page as the last step left it. Up to 50 steps, at most 10 of them screenshots or
135
+ * scrapes. If one fails, the call answers ACTION_FAILED, which names the step and why.
136
+ * That costs nothing, unless one of your scripts started, the one that failed included:
137
+ * then the call is charged. */
138
+ actions?: (
139
+ | {
140
+ type: "wait";
141
+ /** How long to wait, in milliseconds. All the waits together have to fit inside
142
+ * `timeoutMs`. */
143
+ milliseconds?: number;
144
+ /** Wait until this CSS selector is on the page. If it never shows up, the step fails. */
145
+ selector?: string;
146
+ }
147
+ | {
148
+ type: "click";
149
+ /** The CSS selector of what to click. The first match gets the click, and the step
150
+ * waits for it to show up. */
151
+ selector: string;
152
+ /** Click every visible match, not just the first. A selector that matches nothing is
153
+ * then not an error, which suits a cookie banner only some visitors see. */
154
+ all?: boolean;
155
+ }
156
+ | {
157
+ type: "write";
158
+ /** What to type, up to 1,000 characters. */
159
+ text: string;
160
+ }
161
+ | {
162
+ type: "press";
163
+ /** The key's name: "Enter", "Tab", "Escape", "ArrowDown", or two together such as
164
+ * "Control+A". */
165
+ key: string;
166
+ }
167
+ | {
168
+ type: "scroll";
169
+ direction: "up" | "down";
170
+ /** Scroll this element instead of the whole page, such as a list with its own scroll
171
+ * bar. */
172
+ selector?: string;
173
+ }
174
+ | {
175
+ type: "screenshot";
176
+ /** Take the whole scrollable page instead of just the window. */
177
+ fullPage?: boolean;
178
+ }
179
+ | {
180
+ type: "scrape";
181
+ }
182
+ | {
183
+ type: "executeJavascript";
184
+ /** The body of an async function, up to 8,000 characters, such as `return
185
+ * document.title`. It runs in the page, beside the page's own scripts. What it returns
186
+ * comes back in `actions.javascriptReturns` as JSON, or as null when JSON cannot hold
187
+ * it. All the scripts in one call can return 64 KB together. Once a script starts, the
188
+ * call is charged even if that script throws, a later step fails, the page crashes or
189
+ * times out, the site blocks it, the script leads to an address we refuse, your own
190
+ * proxy says no, or an `includeTags` or `excludeTags` selector is too slow for the
191
+ * page it built, because the script may already have read the page. A failure on our
192
+ * side (a 5xx) is still refunded. */
193
+ script: string;
194
+ }
195
+ )[];
108
196
  };
109
197
  /** Extra request headers to send to the page, such as an authorization token, a cookie or a
110
198
  * custom user agent. The connection's own headers cannot be set. */
111
199
  headers?: Record<string, string>;
112
- /** How long to give the page before giving up, in milliseconds. A page that never arrives is a
113
- * free outcome. */
200
+ /** How long to give the page before giving up, in milliseconds. A page that never arrives costs
201
+ * nothing, unless your own `browser.actions` script ran in the page first. */
114
202
  timeoutMs?: number;
203
+ /** The oldest copy of the page you accept, in milliseconds, up to 3600000 (one hour). If we
204
+ * still hold a copy of the same URL that your account read within that time, from the same
205
+ * country and with the same engine, you get it back for 0 credits and the answer says `cached:
206
+ * true`. If not, we read the page now and keep a copy for up to an hour, unless the page still
207
+ * needs JavaScript (`metadata.needsJs`). We may drop a copy sooner, to make room or on a
208
+ * deploy, and a batch keeps its own copies, apart from single calls. `0` always reads the page
209
+ * now, and keeps the copy for the calls after it. `extract` is still charged, because the
210
+ * model reads the copy again. Leave `maxAge` out to read the page fresh every time. A POST, or
211
+ * a call with `headers`, your own proxy, `browser` options, a screenshot, a PDF, `raw` or
212
+ * `escalate: false`, is always read fresh. */
213
+ maxAge?: number;
115
214
  }
116
215
 
117
216
  /** Arguments for `quote()`. */
@@ -124,9 +223,10 @@ export interface QuoteScrapeParams {
124
223
  * always use "fetch", so leave it out for them. */
125
224
  engine?: "auto" | "fetch" | "browser";
126
225
  /** Whether to try again a different way when the first attempt is blocked. On by default.
127
- * `false` stops after one try. The site's refusal is your answer, and it costs nothing. It is
128
- * how you bound a call: with `auto`, trying again is what charges the browser price instead of
129
- * the fetch one. A POST is sent once, so it is off for a POST. */
226
+ * `false` stops after one try. The site's refusal is your answer, and it costs nothing, unless
227
+ * your own `browser.actions` script ran in the page first. It is how you bound a call: with
228
+ * `auto`, trying again is what charges the browser price instead of the fetch one. A POST is
229
+ * sent once, so it is off for a POST. */
130
230
  escalate?: boolean;
131
231
  /** The HTTP method. "POST" sends `body` to the URL, for a search form or a JSON query. It is
132
232
  * sent once: never retried and never sent again another way, because sending it twice could
@@ -141,15 +241,35 @@ export interface QuoteScrapeParams {
141
241
  /** How many extra tries to give a page the site blocks, each from a different address. 0 (the
142
242
  * default) takes the site's first no as the answer. A blocked page costs nothing, so a try
143
243
  * that is blocked again is free, but it takes as long as the first one did, so the call can
144
- * run that many times longer. */
244
+ * run that many times longer. Once your `browser.actions` steps click, type, press a key or
245
+ * run a script on a page, it is not tried again, so nothing is done twice. If one of your
246
+ * scripts ran in it, the block is charged. */
145
247
  retry?: number;
146
248
  /** What to hand back. "screenshot" and "pdf" come back as base64 and need the browser engine;
147
249
  * asking for both costs no more than one. "extract" is the page read into fields by a model,
148
- * and needs an `extract` block saying which ones. "raw" is the file exactly as the site sent
149
- * it, as base64 with its content type: a PDF report, an image, a spreadsheet. It uses the
150
- * fetch engine and holds up to 5 MB; a larger file answers FILE_TOO_LARGE at no charge, never
151
- * cut short. */
250
+ * and needs an `extract` block saying which ones. A link to a PDF or a Word (.docx) file is
251
+ * read into text, so the text formats work on it too. We read files up to 25 MB, and from a
252
+ * PDF the first 200 pages and the first 5 million characters. Reading a file costs what the
253
+ * engine costs: 1 credit with fetch, 5 with the browser. A Word file that holds more than 32
254
+ * MB once unzipped answers FILE_TOO_LARGE, and a file link that answers a short web page
255
+ * instead of the file answers UPSTREAM_BLOCKED, both at no charge. A dead link is the
256
+ * exception: its 404 or 410 page is the site's answer, and comes back with that status at the
257
+ * fetch price. "raw" is the file exactly as the site sent it, as base64 with its content type:
258
+ * a PDF report, an image, a spreadsheet. It uses the fetch engine and holds up to 5 MB; a
259
+ * larger file answers FILE_TOO_LARGE at no charge, never cut short. */
152
260
  formats?: ("markdown" | "html" | "text" | "links" | "screenshot" | "pdf" | "extract" | "raw")[];
261
+ /** Read only the page's main content into `markdown`, `text` and `extract`: the article or the
262
+ * product, without the menus, ads and related links around it. On by default. Set `false` for
263
+ * the whole page. When the main content is under 50 words, you get the whole page instead.
264
+ * `html` and `links` always hold the whole page. */
265
+ onlyMainContent?: boolean;
266
+ /** CSS selectors for the parts of the page to keep, such as `["h1", ".price"]`. `markdown`,
267
+ * `text` and `extract` then hold only what they match, in page order, and `onlyMainContent` no
268
+ * longer applies. Hidden parts are never kept. Up to 20 selectors, 256 characters each. */
269
+ includeTags?: string[];
270
+ /** CSS selectors for the parts of the page to leave out of `markdown`, `text` and `extract`,
271
+ * such as `[".comments", "#newsletter"]`. Up to 20 selectors, 256 characters each. */
272
+ excludeTags?: string[];
153
273
  /** What the page should answer with. Set `"json"` when you are reading an API: anything else
154
274
  * that comes back (a maintenance notice, a "checking your browser" page, a login form) is then
155
275
  * not your answer. It costs nothing, and we try again from another address instead of handing
@@ -178,8 +298,10 @@ export interface QuoteScrapeParams {
178
298
  server?: string;
179
299
  /** Where the call should look like it comes from, ISO 3166-1 alpha-2 (`us`, `br`). Name one
180
300
  * and the managed pool leaves from that country. Leave it out and the page sees a US
181
- * browser, from an address we pick. The browser's clock and languages follow the country
182
- * on every pool. */
301
+ * browser, from an address we pick. A scrape or a map on the managed pool makes one
302
+ * exception: a site whose address ends in `.br`, `.de`, `.es`, `.fr`, `.it`, `.jp`, `.nl`,
303
+ * `.pl` or `.uk` is read from its own country. The browser's clock and languages follow
304
+ * the country on every pool. */
183
305
  country?: string;
184
306
  /** A state or region inside `country`, by name: `california`, `são paulo`. Managed pool
185
307
  * only. Spell the name out, since an abbreviation like `sp` finds nobody. In the US and
@@ -192,7 +314,7 @@ export interface QuoteScrapeParams {
192
314
  city?: string;
193
315
  };
194
316
  /** How to render it. Only the browser engine reads these. With `auto` they apply if the page
195
- * turns out to need one. */
317
+ * turns out to need one, except `actions`, which always use the browser. */
196
318
  browser?: {
197
319
  /** When the page counts as loaded. "load" (the default) waits for images and stylesheets;
198
320
  * "domcontentloaded" is the fastest and stops at the HTML; "networkidle" waits until the
@@ -215,13 +337,155 @@ export interface QuoteScrapeParams {
215
337
  /** Screenshot the whole scrollable page instead of just the window. A long page makes a
216
338
  * large answer: the image comes back as base64 inside the response. */
217
339
  fullPage?: boolean;
340
+ /** Steps to run in the page before it is read, in the order you list them. A step can wait,
341
+ * click, type, press a key, scroll, take a screenshot, keep the HTML or run a script.
342
+ * Steps always use the browser engine, at the browser price. The formats you ask for read
343
+ * the page as the last step left it. Up to 50 steps, at most 10 of them screenshots or
344
+ * scrapes. If one fails, the call answers ACTION_FAILED, which names the step and why.
345
+ * That costs nothing, unless one of your scripts started, the one that failed included:
346
+ * then the call is charged. */
347
+ actions?: (
348
+ | {
349
+ type: "wait";
350
+ /** How long to wait, in milliseconds. All the waits together have to fit inside
351
+ * `timeoutMs`. */
352
+ milliseconds?: number;
353
+ /** Wait until this CSS selector is on the page. If it never shows up, the step fails. */
354
+ selector?: string;
355
+ }
356
+ | {
357
+ type: "click";
358
+ /** The CSS selector of what to click. The first match gets the click, and the step
359
+ * waits for it to show up. */
360
+ selector: string;
361
+ /** Click every visible match, not just the first. A selector that matches nothing is
362
+ * then not an error, which suits a cookie banner only some visitors see. */
363
+ all?: boolean;
364
+ }
365
+ | {
366
+ type: "write";
367
+ /** What to type, up to 1,000 characters. */
368
+ text: string;
369
+ }
370
+ | {
371
+ type: "press";
372
+ /** The key's name: "Enter", "Tab", "Escape", "ArrowDown", or two together such as
373
+ * "Control+A". */
374
+ key: string;
375
+ }
376
+ | {
377
+ type: "scroll";
378
+ direction: "up" | "down";
379
+ /** Scroll this element instead of the whole page, such as a list with its own scroll
380
+ * bar. */
381
+ selector?: string;
382
+ }
383
+ | {
384
+ type: "screenshot";
385
+ /** Take the whole scrollable page instead of just the window. */
386
+ fullPage?: boolean;
387
+ }
388
+ | {
389
+ type: "scrape";
390
+ }
391
+ | {
392
+ type: "executeJavascript";
393
+ /** The body of an async function, up to 8,000 characters, such as `return
394
+ * document.title`. It runs in the page, beside the page's own scripts. What it returns
395
+ * comes back in `actions.javascriptReturns` as JSON, or as null when JSON cannot hold
396
+ * it. All the scripts in one call can return 64 KB together. Once a script starts, the
397
+ * call is charged even if that script throws, a later step fails, the page crashes or
398
+ * times out, the site blocks it, the script leads to an address we refuse, your own
399
+ * proxy says no, or an `includeTags` or `excludeTags` selector is too slow for the
400
+ * page it built, because the script may already have read the page. A failure on our
401
+ * side (a 5xx) is still refunded. */
402
+ script: string;
403
+ }
404
+ )[];
218
405
  };
219
406
  /** Extra request headers to send to the page, such as an authorization token, a cookie or a
220
407
  * custom user agent. The connection's own headers cannot be set. */
221
408
  headers?: Record<string, string>;
222
- /** How long to give the page before giving up, in milliseconds. A page that never arrives is a
223
- * free outcome. */
409
+ /** How long to give the page before giving up, in milliseconds. A page that never arrives costs
410
+ * nothing, unless your own `browser.actions` script ran in the page first. */
224
411
  timeoutMs?: number;
412
+ /** The oldest copy of the page you accept, in milliseconds, up to 3600000 (one hour). If we
413
+ * still hold a copy of the same URL that your account read within that time, from the same
414
+ * country and with the same engine, you get it back for 0 credits and the answer says `cached:
415
+ * true`. If not, we read the page now and keep a copy for up to an hour, unless the page still
416
+ * needs JavaScript (`metadata.needsJs`). We may drop a copy sooner, to make room or on a
417
+ * deploy, and a batch keeps its own copies, apart from single calls. `0` always reads the page
418
+ * now, and keeps the copy for the calls after it. `extract` is still charged, because the
419
+ * model reads the copy again. Leave `maxAge` out to read the page fresh every time. A POST, or
420
+ * a call with `headers`, your own proxy, `browser` options, a screenshot, a PDF, `raw` or
421
+ * `escalate: false`, is always read fresh. */
422
+ maxAge?: number;
423
+ }
424
+
425
+ /** Arguments for `map()`. */
426
+ export interface MapSiteParams {
427
+ /** The site to map, as any http(s) URL on it. Private and loopback hosts are refused. */
428
+ url: string;
429
+ /** How many URLs to hand back at most, 1 to 30,000. The price is the same whatever you ask for. */
430
+ limit?: number;
431
+ /** Only URLs that contain this text, upper or lower case alike: `blog` keeps `/blog/` and
432
+ * `/Blog-post`. */
433
+ search?: string;
434
+ /** Also keep URLs on other hosts of the same domain, like `blog.example.com` for `example.com`.
435
+ * Off by default: only the host you named, with or without `www.`. */
436
+ includeSubdomains?: boolean;
437
+ /** Where to look. "include" reads the site's sitemaps and the links on the page you named;
438
+ * "only" reads the sitemaps alone; "skip" reads the page's links alone. */
439
+ sitemap?: "include" | "only" | "skip";
440
+ /** Where the map's requests leave from. Every file it reads goes through the same exit. */
441
+ proxy?: {
442
+ /** Which exit to leave through. "residential" is the managed pool, metered by the megabyte;
443
+ * "custom" is your own proxy, free on every plan, bytes never billed. */
444
+ pool?: "residential" | "custom";
445
+ /** Your own proxy, `http(s)://[user:pass@]host:port`. Required for pool "custom", refused
446
+ * for the managed one. */
447
+ server?: string;
448
+ /** Where the call should look like it comes from, ISO 3166-1 alpha-2 (`us`, `br`). Name one
449
+ * and the managed pool leaves from that country. Leave it out and the page sees a US
450
+ * browser, from an address we pick. A scrape or a map on the managed pool makes one
451
+ * exception: a site whose address ends in `.br`, `.de`, `.es`, `.fr`, `.it`, `.jp`, `.nl`,
452
+ * `.pl` or `.uk` is read from its own country. The browser's clock and languages follow
453
+ * the country on every pool. */
454
+ country?: string;
455
+ };
456
+ }
457
+
458
+ /** Arguments for `quoteMap()`. */
459
+ export interface QuoteMapParams {
460
+ /** The site to map, as any http(s) URL on it. Private and loopback hosts are refused. */
461
+ url: string;
462
+ /** How many URLs to hand back at most, 1 to 30,000. The price is the same whatever you ask for. */
463
+ limit?: number;
464
+ /** Only URLs that contain this text, upper or lower case alike: `blog` keeps `/blog/` and
465
+ * `/Blog-post`. */
466
+ search?: string;
467
+ /** Also keep URLs on other hosts of the same domain, like `blog.example.com` for `example.com`.
468
+ * Off by default: only the host you named, with or without `www.`. */
469
+ includeSubdomains?: boolean;
470
+ /** Where to look. "include" reads the site's sitemaps and the links on the page you named;
471
+ * "only" reads the sitemaps alone; "skip" reads the page's links alone. */
472
+ sitemap?: "include" | "only" | "skip";
473
+ /** Where the map's requests leave from. Every file it reads goes through the same exit. */
474
+ proxy?: {
475
+ /** Which exit to leave through. "residential" is the managed pool, metered by the megabyte;
476
+ * "custom" is your own proxy, free on every plan, bytes never billed. */
477
+ pool?: "residential" | "custom";
478
+ /** Your own proxy, `http(s)://[user:pass@]host:port`. Required for pool "custom", refused
479
+ * for the managed one. */
480
+ server?: string;
481
+ /** Where the call should look like it comes from, ISO 3166-1 alpha-2 (`us`, `br`). Name one
482
+ * and the managed pool leaves from that country. Leave it out and the page sees a US
483
+ * browser, from an address we pick. A scrape or a map on the managed pool makes one
484
+ * exception: a site whose address ends in `.br`, `.de`, `.es`, `.fr`, `.it`, `.jp`, `.nl`,
485
+ * `.pl` or `.uk` is read from its own country. The browser's clock and languages follow
486
+ * the country on every pool. */
487
+ country?: string;
488
+ };
225
489
  }
226
490
 
227
491
  /** Arguments for `search()`. */
@@ -233,13 +497,15 @@ export interface SearchParams {
233
497
  * `includeDomains` filters what it returns. */
234
498
  limit?: number;
235
499
  /** What kind of results. "general" is the web; "news" is dated journalism, and the only topic
236
- * that reliably carries `publishedAt`. */
237
- topic?: "general" | "news";
500
+ * that reliably carries `publishedAt`; "images" is pictures: each result is the page that
501
+ * shows one, with the picture's own address, a thumbnail and its size in `image`. Pictures the
502
+ * engine marks as adult are left out. */
503
+ topic?: "general" | "news" | "images";
238
504
  /** Only pages from the last day, week, month or year. Absent is any time. */
239
505
  timeRange?: "day" | "week" | "month" | "year";
240
506
  /** Only results on these hosts, bare (`linkedin.com`). Subdomains are included. Asked of the
241
507
  * search front AND enforced on the answer, so what comes back is on your list whatever the
242
- * front did with it. */
508
+ * front did with it. With `topic: "images"` it takes one host. */
243
509
  includeDomains?: string[];
244
510
  /** Also write a short paragraph answering the query from the results, in the query's language.
245
511
  * Costs one extra credit; leave it off when your own model reads the snippets. */
@@ -255,8 +521,10 @@ export interface SearchParams {
255
521
  server?: string;
256
522
  /** Where the call should look like it comes from, ISO 3166-1 alpha-2 (`us`, `br`). Name one
257
523
  * and the managed pool leaves from that country. Leave it out and the page sees a US
258
- * browser, from an address we pick. The browser's clock and languages follow the country
259
- * on every pool. */
524
+ * browser, from an address we pick. A scrape or a map on the managed pool makes one
525
+ * exception: a site whose address ends in `.br`, `.de`, `.es`, `.fr`, `.it`, `.jp`, `.nl`,
526
+ * `.pl` or `.uk` is read from its own country. The browser's clock and languages follow
527
+ * the country on every pool. */
260
528
  country?: string;
261
529
  };
262
530
  }
@@ -269,22 +537,43 @@ export interface CreateBatchParams {
269
537
  * always use "fetch", so leave it out for them. */
270
538
  engine?: "auto" | "fetch" | "browser";
271
539
  /** Whether to try again a different way when the first attempt is blocked. On by default.
272
- * `false` stops after one try. The site's refusal is your answer, and it costs nothing. It is
273
- * how you bound a call: with `auto`, trying again is what charges the browser price instead of
274
- * the fetch one. A POST is sent once, so it is off for a POST. */
540
+ * `false` stops after one try. The site's refusal is your answer, and it costs nothing, unless
541
+ * your own `browser.actions` script ran in the page first. It is how you bound a call: with
542
+ * `auto`, trying again is what charges the browser price instead of the fetch one. A POST is
543
+ * sent once, so it is off for a POST. */
275
544
  escalate?: boolean;
276
545
  /** How many extra tries to give a page the site blocks, each from a different address. 0 (the
277
546
  * default) takes the site's first no as the answer. A blocked page costs nothing, so a try
278
547
  * that is blocked again is free, but it takes as long as the first one did, so the call can
279
- * run that many times longer. */
548
+ * run that many times longer. Once your `browser.actions` steps click, type, press a key or
549
+ * run a script on a page, it is not tried again, so nothing is done twice. If one of your
550
+ * scripts ran in it, the block is charged. */
280
551
  retry?: number;
281
552
  /** What to hand back. "screenshot" and "pdf" come back as base64 and need the browser engine;
282
553
  * asking for both costs no more than one. "extract" is the page read into fields by a model,
283
- * and needs an `extract` block saying which ones. "raw" is the file exactly as the site sent
284
- * it, as base64 with its content type: a PDF report, an image, a spreadsheet. It uses the
285
- * fetch engine and holds up to 5 MB; a larger file answers FILE_TOO_LARGE at no charge, never
286
- * cut short. */
554
+ * and needs an `extract` block saying which ones. A link to a PDF or a Word (.docx) file is
555
+ * read into text, so the text formats work on it too. We read files up to 25 MB, and from a
556
+ * PDF the first 200 pages and the first 5 million characters. Reading a file costs what the
557
+ * engine costs: 1 credit with fetch, 5 with the browser. A Word file that holds more than 32
558
+ * MB once unzipped answers FILE_TOO_LARGE, and a file link that answers a short web page
559
+ * instead of the file answers UPSTREAM_BLOCKED, both at no charge. A dead link is the
560
+ * exception: its 404 or 410 page is the site's answer, and comes back with that status at the
561
+ * fetch price. "raw" is the file exactly as the site sent it, as base64 with its content type:
562
+ * a PDF report, an image, a spreadsheet. It uses the fetch engine and holds up to 5 MB; a
563
+ * larger file answers FILE_TOO_LARGE at no charge, never cut short. */
287
564
  formats?: ("markdown" | "html" | "text" | "links" | "screenshot" | "pdf" | "extract" | "raw")[];
565
+ /** Read only the page's main content into `markdown`, `text` and `extract`: the article or the
566
+ * product, without the menus, ads and related links around it. On by default. Set `false` for
567
+ * the whole page. When the main content is under 50 words, you get the whole page instead.
568
+ * `html` and `links` always hold the whole page. */
569
+ onlyMainContent?: boolean;
570
+ /** CSS selectors for the parts of the page to keep, such as `["h1", ".price"]`. `markdown`,
571
+ * `text` and `extract` then hold only what they match, in page order, and `onlyMainContent` no
572
+ * longer applies. Hidden parts are never kept. Up to 20 selectors, 256 characters each. */
573
+ includeTags?: string[];
574
+ /** CSS selectors for the parts of the page to leave out of `markdown`, `text` and `extract`,
575
+ * such as `[".comments", "#newsletter"]`. Up to 20 selectors, 256 characters each. */
576
+ excludeTags?: string[];
288
577
  /** What the page should answer with. Set `"json"` when you are reading an API: anything else
289
578
  * that comes back (a maintenance notice, a "checking your browser" page, a login form) is then
290
579
  * not your answer. It costs nothing, and we try again from another address instead of handing
@@ -313,8 +602,10 @@ export interface CreateBatchParams {
313
602
  server?: string;
314
603
  /** Where the call should look like it comes from, ISO 3166-1 alpha-2 (`us`, `br`). Name one
315
604
  * and the managed pool leaves from that country. Leave it out and the page sees a US
316
- * browser, from an address we pick. The browser's clock and languages follow the country
317
- * on every pool. */
605
+ * browser, from an address we pick. A scrape or a map on the managed pool makes one
606
+ * exception: a site whose address ends in `.br`, `.de`, `.es`, `.fr`, `.it`, `.jp`, `.nl`,
607
+ * `.pl` or `.uk` is read from its own country. The browser's clock and languages follow
608
+ * the country on every pool. */
318
609
  country?: string;
319
610
  /** A state or region inside `country`, by name: `california`, `são paulo`. Managed pool
320
611
  * only. Spell the name out, since an abbreviation like `sp` finds nobody. In the US and
@@ -327,7 +618,7 @@ export interface CreateBatchParams {
327
618
  city?: string;
328
619
  };
329
620
  /** How to render it. Only the browser engine reads these. With `auto` they apply if the page
330
- * turns out to need one. */
621
+ * turns out to need one, except `actions`, which always use the browser. */
331
622
  browser?: {
332
623
  /** When the page counts as loaded. "load" (the default) waits for images and stylesheets;
333
624
  * "domcontentloaded" is the fastest and stops at the HTML; "networkidle" waits until the
@@ -350,13 +641,89 @@ export interface CreateBatchParams {
350
641
  /** Screenshot the whole scrollable page instead of just the window. A long page makes a
351
642
  * large answer: the image comes back as base64 inside the response. */
352
643
  fullPage?: boolean;
644
+ /** Steps to run in the page before it is read, in the order you list them. A step can wait,
645
+ * click, type, press a key, scroll, take a screenshot, keep the HTML or run a script.
646
+ * Steps always use the browser engine, at the browser price. The formats you ask for read
647
+ * the page as the last step left it. Up to 50 steps, at most 10 of them screenshots or
648
+ * scrapes. If one fails, the call answers ACTION_FAILED, which names the step and why.
649
+ * That costs nothing, unless one of your scripts started, the one that failed included:
650
+ * then the call is charged. */
651
+ actions?: (
652
+ | {
653
+ type: "wait";
654
+ /** How long to wait, in milliseconds. All the waits together have to fit inside
655
+ * `timeoutMs`. */
656
+ milliseconds?: number;
657
+ /** Wait until this CSS selector is on the page. If it never shows up, the step fails. */
658
+ selector?: string;
659
+ }
660
+ | {
661
+ type: "click";
662
+ /** The CSS selector of what to click. The first match gets the click, and the step
663
+ * waits for it to show up. */
664
+ selector: string;
665
+ /** Click every visible match, not just the first. A selector that matches nothing is
666
+ * then not an error, which suits a cookie banner only some visitors see. */
667
+ all?: boolean;
668
+ }
669
+ | {
670
+ type: "write";
671
+ /** What to type, up to 1,000 characters. */
672
+ text: string;
673
+ }
674
+ | {
675
+ type: "press";
676
+ /** The key's name: "Enter", "Tab", "Escape", "ArrowDown", or two together such as
677
+ * "Control+A". */
678
+ key: string;
679
+ }
680
+ | {
681
+ type: "scroll";
682
+ direction: "up" | "down";
683
+ /** Scroll this element instead of the whole page, such as a list with its own scroll
684
+ * bar. */
685
+ selector?: string;
686
+ }
687
+ | {
688
+ type: "screenshot";
689
+ /** Take the whole scrollable page instead of just the window. */
690
+ fullPage?: boolean;
691
+ }
692
+ | {
693
+ type: "scrape";
694
+ }
695
+ | {
696
+ type: "executeJavascript";
697
+ /** The body of an async function, up to 8,000 characters, such as `return
698
+ * document.title`. It runs in the page, beside the page's own scripts. What it returns
699
+ * comes back in `actions.javascriptReturns` as JSON, or as null when JSON cannot hold
700
+ * it. All the scripts in one call can return 64 KB together. Once a script starts, the
701
+ * call is charged even if that script throws, a later step fails, the page crashes or
702
+ * times out, the site blocks it, the script leads to an address we refuse, your own
703
+ * proxy says no, or an `includeTags` or `excludeTags` selector is too slow for the
704
+ * page it built, because the script may already have read the page. A failure on our
705
+ * side (a 5xx) is still refunded. */
706
+ script: string;
707
+ }
708
+ )[];
353
709
  };
354
710
  /** Extra request headers to send to the page, such as an authorization token, a cookie or a
355
711
  * custom user agent. The connection's own headers cannot be set. */
356
712
  headers?: Record<string, string>;
357
- /** How long to give the page before giving up, in milliseconds. A page that never arrives is a
358
- * free outcome. */
713
+ /** How long to give the page before giving up, in milliseconds. A page that never arrives costs
714
+ * nothing, unless your own `browser.actions` script ran in the page first. */
359
715
  timeoutMs?: number;
716
+ /** The oldest copy of the page you accept, in milliseconds, up to 3600000 (one hour). If we
717
+ * still hold a copy of the same URL that your account read within that time, from the same
718
+ * country and with the same engine, you get it back for 0 credits and the answer says `cached:
719
+ * true`. If not, we read the page now and keep a copy for up to an hour, unless the page still
720
+ * needs JavaScript (`metadata.needsJs`). We may drop a copy sooner, to make room or on a
721
+ * deploy, and a batch keeps its own copies, apart from single calls. `0` always reads the page
722
+ * now, and keeps the copy for the calls after it. `extract` is still charged, because the
723
+ * model reads the copy again. Leave `maxAge` out to read the page fresh every time. A POST, or
724
+ * a call with `headers`, your own proxy, `browser` options, a screenshot, a PDF, `raw` or
725
+ * `escalate: false`, is always read fresh. */
726
+ maxAge?: number;
360
727
  /** The pages to read, in the order you want them back. How many one batch may carry depends on
361
728
  * your plan. Duplicates are kept: position, not URL, is what lines results up against what you
362
729
  * submitted. */
@@ -383,22 +750,43 @@ export interface QuoteBatchParams {
383
750
  * always use "fetch", so leave it out for them. */
384
751
  engine?: "auto" | "fetch" | "browser";
385
752
  /** Whether to try again a different way when the first attempt is blocked. On by default.
386
- * `false` stops after one try. The site's refusal is your answer, and it costs nothing. It is
387
- * how you bound a call: with `auto`, trying again is what charges the browser price instead of
388
- * the fetch one. A POST is sent once, so it is off for a POST. */
753
+ * `false` stops after one try. The site's refusal is your answer, and it costs nothing, unless
754
+ * your own `browser.actions` script ran in the page first. It is how you bound a call: with
755
+ * `auto`, trying again is what charges the browser price instead of the fetch one. A POST is
756
+ * sent once, so it is off for a POST. */
389
757
  escalate?: boolean;
390
758
  /** How many extra tries to give a page the site blocks, each from a different address. 0 (the
391
759
  * default) takes the site's first no as the answer. A blocked page costs nothing, so a try
392
760
  * that is blocked again is free, but it takes as long as the first one did, so the call can
393
- * run that many times longer. */
761
+ * run that many times longer. Once your `browser.actions` steps click, type, press a key or
762
+ * run a script on a page, it is not tried again, so nothing is done twice. If one of your
763
+ * scripts ran in it, the block is charged. */
394
764
  retry?: number;
395
765
  /** What to hand back. "screenshot" and "pdf" come back as base64 and need the browser engine;
396
766
  * asking for both costs no more than one. "extract" is the page read into fields by a model,
397
- * and needs an `extract` block saying which ones. "raw" is the file exactly as the site sent
398
- * it, as base64 with its content type: a PDF report, an image, a spreadsheet. It uses the
399
- * fetch engine and holds up to 5 MB; a larger file answers FILE_TOO_LARGE at no charge, never
400
- * cut short. */
767
+ * and needs an `extract` block saying which ones. A link to a PDF or a Word (.docx) file is
768
+ * read into text, so the text formats work on it too. We read files up to 25 MB, and from a
769
+ * PDF the first 200 pages and the first 5 million characters. Reading a file costs what the
770
+ * engine costs: 1 credit with fetch, 5 with the browser. A Word file that holds more than 32
771
+ * MB once unzipped answers FILE_TOO_LARGE, and a file link that answers a short web page
772
+ * instead of the file answers UPSTREAM_BLOCKED, both at no charge. A dead link is the
773
+ * exception: its 404 or 410 page is the site's answer, and comes back with that status at the
774
+ * fetch price. "raw" is the file exactly as the site sent it, as base64 with its content type:
775
+ * a PDF report, an image, a spreadsheet. It uses the fetch engine and holds up to 5 MB; a
776
+ * larger file answers FILE_TOO_LARGE at no charge, never cut short. */
401
777
  formats?: ("markdown" | "html" | "text" | "links" | "screenshot" | "pdf" | "extract" | "raw")[];
778
+ /** Read only the page's main content into `markdown`, `text` and `extract`: the article or the
779
+ * product, without the menus, ads and related links around it. On by default. Set `false` for
780
+ * the whole page. When the main content is under 50 words, you get the whole page instead.
781
+ * `html` and `links` always hold the whole page. */
782
+ onlyMainContent?: boolean;
783
+ /** CSS selectors for the parts of the page to keep, such as `["h1", ".price"]`. `markdown`,
784
+ * `text` and `extract` then hold only what they match, in page order, and `onlyMainContent` no
785
+ * longer applies. Hidden parts are never kept. Up to 20 selectors, 256 characters each. */
786
+ includeTags?: string[];
787
+ /** CSS selectors for the parts of the page to leave out of `markdown`, `text` and `extract`,
788
+ * such as `[".comments", "#newsletter"]`. Up to 20 selectors, 256 characters each. */
789
+ excludeTags?: string[];
402
790
  /** What the page should answer with. Set `"json"` when you are reading an API: anything else
403
791
  * that comes back (a maintenance notice, a "checking your browser" page, a login form) is then
404
792
  * not your answer. It costs nothing, and we try again from another address instead of handing
@@ -427,8 +815,10 @@ export interface QuoteBatchParams {
427
815
  server?: string;
428
816
  /** Where the call should look like it comes from, ISO 3166-1 alpha-2 (`us`, `br`). Name one
429
817
  * and the managed pool leaves from that country. Leave it out and the page sees a US
430
- * browser, from an address we pick. The browser's clock and languages follow the country
431
- * on every pool. */
818
+ * browser, from an address we pick. A scrape or a map on the managed pool makes one
819
+ * exception: a site whose address ends in `.br`, `.de`, `.es`, `.fr`, `.it`, `.jp`, `.nl`,
820
+ * `.pl` or `.uk` is read from its own country. The browser's clock and languages follow
821
+ * the country on every pool. */
432
822
  country?: string;
433
823
  /** A state or region inside `country`, by name: `california`, `são paulo`. Managed pool
434
824
  * only. Spell the name out, since an abbreviation like `sp` finds nobody. In the US and
@@ -441,7 +831,7 @@ export interface QuoteBatchParams {
441
831
  city?: string;
442
832
  };
443
833
  /** How to render it. Only the browser engine reads these. With `auto` they apply if the page
444
- * turns out to need one. */
834
+ * turns out to need one, except `actions`, which always use the browser. */
445
835
  browser?: {
446
836
  /** When the page counts as loaded. "load" (the default) waits for images and stylesheets;
447
837
  * "domcontentloaded" is the fastest and stops at the HTML; "networkidle" waits until the
@@ -464,13 +854,89 @@ export interface QuoteBatchParams {
464
854
  /** Screenshot the whole scrollable page instead of just the window. A long page makes a
465
855
  * large answer: the image comes back as base64 inside the response. */
466
856
  fullPage?: boolean;
857
+ /** Steps to run in the page before it is read, in the order you list them. A step can wait,
858
+ * click, type, press a key, scroll, take a screenshot, keep the HTML or run a script.
859
+ * Steps always use the browser engine, at the browser price. The formats you ask for read
860
+ * the page as the last step left it. Up to 50 steps, at most 10 of them screenshots or
861
+ * scrapes. If one fails, the call answers ACTION_FAILED, which names the step and why.
862
+ * That costs nothing, unless one of your scripts started, the one that failed included:
863
+ * then the call is charged. */
864
+ actions?: (
865
+ | {
866
+ type: "wait";
867
+ /** How long to wait, in milliseconds. All the waits together have to fit inside
868
+ * `timeoutMs`. */
869
+ milliseconds?: number;
870
+ /** Wait until this CSS selector is on the page. If it never shows up, the step fails. */
871
+ selector?: string;
872
+ }
873
+ | {
874
+ type: "click";
875
+ /** The CSS selector of what to click. The first match gets the click, and the step
876
+ * waits for it to show up. */
877
+ selector: string;
878
+ /** Click every visible match, not just the first. A selector that matches nothing is
879
+ * then not an error, which suits a cookie banner only some visitors see. */
880
+ all?: boolean;
881
+ }
882
+ | {
883
+ type: "write";
884
+ /** What to type, up to 1,000 characters. */
885
+ text: string;
886
+ }
887
+ | {
888
+ type: "press";
889
+ /** The key's name: "Enter", "Tab", "Escape", "ArrowDown", or two together such as
890
+ * "Control+A". */
891
+ key: string;
892
+ }
893
+ | {
894
+ type: "scroll";
895
+ direction: "up" | "down";
896
+ /** Scroll this element instead of the whole page, such as a list with its own scroll
897
+ * bar. */
898
+ selector?: string;
899
+ }
900
+ | {
901
+ type: "screenshot";
902
+ /** Take the whole scrollable page instead of just the window. */
903
+ fullPage?: boolean;
904
+ }
905
+ | {
906
+ type: "scrape";
907
+ }
908
+ | {
909
+ type: "executeJavascript";
910
+ /** The body of an async function, up to 8,000 characters, such as `return
911
+ * document.title`. It runs in the page, beside the page's own scripts. What it returns
912
+ * comes back in `actions.javascriptReturns` as JSON, or as null when JSON cannot hold
913
+ * it. All the scripts in one call can return 64 KB together. Once a script starts, the
914
+ * call is charged even if that script throws, a later step fails, the page crashes or
915
+ * times out, the site blocks it, the script leads to an address we refuse, your own
916
+ * proxy says no, or an `includeTags` or `excludeTags` selector is too slow for the
917
+ * page it built, because the script may already have read the page. A failure on our
918
+ * side (a 5xx) is still refunded. */
919
+ script: string;
920
+ }
921
+ )[];
467
922
  };
468
923
  /** Extra request headers to send to the page, such as an authorization token, a cookie or a
469
924
  * custom user agent. The connection's own headers cannot be set. */
470
925
  headers?: Record<string, string>;
471
- /** How long to give the page before giving up, in milliseconds. A page that never arrives is a
472
- * free outcome. */
926
+ /** How long to give the page before giving up, in milliseconds. A page that never arrives costs
927
+ * nothing, unless your own `browser.actions` script ran in the page first. */
473
928
  timeoutMs?: number;
929
+ /** The oldest copy of the page you accept, in milliseconds, up to 3600000 (one hour). If we
930
+ * still hold a copy of the same URL that your account read within that time, from the same
931
+ * country and with the same engine, you get it back for 0 credits and the answer says `cached:
932
+ * true`. If not, we read the page now and keep a copy for up to an hour, unless the page still
933
+ * needs JavaScript (`metadata.needsJs`). We may drop a copy sooner, to make room or on a
934
+ * deploy, and a batch keeps its own copies, apart from single calls. `0` always reads the page
935
+ * now, and keeps the copy for the calls after it. `extract` is still charged, because the
936
+ * model reads the copy again. Leave `maxAge` out to read the page fresh every time. A POST, or
937
+ * a call with `headers`, your own proxy, `browser` options, a screenshot, a PDF, `raw` or
938
+ * `escalate: false`, is always read fresh. */
939
+ maxAge?: number;
474
940
  /** The pages to read, in the order you want them back. How many one batch may carry depends on
475
941
  * your plan. Duplicates are kept: position, not URL, is what lines results up against what you
476
942
  * submitted. */
@@ -530,8 +996,10 @@ export interface CreateSessionParams {
530
996
  server?: string;
531
997
  /** Where the call should look like it comes from, ISO 3166-1 alpha-2 (`us`, `br`). Name one
532
998
  * and the managed pool leaves from that country. Leave it out and the page sees a US
533
- * browser, from an address we pick. The browser's clock and languages follow the country
534
- * on every pool. */
999
+ * browser, from an address we pick. A scrape or a map on the managed pool makes one
1000
+ * exception: a site whose address ends in `.br`, `.de`, `.es`, `.fr`, `.it`, `.jp`, `.nl`,
1001
+ * `.pl` or `.uk` is read from its own country. The browser's clock and languages follow
1002
+ * the country on every pool. */
535
1003
  country?: string;
536
1004
  /** A state or region inside `country`, by name: `california`, `são paulo`. Managed pool
537
1005
  * only. Spell the name out, since an abbreviation like `sp` finds nobody. In the US and
@@ -562,8 +1030,10 @@ export interface QuoteSessionParams {
562
1030
  server?: string;
563
1031
  /** Where the call should look like it comes from, ISO 3166-1 alpha-2 (`us`, `br`). Name one
564
1032
  * and the managed pool leaves from that country. Leave it out and the page sees a US
565
- * browser, from an address we pick. The browser's clock and languages follow the country
566
- * on every pool. */
1033
+ * browser, from an address we pick. A scrape or a map on the managed pool makes one
1034
+ * exception: a site whose address ends in `.br`, `.de`, `.es`, `.fr`, `.it`, `.jp`, `.nl`,
1035
+ * `.pl` or `.uk` is read from its own country. The browser's clock and languages follow
1036
+ * the country on every pool. */
567
1037
  country?: string;
568
1038
  /** A state or region inside `country`, by name: `california`, `são paulo`. Managed pool
569
1039
  * only. Spell the name out, since an abbreviation like `sp` finds nobody. In the US and