diff --git a/snippets/whitelabel/ai/batches/id.mdx b/snippets/whitelabel/ai/batches/id.mdx
new file mode 100644
index 0000000..6acde92
--- /dev/null
+++ b/snippets/whitelabel/ai/batches/id.mdx
@@ -0,0 +1,5 @@
+```shell
+curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
+'https://render.joinmassive.com/ai/batches'\
+'?id=b3829f53-abc9-4e62-8a65-2d9359d586d6'
+```
diff --git a/snippets/whitelabel/ai/batches/prompts.mdx b/snippets/whitelabel/ai/batches/prompts.mdx
new file mode 100644
index 0000000..6456a20
--- /dev/null
+++ b/snippets/whitelabel/ai/batches/prompts.mdx
@@ -0,0 +1,15 @@
+```shell
+curl -X POST 'https://render.joinmassive.com/ai/batches' \
+-H "Authorization: Bearer $MASSIVE_TOKEN" \
+-H 'Content-Type: application/json' \
+-d '{
+ "prompts": [
+ "Where can I buy a vintage guitar?",
+ "Help me find guitar repair shops nearby.",
+ "Do any local stores sell vinyl records?",
+ "[Additional prompts here]"
+ ],
+ "country": "US",
+ "subdivision": "TN"
+}'
+```
diff --git a/snippets/whitelabel/ai/completions.mdx b/snippets/whitelabel/ai/completions.mdx
new file mode 100644
index 0000000..6acde92
--- /dev/null
+++ b/snippets/whitelabel/ai/completions.mdx
@@ -0,0 +1,5 @@
+```shell
+curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
+'https://render.joinmassive.com/ai/batches'\
+'?id=b3829f53-abc9-4e62-8a65-2d9359d586d6'
+```
diff --git a/snippets/whitelabel/ai/device.mdx b/snippets/whitelabel/ai/device.mdx
index fb96883..773c47c 100644
--- a/snippets/whitelabel/ai/device.mdx
+++ b/snippets/whitelabel/ai/device.mdx
@@ -1,6 +1,6 @@
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/ai'\
-'?prompt=what+happened+to+blackberry'\
-'&device=blackberry+playbook'
+'?prompt=What+happened+to+blackberry%3F'\
+'&device=Blackberry+PlayBook'
```
diff --git a/snippets/whitelabel/ai/expiration.mdx b/snippets/whitelabel/ai/expiration.mdx
index 8037a6d..1a081f1 100644
--- a/snippets/whitelabel/ai/expiration.mdx
+++ b/snippets/whitelabel/ai/expiration.mdx
@@ -1,8 +1,6 @@
-### Cachebusting
-
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/ai'\
-'?prompt=current+weather'\
+'?prompt=Show+the+current+weather.'\
'&expiration=0'
```
diff --git a/snippets/whitelabel/ai/id.mdx b/snippets/whitelabel/ai/id.mdx
index 513678c..fa0c42e 100644
--- a/snippets/whitelabel/ai/id.mdx
+++ b/snippets/whitelabel/ai/id.mdx
@@ -1,5 +1,5 @@
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
-'https://render.joinmassive.com/ai/completions'\
+'https://render.joinmassive.com/ai'\
'?id=1851dab8-4619-409f-893f-47dd3a180bc3'
```
diff --git a/snippets/whitelabel/ai/mode.mdx b/snippets/whitelabel/ai/mode.mdx
index bb9bb9f..d264950 100644
--- a/snippets/whitelabel/ai/mode.mdx
+++ b/snippets/whitelabel/ai/mode.mdx
@@ -1,6 +1,6 @@
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/ai'\
-'?prompt=prolonged+fasting'\
+'?prompt=Summarize+the+latest+research+on+prolonged+fasting.'\
'&mode=async'
```
diff --git a/snippets/whitelabel/ai/subdivision.mdx b/snippets/whitelabel/ai/subdivision.mdx
index bbb582a..b85a5a3 100644
--- a/snippets/whitelabel/ai/subdivision.mdx
+++ b/snippets/whitelabel/ai/subdivision.mdx
@@ -1,7 +1,7 @@
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/ai'\
-'?prompt=find+vintage+guitar+stores'\
-'&country=us'\
-'&subdivision=tn'
+'?prompt=Where+can+I+buy+a+vintage+guitar%3F'\
+'&country=US'\
+'&subdivision=TN'
```
diff --git a/snippets/whitelabel/browser/batches/id.mdx b/snippets/whitelabel/browser/batches/id.mdx
new file mode 100644
index 0000000..0ad8a20
--- /dev/null
+++ b/snippets/whitelabel/browser/batches/id.mdx
@@ -0,0 +1,5 @@
+```shell
+curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
+'https://render.joinmassive.com/browser/batches'\
+'?id=cfde9b46-f599-49ab-844e-9b2461285284'
+```
diff --git a/snippets/whitelabel/browser/batches/urls.mdx b/snippets/whitelabel/browser/batches/urls.mdx
new file mode 100644
index 0000000..af735d6
--- /dev/null
+++ b/snippets/whitelabel/browser/batches/urls.mdx
@@ -0,0 +1,15 @@
+```shell
+curl -X POST 'https://render.joinmassive.com/browser/batches' \
+-H "Authorization: Bearer $MASSIVE_TOKEN" \
+-H 'Content-Type: application/json' \
+-d '{
+ "urls": [
+ "https://guitars.com/",
+ "https://cartervintage.com/",
+ "https://musiccityvintageguitars.com/",
+ "[Additional URLs here]"
+ ],
+ "country": "US",
+ "city": "Nashville"
+}'
+```
diff --git a/snippets/whitelabel/browser/cdp/cdp2rest.mdx b/snippets/whitelabel/browser/cdp/cdp2rest.mdx
new file mode 100644
index 0000000..88e9a79
--- /dev/null
+++ b/snippets/whitelabel/browser/cdp/cdp2rest.mdx
@@ -0,0 +1,107 @@
+```js
+#!/usr/bin/env node
+import puppeteer from 'puppeteer';
+
+const cdpEndpoint = 'wss://render.joinmassive.com/browser';
+const restEndpoint = 'https://render.joinmassive.com/browser';
+const apiToken = process.env.MASSIVE_TOKEN;
+const sessionId = Date.now();
+const headers = {
+ Authorization: `Bearer ${apiToken}`,
+ Cookie: `session=${sessionId}`
+};
+const orderUrl = 'https://httpbin.org/forms/post';
+const receiptUrl = 'https://httpbin.org/post';
+const sizeSelector = '[name="size"][value="medium"]';
+const toppingSelector = '[name="topping"][value="onion"]';
+const buttonSelector = 'button';
+const timeoutMs = 180_000;
+const delayMs = 1_000;
+
+console.log(`Connecting via CDP with session ${sessionId} ...`);
+
+const browser = await puppeteer.connect({
+ browserWSEndpoint: cdpEndpoint,
+ headers
+});
+const [page] = await browser.pages();
+let response = await page.goto(orderUrl);
+
+if (response?.ok()) {
+ console.log(`"${await page.title()}" interacted with successfully`);
+} else {
+ console.error(
+ 'Page interaction failure:',
+ await page.evaluate(() => {
+ return document.body.innerText;
+ })
+ );
+
+ process.exit(1);
+}
+
+await page.waitForSelector(sizeSelector);
+await page.click(sizeSelector);
+await new Promise((resolve) => {
+ setTimeout(resolve, delayMs);
+});
+await page.waitForSelector(toppingSelector);
+await page.click(toppingSelector);
+await new Promise((resolve) => {
+ setTimeout(resolve, delayMs);
+});
+await page.waitForSelector(buttonSelector);
+await Promise.all([
+ page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
+ page.click(buttonSelector)
+]);
+await browser.disconnect();
+
+console.log(`Making REST request with session ${sessionId} ...`);
+
+const controller = new AbortController();
+const timeoutId = setTimeout(() => {
+ controller.abort();
+}, timeoutMs);
+
+try {
+ response = await fetch(
+ `${restEndpoint}?url=${encodeURIComponent(receiptUrl)}&expiration=0`,
+ { headers, signal: controller.signal }
+ );
+} catch (error) {
+ const hasTimedOut = error.name == 'AbortError';
+ response = new Response(
+ hasTimedOut ? 'Request timed out' : 'Unknown error occurred',
+ { status: hasTimedOut ? 504 : 500 }
+ );
+} finally {
+ clearTimeout(timeoutId);
+}
+
+if (response.ok) {
+ const data = await response.json();
+
+ if (
+ data.form &&
+ typeof data.form.size == 'string' &&
+ typeof data.form.topping == 'string'
+ ) {
+ console.log(
+ 'Ordered ' +
+ data.form.size.toLowerCase() +
+ ' pizza with ' +
+ data.form.topping.toLowerCase() +
+ ' successfully'
+ );
+ } else {
+ console.error('Receipt was malformed');
+
+ process.exit(1);
+ }
+} else {
+ console.error('Order form read failure:', (await response.text()).trimEnd());
+
+ process.exit(1);
+}
+```
diff --git a/snippets/whitelabel/browser/cdp/close.mdx b/snippets/whitelabel/browser/cdp/close.mdx
new file mode 100644
index 0000000..2a4d900
--- /dev/null
+++ b/snippets/whitelabel/browser/cdp/close.mdx
@@ -0,0 +1,3 @@
+```py
+browser.close()
+```
diff --git a/snippets/whitelabel/browser/cdp/device.mdx b/snippets/whitelabel/browser/cdp/device.mdx
new file mode 100644
index 0000000..68f50ca
--- /dev/null
+++ b/snippets/whitelabel/browser/cdp/device.mdx
@@ -0,0 +1,10 @@
+```js
+const browser = await puppeteer.connect({
+ browserWSEndpoint:
+ 'wss://render.joinmassive.com/browser' +
+ '?device=Blackberry+Playbook',
+ headers: {
+ Authorization: `Bearer ${process.env.MASSIVE_TOKEN}`
+ }
+});
+```
diff --git a/snippets/whitelabel/browser/cdp/difficulty.mdx b/snippets/whitelabel/browser/cdp/difficulty.mdx
new file mode 100644
index 0000000..700c748
--- /dev/null
+++ b/snippets/whitelabel/browser/cdp/difficulty.mdx
@@ -0,0 +1,10 @@
+```js
+const browser = await puppeteer.connect({
+ browserWSEndpoint:
+ 'wss://render.joinmassive.com/browser' +
+ '?difficulty=medium',
+ headers: {
+ Authorization: `Bearer ${process.env.MASSIVE_TOKEN}`
+ }
+});
+```
diff --git a/snippets/whitelabel/browser/cdp/disconnect.mdx b/snippets/whitelabel/browser/cdp/disconnect.mdx
new file mode 100644
index 0000000..a9eb10d
--- /dev/null
+++ b/snippets/whitelabel/browser/cdp/disconnect.mdx
@@ -0,0 +1,3 @@
+```js
+await browser.disconnect();
+```
diff --git a/snippets/whitelabel/browser/cdp/e2e.mdx b/snippets/whitelabel/browser/cdp/e2e.mdx
new file mode 100644
index 0000000..c7f9f75
--- /dev/null
+++ b/snippets/whitelabel/browser/cdp/e2e.mdx
@@ -0,0 +1,26 @@
+```py
+#!/usr/bin/env python3
+import os
+from playwright.sync_api import sync_playwright
+
+ENDPOINT = 'wss://render.joinmassive.com/browser'
+API_TOKEN = os.environ['MASSIVE_TOKEN']
+URL = 'https://example.com/'
+
+print(f"Connecting to {ENDPOINT} ...")
+
+with sync_playwright() as playwright:
+ browser = playwright.chromium.connect_over_cdp(
+ ENDPOINT,
+ headers={'Authorization': f"Bearer {API_TOKEN}"}
+ )
+ context = browser.contexts[0]
+ page = context.pages[0]
+
+ print(f"Going to {URL} ...")
+
+ page.goto(URL)
+ print(f"URL: {page.url}")
+ print(f"Title: {page.title()}")
+ browser.close()
+```
diff --git a/snippets/whitelabel/browser/cdp/goto.mdx b/snippets/whitelabel/browser/cdp/goto.mdx
new file mode 100644
index 0000000..cd51a4a
--- /dev/null
+++ b/snippets/whitelabel/browser/cdp/goto.mdx
@@ -0,0 +1,7 @@
+```js
+const [page] = await browser.pages();
+
+await page.goto('https://example.com/');
+console.log('URL:', page.url());
+console.log('Title:', await page.title());
+```
diff --git a/snippets/whitelabel/browser/cdp/playwright.mdx b/snippets/whitelabel/browser/cdp/playwright.mdx
new file mode 100644
index 0000000..9f1430e
--- /dev/null
+++ b/snippets/whitelabel/browser/cdp/playwright.mdx
@@ -0,0 +1,14 @@
+```py
+#!/usr/bin/env python3
+import os
+from playwright.sync_api import sync_playwright
+
+with sync_playwright() as playwright:
+ browser = playwright.chromium.connect_over_cdp(
+ 'wss://render.joinmassive.com/browser',
+ headers={
+ 'Authorization':
+ f"Bearer {os.environ['MASSIVE_TOKEN']}"
+ }
+ )
+```
diff --git a/snippets/whitelabel/browser/cdp/puppeteer.mdx b/snippets/whitelabel/browser/cdp/puppeteer.mdx
new file mode 100644
index 0000000..ad21f80
--- /dev/null
+++ b/snippets/whitelabel/browser/cdp/puppeteer.mdx
@@ -0,0 +1,11 @@
+```js
+#!/usr/bin/env node
+import puppeteer from 'puppeteer';
+
+const browser = await puppeteer.connect({
+ browserWSEndpoint: 'wss://render.joinmassive.com/browser',
+ headers: {
+ Authorization: `Bearer ${process.env.MASSIVE_TOKEN}`
+ }
+});
+```
diff --git a/snippets/whitelabel/browser/cdp/rest2cdp.mdx b/snippets/whitelabel/browser/cdp/rest2cdp.mdx
new file mode 100644
index 0000000..7324512
--- /dev/null
+++ b/snippets/whitelabel/browser/cdp/rest2cdp.mdx
@@ -0,0 +1,149 @@
+```js
+#!/usr/bin/env node
+import puppeteer from 'puppeteer';
+
+const restEndpoint = 'https://render.joinmassive.com/browser';
+const cdpEndpoint = 'wss://render.joinmassive.com/browser';
+const apiToken = process.env.MASSIVE_TOKEN;
+const headers = { Authorization: `Bearer ${apiToken}` };
+const sessionId = Date.now();
+const orderUrl = 'https://httpbin.org/forms/post';
+const sizeSelector = '[name="size"][value="small"]';
+const toppingSelector = '[name="topping"][value="cheese"]';
+const buttonSelector = 'button';
+const requestTimeoutMs = 180_000;
+const connectionTimeoutMs = 10_000;
+const delayMs = 1_000;
+const delay = (ms) => {
+ return new Promise((resolve) => {
+ setTimeout(resolve, ms);
+ });
+};
+const getCookies = (response) => {
+ const cookies = response.headers.getSetCookie?.() ?? [];
+ const keyvals = cookies.map((cookie) => {
+ return cookie.split(';', 1)[0];
+ });
+
+ return keyvals.join('; ');
+};
+
+console.log(
+ 'Making REST request with session',
+ sessionId,
+ '...'
+);
+
+const controller = new AbortController();
+let timeoutId = setTimeout(() => {
+ controller.abort();
+}, requestTimeoutMs);
+let response;
+
+try {
+ const url =
+ restEndpoint +
+ '?url=' +
+ encodeURIComponent(orderUrl) +
+ '&session=' +
+ sessionId +
+ '&expiration=0';
+ response = await fetch(
+ url,
+ { headers, signal: controller.signal }
+ );
+} catch (error) {
+ const hasTimedOut = error.name == 'AbortError';
+ response = new Response(
+ hasTimedOut
+ ? 'Request timed out'
+ : 'Unknown error occurred',
+ { status: hasTimedOut ? 504 : 500 }
+ );
+} finally {
+ clearTimeout(timeoutId);
+}
+
+if (response.ok) {
+ console.log('Order form read successfully');
+} else {
+ const text = await response.text();
+
+ console.error('Order form read failure:', text.trimEnd());
+
+ process.exit(1);
+}
+
+console.log(
+ 'Connecting via CDP with session',
+ sessionId,
+ '...'
+);
+
+timeoutId = setTimeout(() => {
+ console.error('Connection timed out');
+
+ process.exit(1);
+}, connectionTimeoutMs);
+const url = `${cdpEndpoint}?session=${sessionId}`;
+const browser = await puppeteer.connect({
+ browserWSEndpoint: url,
+ headers: { ...headers, Cookie: getCookies(response) }
+});
+
+clearTimeout(timeoutId);
+
+const page = (await browser.pages()).find((page) => {
+ return page.url().includes(orderUrl);
+});
+
+if (page) {
+ console.log('Session handed off successfully');
+} else {
+ console.error('Session handoff failure');
+
+ process.exit(1);
+}
+
+await page.waitForSelector(sizeSelector);
+await page.click(sizeSelector);
+await delay(delayMs);
+await page.waitForSelector(toppingSelector);
+await page.click(toppingSelector);
+await delay(delayMs);
+await page.waitForSelector(buttonSelector);
+await Promise.all([
+ page.waitForNavigation({ waitUntil: 'domcontentloaded' }),
+ page.click(buttonSelector)
+]);
+
+try {
+ const json = await page.evaluate(() => {
+ return document.body.innerText;
+ });
+ const data = JSON.parse(json);
+
+ if (
+ data.form &&
+ typeof data.form.size == 'string' &&
+ typeof data.form.topping == 'string'
+ ) {
+ console.log(
+ 'Ordered',
+ data.form.size.toLowerCase(),
+ 'pizza with',
+ data.form.topping.toLowerCase()
+ );
+ } else {
+ console.error('Receipt was malformed');
+
+ process.exit(1);
+ }
+} catch (error) {
+ console.error('Receipt parse failure:', error.message);
+
+ process.exit(1);
+}
+
+await browser.disconnect();
+```
diff --git a/snippets/whitelabel/browser/city.mdx b/snippets/whitelabel/browser/city.mdx
index 7dc5326..ef8b6bc 100644
--- a/snippets/whitelabel/browser/city.mdx
+++ b/snippets/whitelabel/browser/city.mdx
@@ -2,6 +2,6 @@
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/browser'\
'?url=https://guitars.com/'\
-'&country=us'\
+'&country=US'\
'&city=Nashville'
```
diff --git a/snippets/whitelabel/browser/content.mdx b/snippets/whitelabel/browser/content.mdx
new file mode 100644
index 0000000..0ad8a20
--- /dev/null
+++ b/snippets/whitelabel/browser/content.mdx
@@ -0,0 +1,5 @@
+```shell
+curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
+'https://render.joinmassive.com/browser/batches'\
+'?id=cfde9b46-f599-49ab-844e-9b2461285284'
+```
diff --git a/snippets/whitelabel/browser/device.mdx b/snippets/whitelabel/browser/device.mdx
index f6a1bf9..a0a7500 100644
--- a/snippets/whitelabel/browser/device.mdx
+++ b/snippets/whitelabel/browser/device.mdx
@@ -2,5 +2,5 @@
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/browser'\
'?url=https://crackberry.com/'\
-'&device=blackberry+playbook'
+'&device=Blackberry+Playbook'
```
diff --git a/snippets/whitelabel/browser/expiration.mdx b/snippets/whitelabel/browser/expiration.mdx
index 0f2854a..abfb802 100644
--- a/snippets/whitelabel/browser/expiration.mdx
+++ b/snippets/whitelabel/browser/expiration.mdx
@@ -1,5 +1,3 @@
-### Cachebusting
-
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/browser'\
diff --git a/snippets/whitelabel/browser/id.mdx b/snippets/whitelabel/browser/id.mdx
index 3e22ad0..941467a 100644
--- a/snippets/whitelabel/browser/id.mdx
+++ b/snippets/whitelabel/browser/id.mdx
@@ -1,5 +1,5 @@
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
-'https://render.joinmassive.com/browser/content'\
+'https://render.joinmassive.com/browser'\
'?id=21cb972e-0e0f-47bb-9ce9-65b99e9cee77'
```
diff --git a/snippets/whitelabel/browser/session.mdx b/snippets/whitelabel/browser/session.mdx
deleted file mode 100644
index d100ed1..0000000
--- a/snippets/whitelabel/browser/session.mdx
+++ /dev/null
@@ -1,6 +0,0 @@
-```shell
-curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
--H 'Cookie: session=12345' \
-'https://render.joinmassive.com/browser'\
-'?url=https://www.amazon.com/s?k=luggage'
-```
diff --git a/snippets/whitelabel/search/awaiting.mdx b/snippets/whitelabel/search/awaiting.mdx
index fe3fd84..71625cb 100644
--- a/snippets/whitelabel/search/awaiting.mdx
+++ b/snippets/whitelabel/search/awaiting.mdx
@@ -1,9 +1,7 @@
-### Lazy results
-
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/search'\
-'?terms=how+tall+is+mount+everest'\
+'?terms=how+tall+is+mount+everest%3F'\
'&awaiting=ai'\
'&awaiting=answers'
```
diff --git a/snippets/whitelabel/search/batches/id.mdx b/snippets/whitelabel/search/batches/id.mdx
new file mode 100644
index 0000000..242d046
--- /dev/null
+++ b/snippets/whitelabel/search/batches/id.mdx
@@ -0,0 +1,5 @@
+```shell
+curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
+'https://render.joinmassive.com/search/batches'\
+'?id=a0482596-c2ad-445a-a435-727002156a20'
+```
diff --git a/snippets/whitelabel/search/batches/terms.mdx b/snippets/whitelabel/search/batches/terms.mdx
new file mode 100644
index 0000000..e240454
--- /dev/null
+++ b/snippets/whitelabel/search/batches/terms.mdx
@@ -0,0 +1,9 @@
+```shell
+curl -X POST 'https://render.joinmassive.com/search/batches' \
+-H "Authorization: Bearer $MASSIVE_TOKEN" \
+-H 'Content-Type: application/json' \
+-d '{
+ "terms": ["musical instruments"],
+ "country": "US"
+}'
+```
diff --git a/snippets/whitelabel/search/callback.mdx b/snippets/whitelabel/search/callback.mdx
index 1f494cf..9d87cb8 100644
--- a/snippets/whitelabel/search/callback.mdx
+++ b/snippets/whitelabel/search/callback.mdx
@@ -1,5 +1,3 @@
-### Push notification
-
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/search'\
diff --git a/snippets/whitelabel/search/country.mdx b/snippets/whitelabel/search/country.mdx
index bf57367..257ff2f 100644
--- a/snippets/whitelabel/search/country.mdx
+++ b/snippets/whitelabel/search/country.mdx
@@ -2,5 +2,5 @@
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/search'\
'?terms=musical+instruments'\
-'&country=us'
+'&country=US'
```
diff --git a/snippets/whitelabel/search/device.mdx b/snippets/whitelabel/search/device.mdx
new file mode 100644
index 0000000..e826424
--- /dev/null
+++ b/snippets/whitelabel/search/device.mdx
@@ -0,0 +1,6 @@
+```shell
+curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
+'https://render.joinmassive.com/search'\
+'?terms=blackberry'\
+'&device=Blackberry+PlayBook'
+```
diff --git a/snippets/whitelabel/search/devices.mdx b/snippets/whitelabel/search/devices.mdx
new file mode 100644
index 0000000..a3940c9
--- /dev/null
+++ b/snippets/whitelabel/search/devices.mdx
@@ -0,0 +1,4 @@
+```shell
+curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
+'https://render.joinmassive.com/search/devices'
+```
diff --git a/snippets/whitelabel/search/id.mdx b/snippets/whitelabel/search/id.mdx
index 04b4b73..3045706 100644
--- a/snippets/whitelabel/search/id.mdx
+++ b/snippets/whitelabel/search/id.mdx
@@ -1,5 +1,5 @@
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
-'https://render.joinmassive.com/search/results'\
+'https://render.joinmassive.com/search'\
'?id=078fd246-f0f7-44a0-aabb-cadd7b12454f'
```
diff --git a/snippets/whitelabel/search/offset.mdx b/snippets/whitelabel/search/offset.mdx
index 56bb4d4..4d5c30c 100644
--- a/snippets/whitelabel/search/offset.mdx
+++ b/snippets/whitelabel/search/offset.mdx
@@ -1,5 +1,3 @@
-### Results omission
-
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/search'\
diff --git a/snippets/whitelabel/search/results.mdx b/snippets/whitelabel/search/results.mdx
new file mode 100644
index 0000000..242d046
--- /dev/null
+++ b/snippets/whitelabel/search/results.mdx
@@ -0,0 +1,5 @@
+```shell
+curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
+'https://render.joinmassive.com/search/batches'\
+'?id=a0482596-c2ad-445a-a435-727002156a20'
+```
diff --git a/snippets/whitelabel/search/subaccount.mdx b/snippets/whitelabel/search/subaccount.mdx
index ad2b3b5..8c1970e 100644
--- a/snippets/whitelabel/search/subaccount.mdx
+++ b/snippets/whitelabel/search/subaccount.mdx
@@ -1,5 +1,3 @@
-### Accounting
-
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/search'\
diff --git a/snippets/whitelabel/search/url.mdx b/snippets/whitelabel/search/url.mdx
index 5b23e48..e7a27de 100644
--- a/snippets/whitelabel/search/url.mdx
+++ b/snippets/whitelabel/search/url.mdx
@@ -1,5 +1,3 @@
-### Alternative URL
-
```shell
curl -H "Authorization: Bearer $MASSIVE_TOKEN" \
'https://render.joinmassive.com/search'\
diff --git a/web-render/ai.mdx b/web-render/ai.mdx
index 940d96b..edd7146 100644
--- a/web-render/ai.mdx
+++ b/web-render/ai.mdx
@@ -49,8 +49,9 @@ The keys and values **{companyName}** supports are as follows:
| `device` | ⬜ | The name as returned by the [devices resource](#device-emulation) of the device to emulate chatting on (these names are case insensitive but must include form- or URL-encoded spaces and punctuation marks); device emulation is unused by default |
| `language` (planned) | ⬜ | The commonly spelled name or [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php) of the language to chat in (these names and codes are case insensitive but required to include form- or URL-encoded spaces and punctuation marks); the language is unset by default |
| `display` (planned) | ⬜ | The commonly spelled name or [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php) of the chat interface's display language (these names and codes are case insensitive but required to include form- or URL-encoded spaces and punctuation marks); the display language is unset by default or set to the language if given |
-| `format` | ⬜ | The format to output to, `json`, `rendered`, or `raw` (see the [section below](#response-format)); `json` is the default format |
+| `format` | ⬜ | The format to output to, `json`, `rendered`, or `raw` (planned) (see the [section below](#response-format)); `json` is the default format |
| `expiration` | ⬜ | The age in days of when to consider a cached completion expired, where `0` disables caching; `1` is the default number of days before expiration |
+| `callback` | ⬜ | The encoded HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN of up to 2,047 characters to notify when the (prerequisite) asynchronous completion has been retrieved; any SQS queue must grant `sqs:SendMessage` permission to the `arn:aws:iam::180363035301:role/api-instance` AWS principal |
### Device emulation
@@ -77,7 +78,7 @@ Then, pass your device name of choice into your chat request:
### Response format
-Completions are returned as structured JSON, rendered HTML, or raw (unrendered) HTML.
+Completions are returned as structured JSON, rendered HTML, or raw (unrendered) HTML (planned).
#### JSON
@@ -106,8 +107,10 @@ If you set the output to JSON, your completion will be parsed into an object wit
#### HTML
HTML is available after processing by rendering and JavaScript engines or in the raw form sent by
-the chatbot.
+the chatbot (planned).
## Additional examples
+### Cachebusting
+
diff --git a/web-render/batching.mdx b/web-render/batching.mdx
new file mode 100644
index 0000000..d1963b8
--- /dev/null
+++ b/web-render/batching.mdx
@@ -0,0 +1,57 @@
+---
+title: Bulk processing
+icon: list-check
+iconType: light
+description: Make massively parallel requests.
+---
+
+import { apiEndpoint } from '/snippets/whitelabel/config.mdx';
+import Terms from '/snippets/whitelabel/search/batches/terms.mdx';
+import Prompts from '/snippets/whitelabel/ai/batches/prompts.mdx';
+import Urls from '/snippets/whitelabel/browser/batches/urls.mdx';
+import Results from '/snippets/whitelabel/search/batches/id.mdx';
+import Completions from '/snippets/whitelabel/ai/batches/id.mdx';
+import Content from '/snippets/whitelabel/browser/batches/id.mdx';
+
+## Batch jobs
+
+Each service has a batch endpoint that accepts a JSON array of up to **1,000 requests** in a `POST`
+body:
+
+
+
+
+
+
+
+
+
+
+
+
+
+Search, AI, or
+browsing and
+geotargeting parameters can be included in the body. In response,
+the API will provide a job identifier and confirmation of the request count:
+
+```json
+{
+ "id": "b3829f53-abc9-4e62-8a65-2d9359d586d6",
+ "count": 37
+}
+```
+
+The job status can be requested any time thereafter by passing back the ID:
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web-render/browser.mdx b/web-render/browser.mdx
index c03c424..90aa5f6 100644
--- a/web-render/browser.mdx
+++ b/web-render/browser.mdx
@@ -10,9 +10,15 @@ import { Reference } from '/snippets/whitelabel/reference.mdx';
import { Autoscaling } from '/snippets/whitelabel/autoscaling.mdx';
import Authentication from '/snippets/whitelabel/authentication.mdx';
import Url from '/snippets/whitelabel/browser/url.mdx';
+import Puppeteer from '/snippets/whitelabel/browser/cdp/puppeteer.mdx';
+import Playwright from '/snippets/whitelabel/browser/cdp/playwright.mdx';
+import Difficulty from '/snippets/whitelabel/browser/cdp/difficulty.mdx';
+import Goto from '/snippets/whitelabel/browser/cdp/goto.mdx';
+import Rest2Cdp from '/snippets/whitelabel/browser/cdp/rest2cdp.mdx';
import Device from '/snippets/whitelabel/browser/device.mdx';
+import CdpDevice from '/snippets/whitelabel/browser/cdp/device.mdx';
import Expiration from '/snippets/whitelabel/browser/expiration.mdx';
-import Session from '/snippets/whitelabel/browser/session.mdx';
+import E2E from '/snippets/whitelabel/browser/cdp/e2e.mdx';
import Devices from '/snippets/whitelabel/browser/devices.mdx';
@@ -22,13 +28,15 @@ import Devices from '/snippets/whitelabel/browser/devices.mdx';
**{companyName}'s** browser service navigates intelligently on your behalf, rendering JavaScript,
solving [captchas](https://www.ibm.com/topics/captcha), and retrying failed requests. The service
has a RESTful interface that accepts `GET` requests at
-**https://{apiEndpoint}/browser**.
+**https://{apiEndpoint}/browser** and a
+[Chrome DevTools Protocol (CDP) interface](#cdp-interaction) for controlling browser navigation
+yourself that accepts WebSocket connections at **wss://{apiEndpoint}/browser**.
-Webpages can be localized with geotargeting parameters. They're
-retrieved in real time by default or optionally queued for subsequent
-retrieval. Up to **3 minutes** is allotted per real-time API call to accommodate
-captcha-solving, multiple retries, and other mitigations. Usage data is
-published regularly.
+Webpages can be localized with geotargeting parameters. Those
+requested via REST are retrieved in real time by default or optionally queued for subsequent retrieval. Up to **3 minutes** is allotted per real-time API
+call to accommodate captcha-solving, multiple retries, and other mitigations. Usage data is published regularly.
If a site you're targeting remains blocked, even with different browsing params,
-### Browsing parameters
+### REST parameters
Besides the [geotargeting and scheduling params linked above](#service-usage), required and optional
-browsing params can be added in a
+REST params can be added in a
[standard query string](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL#parameters).
See our rate card for the prices of premium params. The keys and values
**{companyName}** supports are as follows:
-| Key | Required | Premium | Value |
-| :----------- | :------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `url` | ✅ | ⬜ | The URL of the page to browse; any unsafe characters require [URL encoding](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding) |
-| `difficulty` | ⬜ | ✅ | The difficulty pool to attempt to access the URL from, `low`, `medium`, or `high` (planned); `low` is the default difficulty |
-| `speed` | ⬜ | ✅ | The speed to attempt to access the URL at, `light`, `ridiculous`, or `ludicrous` (planned), where `ridiculous` is 30 percent faster on average than `light` speed; `light` is the default speed |
-| `device` | ⬜ | ⬜ | The name as returned by the [devices resource](#device-emulation) of the device to emulate browsing on (these names are case insensitive but must include form- or URL-encoded spaces and punctuation marks); device emulation is unused by default |
-| `captcha` | ⬜ | ⬜ | The intended resolution of any detected captcha, `solved`, `ignored`, or `rejected`, where `rejected` results in a `403` response; `solved` is the default captcha resolution |
-| `readiness` | ⬜ | ⬜ | The standard ready event to await before snapshotting browsed content, `load` or `domcontentloaded`; `load` is the default ready event |
-| `delay` | ⬜ | ⬜ | The number of supplemental seconds to delay before snapshotting browsed content, from `.1` to `10` inclusive; no delay is used by default |
-| `format` | ⬜ | ⬜ | The format to output to, `rendered`, `raw`, or `markdown` (see the [section below](#response-format)); `rendered` is the default format |
-| `expiration` | ⬜ | ⬜ | The age in days of when to consider cached content expired, where `0` disables caching; `1` is the default number of days before expiration |
+| Key | Required | Premium | Value |
+| :----------- | :------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `url` | ✅ | ⬜ | The URL of up to 2,047 characters of the page to browse; any unsafe characters require [URL encoding](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding) |
+| `difficulty` | ⬜ | ✅ | The difficulty pool to attempt to access the URL from, `low`, `medium`, or `high` (planned); `low` is the default difficulty |
+| `speed` | ⬜ | ✅ | The speed to attempt to access the URL at, `light`, `ridiculous`, or `ludicrous` (planned), where `ridiculous` is 30 percent faster on average than `light` speed; `light` is the default speed |
+| `device` | ⬜ | ⬜ | The name as returned by the [devices resource](#device-emulation) of the device to emulate browsing on (these names are case insensitive but must include form- or URL-encoded spaces and punctuation marks); device emulation is unused by default |
+| `session` | ⬜ | ⬜ | Any unique identifier of up to 255 characters (regardless of character encoding); **{companyName}** will make best efforts to route calls in the same session to the same egress node for up to 12 minutes |
+| `captcha` | ⬜ | ⬜ | The intended resolution of any detected captcha, `solved`, `ignored`, or `rejected`, where `rejected` results in a `403` response; `solved` is the default captcha resolution |
+| `readiness` | ⬜ | ⬜ | The standard ready event to await before snapshotting browsed content, `load` or `domcontentloaded`; `load` is the default ready event |
+| `delay` | ⬜ | ⬜ | The number of supplemental seconds to delay before snapshotting browsed content, from `.1` to `10` inclusive; no delay is used by default |
+| `format` | ⬜ | ⬜ | The format to output to, `rendered`, `raw`, or `markdown` (see the [section below](#response-format)); `rendered` is the default format |
+| `expiration` | ⬜ | ⬜ | The age in days of when to consider cached content expired, where `0` disables caching; `1` is the default number of days before expiration |
+| `callback` | ⬜ | ⬜ | The encoded HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN of up to 2,047 characters to notify when the (prerequisite) asynchronous content has been retrieved; any SQS queue must grant `sqs:SendMessage` permission to the `arn:aws:iam::180363035301:role/api-instance` AWS principal |
+
+### CDP interaction
+
+If your use case involves interacting with not just reading pages, you can connect
+[Puppeteer](https://pptr.dev/), [Playwright](https://playwright.dev/), or
+[another CDP-compatible automation framework](https://github.com/ChromeDevTools/awesome-chrome-devtools#libraries-for-driving-the-protocol-or-a-layer-above)
+to the browser service:
+
+
+
+
+
+
+
+The connection string takes geotargeting params and a subset of the [REST params](#rest-parameters),
+namely `difficulty`, `device`, and `session`:
+
+
+
+After connecting, most of your framework's API (see the [Puppeteer reference](https://pptr.dev/api),
+[Playwright reference](https://playwright.dev/docs/api/class-playwright), et cetera) will be
+available:
+
+
+
+CDP use is metered by time, so you should disconnect from the service when done by calling
+`browser.disconnect()` (Puppeteer), `browser.close()` (Playwright), or the equivalent.
+
+### Protocol mixture
+
+You can control costs by mixing REST requests and CDP use within a sticky browser session. E.g., you
+may want to prepare a page for interaction over REST then interact with the page over CDP:
+
+
+
+
+
+To hand off in reverse, from CDP to REST, call your disconnect method first.
### Device emulation
-The `device` param lets you fetch device-specific content, rather than the default desktop content.
+The `device` param lets you browse device-specific content, rather than the default desktop content.
For a list of supported smartphone and tablet devices, make a request with your API token and no
params to **https://{apiEndpoint}/browser/devices**:
@@ -81,24 +129,25 @@ The API will return JSON that contains an alphabetized array of device names:
]
```
-Then, pass your device name of choice into your browsing request:
+Then, pass your device name of choice into your browsing request or connection:
-### Sticky sessions
-
-To reuse a browser session across requests, set a cookie named `session`:
-
-
-
-The cookie value can be any unique identifier of up to **255 characters** (regardless of character
-encoding) and **{companyName}** will make best efforts to route calls in the same session to the
-same egress node for up to **12 minutes**.
+
### Response format
-Pages are returned as rendered HTML, raw (unrendered) HTML, or Markdown optimized for LLM prompts.
+Read-only pages are returned as rendered HTML, raw (unrendered) HTML, or Markdown optimized for LLM
+prompts.
## Additional examples
+### Cachebusting
+
+
+### End-to-end Playwright
+
+
+
+
diff --git a/web-render/geotargeting.mdx b/web-render/geotargeting.mdx
index 0647226..3250b37 100644
--- a/web-render/geotargeting.mdx
+++ b/web-render/geotargeting.mdx
@@ -16,11 +16,11 @@ Every HTTP request can be routed through one of more than 190 countries, by subd
-
+
-
+
diff --git a/web-render/openapi.json b/web-render/openapi.json
index 57cf824..7d67b6d 100644
--- a/web-render/openapi.json
+++ b/web-render/openapi.json
@@ -11,14 +11,34 @@
"/search": {
"get": {
"summary": "Results",
- "description": "Retrieve search results or queue results for later retrieval.",
+ "description": "Retrieve search results or queue a search for later retrieval.",
"parameters": [
+ {
+ "name": "id",
+ "description": "The identifier returned by this endpoint of the queued search to retrieve; if given, all other params are ignored; either an ID or terms are required.",
+ "schema": {
+ "type": "string",
+ "format": "uuid",
+ "example": "078fd246-f0f7-44a0-aabb-cadd7b12454f"
+ },
+ "in": "query"
+ },
{
"name": "terms",
- "description": "The broad or exact word or phrase of up to 255 characters to query (e.g., `foo bar baz` or `\"foo bar baz\"`); the terms require [form or URL encoding](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding) (with any space character being replaced by a `+` character or `%20` sequence).",
- "schema": { "type": "string", "example": "foo bar baz" },
- "in": "query",
- "required": true
+ "description": "The broad or exact word or phrase to query (e.g., `foo bar baz` or `\"foo bar baz\"`); the terms require [form or URL encoding](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding) (with any space character being replaced by a `+` character or `%20` sequence); either terms or an ID are required.",
+ "schema": { "type": "string", "maxLength": 255, "example": "foo bar baz" },
+ "in": "query"
+ },
+ {
+ "name": "engine",
+ "description": "The search engine to use.",
+ "schema": {
+ "type": "string",
+ "enum": ["google", "bing (coming soon)"],
+ "default": "google",
+ "example": "google"
+ },
+ "in": "query"
},
{
"name": "serps",
@@ -44,6 +64,12 @@
"schema": { "type": "integer", "minimum": 0, "maximum": 100, "example": null },
"in": "query"
},
+ {
+ "name": "device",
+ "description": "The name as returned by the [devices resource](search/devices) of the device to emulate searching on (these names are case insensitive but must include form- or URL-encoded spaces and punctuation marks); device emulation is unused by default.",
+ "schema": { "type": "string", "example": null },
+ "in": "query"
+ },
{
"name": "country",
"description": "The [two-letter ISO code](https://www.iso.org/obp/ui/#search/code) of the country to search from (these codes are case insensitive); a random country is used by default.",
@@ -86,7 +112,8 @@
"schema": {
"type": "string",
"format": "uri",
- "example": "https://www.google.com/search?q=foo+bar+baz&hl=fr&gl=fr&ie=UTF-8"
+ "maxLength": 2047,
+ "example": "https%3A%2F%2Fwww.google.com%2Fsearch%3Fq%3Dfoo%2Bbar%2Bbaz%26hl%3Dfr%26gl%3Dfr%26ie%3DUTF-8"
},
"in": "query"
},
@@ -104,16 +131,17 @@
},
{
"name": "mode",
- "description": "The synchronous or asynchronous mode of results retrieval (see the [async resource](search/results)).",
+ "description": "The synchronous or asynchronous mode of results retrieval (retrieve async results by providing the returned ID).",
"schema": { "$ref": "#/components/schemas/Mode" },
"in": "query"
},
{
"name": "callback",
- "description": "The HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN to notify when the (prerequisite) asynchronous results have been retrieved.",
+ "description": "The encoded HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN to notify when the (prerequisite) async results have been retrieved; any SQS queue must grant `sqs:SendMessage` permission to the `arn:aws:iam::180363035301:role/api-instance` AWS principal.",
"schema": {
"type": "string",
- "example": "https://webhook.site/02e249f8-1faf-4fab-bcf5-78ce683e85a8"
+ "maxLength": 2047,
+ "example": "https%3A%2F%2Fwebhook.site%2F02e249f8-1faf-4fab-bcf5-78ce683e85a8"
},
"in": "query"
}
@@ -124,20 +152,34 @@
"content": { "text/html": { "schema": { "type": "string" } } }
},
"202": {
- "description": "A job identifier.",
+ "description": "A job identifier or the search status.",
"content": {
"application/json": {
"schema": {
- "type": "object",
- "properties": {
- "id": {
- "description": "The identifier to retrieve the queued search with.",
- "type": "string",
- "format": "uuid",
- "example": "078fd246-f0f7-44a0-aabb-cadd7b12454f"
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "The identifier to retrieve the queued search with.",
+ "type": "string",
+ "format": "uuid",
+ "example": "078fd246-f0f7-44a0-aabb-cadd7b12454f"
+ }
+ },
+ "required": ["id"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "status": {
+ "description": "The status of the queued search.",
+ "allOf": [{ "$ref": "#/components/schemas/Status" }]
+ }
+ },
+ "required": ["status"]
}
- },
- "required": ["id"]
+ ]
}
}
}
@@ -155,7 +197,7 @@
"/search/results": {
"get": {
"summary": "Async results",
- "description": "Retrieve the results or status of a queued search.",
+ "description": "Retrieve the results or status of a queued search – use `GET /search?id=...`.",
"parameters": [
{
"name": "id",
@@ -171,12 +213,9 @@
],
"responses": {
"200": {
- "description": "The search results.",
- "content": { "text/html": { "schema": { "type": "string" } } }
- },
- "202": {
- "description": "The search status.",
+ "description": "The search results or status.",
"content": {
+ "text/html": { "schema": { "type": "string" } },
"application/json": {
"schema": {
"type": "object",
@@ -193,22 +232,247 @@
},
"401": { "$ref": "#/components/responses/authenticationError" },
"403": { "$ref": "#/components/responses/authorizationError" },
+ "402": { "$ref": "#/components/responses/creditsError" },
+ "422": { "$ref": "#/components/responses/paramsError" },
+ "500": { "$ref": "#/components/responses/unknownError" }
+ },
+ "deprecated": true
+ }
+ },
+ "/search/batches": {
+ "post": {
+ "summary": "Batch results",
+ "description": "Queue multiple searches for later retrieval.",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "terms": {
+ "description": "The broad or exact words or phrases to query (e.g., `foo bar baz` or `\"foo bar baz\"`).",
+ "type": "array",
+ "items": { "type": "string", "maxLength": 255 },
+ "minItems": 1,
+ "maxItems": 1000,
+ "example": ["foo bar baz"]
+ },
+ "engine": {
+ "description": "The search engine to use.",
+ "type": "string",
+ "enum": ["google", "bing (coming soon)"],
+ "default": "google",
+ "example": "google"
+ },
+ "serps": {
+ "description": "The number of results pages to fetch.",
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 10,
+ "default": 1,
+ "example": 1
+ },
+ "size": {
+ "description": "The number of results per page to fetch; page size is unset by default.",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 100,
+ "example": null
+ },
+ "offset": {
+ "description": "The number of initial results to skip; results aren’t offset by default.",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 100,
+ "example": null
+ },
+ "device": {
+ "description": "The name as returned by the [devices resource](devices) of the device to emulate searching on (these names are case insensitive); device emulation is unused by default.",
+ "type": "string",
+ "example": null
+ },
+ "country": {
+ "description": "The [two-letter ISO code](https://www.iso.org/obp/ui/#search/code) of the country to search from (these codes are case insensitive); a random country is used by default.",
+ "type": "string",
+ "example": null
+ },
+ "subdivision": {
+ "description": "The alphanumeric second part (proceeding the separator) of a [first-level subdivision code](https://www.iso.org/obp/ui/#search/code) in the (prerequisite) country to search from (these codes are case insensitive); a random subdivision is used by default.",
+ "type": "string",
+ "example": null
+ },
+ "city": {
+ "description": "The [commonly spelled name](https://www.geonames.org/) of the city in the (prerequisite) country to search from (these names are temporarily case sensitive); a random city is used by default.",
+ "type": "string",
+ "example": null
+ },
+ "uule": {
+ "description": "The [proprietary encoded string](https://moz.com/blog/geolocation-the-ultimate-tip-to-emulate-local-search) for emulating the location to search from; if possible, the actual location is searched from not emulated; location emulation is unused by default.",
+ "type": "string",
+ "example": null
+ },
+ "language": {
+ "description": "The commonly spelled name, [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php), or [Google code](https://www.google.com/advanced_search) of the language to search in (these names and codes are case insensitive); the language is unset by default.",
+ "type": "string",
+ "example": null
+ },
+ "display": {
+ "description": "The commonly spelled name, [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php), or [Google code](https://www.google.com/advanced_search) of the search interface’s display language (these names and codes are case insensitive); the display language is unset by default or set to the language if given.",
+ "type": "string",
+ "example": null
+ },
+ "urls": {
+ "description": "The URLs of the results pages to retrieve; if given, the URLs override any of the above params.",
+ "type": "array",
+ "items": { "type": "string", "format": "uri", "maxLength": 2047 },
+ "minItems": 1,
+ "maxItems": 1000,
+ "example": ["https://www.google.com/search?q=foo+bar+baz&hl=fr&gl=fr&ie=UTF-8"]
+ },
+ "awaiting": {
+ "description": "The lazy result features to wait to load; any AI overview, which can take significant time to generate, is awaited for a maximum of **1 minute**; multiple features can be provided by repeating the key; lazy results aren’t awaited by default.",
+ "type": "string",
+ "enum": ["ai", "answers", "ads"],
+ "example": "ai"
+ },
+ "expiration": {
+ "description": "The age in days of when to consider cached results expired, where `0` disables caching.",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 1,
+ "default": 1,
+ "example": 0
+ },
+ "callback": {
+ "description": "The HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN to notify when the batch results have been retrieved; any SQS queue must grant `sqs:SendMessage` permission to the `arn:aws:iam::180363035301:role/api-instance` AWS principal.",
+ "type": "string",
+ "maxLength": 2047,
+ "example": "https://webhook.site/02e249f8-1faf-4fab-bcf5-78ce683e85a8"
+ }
+ },
+ "anyOf": [{ "required": ["terms"] }, { "required": ["urls"] }]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "202": {
+ "description": "A job identifier and confirmation count.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "The identifier to retrieve the batch searches with.",
+ "type": "string",
+ "format": "uuid",
+ "example": "a0482596-c2ad-445a-a435-727002156a20"
+ },
+ "count": {
+ "description": "The number of queries in the batch as confirmed by the API.",
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 1000,
+ "example": 1
+ }
+ },
+ "required": ["id", "count"]
+ }
+ }
+ }
+ },
+ "401": { "$ref": "#/components/responses/authenticationError" },
+ "403": { "$ref": "#/components/responses/authorizationError" },
+ "402": { "$ref": "#/components/responses/creditsError" },
+ "422": { "$ref": "#/components/responses/paramsError" },
+ "500": { "$ref": "#/components/responses/unknownError" }
+ }
+ },
+ "get": {
+ "summary": "Batch results",
+ "description": "Retrieve the status of batch searches.",
+ "parameters": [
+ {
+ "name": "id",
+ "description": "The identifier returned by the [batch search endpoint](../batching) of the bulk searches to retrieve.",
+ "schema": {
+ "type": "string",
+ "format": "uuid",
+ "example": "a0482596-c2ad-445a-a435-727002156a20"
+ },
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The batch status.",
+ "content": {
+ "application/json": { "schema": { "$ref": "#/components/schemas/BatchStatus" } }
+ }
+ },
+ "401": { "$ref": "#/components/responses/authenticationError" },
+ "403": { "$ref": "#/components/responses/authorizationError" },
+ "402": { "$ref": "#/components/responses/creditsError" },
"422": { "$ref": "#/components/responses/paramsError" },
"500": { "$ref": "#/components/responses/unknownError" }
}
}
},
+ "/search/devices": {
+ "get": {
+ "summary": "Devices",
+ "description": "Retrieve supported device names.",
+ "responses": {
+ "200": {
+ "description": "The device names.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "array",
+ "items": { "type": "string" },
+ "example": [
+ "Blackberry PlayBook",
+ "Blackberry PlayBook landscape",
+ "BlackBerry Z30",
+ ""
+ ]
+ }
+ }
+ }
+ },
+ "401": { "$ref": "#/components/responses/authenticationError" },
+ "403": { "$ref": "#/components/responses/authorizationError" },
+ "402": { "$ref": "#/components/responses/creditsError" }
+ }
+ }
+ },
"/ai": {
"get": {
"summary": "Completion",
- "description": "Retrieve an LLM completion or queue a completion for later retrieval.",
+ "description": "Retrieve an LLM completion or queue a conversation for later retrieval.",
"parameters": [
+ {
+ "name": "id",
+ "description": "The identifier returned by this endpoint of the queued conversation to retrieve; if given, all other params are ignored; either an ID or prompt is required.",
+ "schema": {
+ "type": "string",
+ "format": "uuid",
+ "example": "1851dab8-4619-409f-893f-47dd3a180bc3"
+ },
+ "in": "query"
+ },
{
"name": "prompt",
- "description": "The question or instruction of up to 2,047 characters to use as a chat prompt; the prompt requires [form or URL encoding](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding) (with any space character being replaced by a `+` character or `%20` sequence).",
- "schema": { "type": "string", "example": "best basketball shoes for 2026" },
- "in": "query",
- "required": true
+ "description": "The question or instruction to use as a chat prompt; the prompt requires [form or URL encoding](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding) (with any space character being replaced by a `+` character or `%20` sequence); either a prompt or ID is required.",
+ "schema": {
+ "type": "string",
+ "maxLength": 2047,
+ "example": "best basketball shoes for 2026"
+ },
+ "in": "query"
},
{
"name": "model",
@@ -246,14 +510,14 @@
"in": "query"
},
{
- "name": "language (planned)",
- "description": "The commonly spelled name or [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php) of the language to chat in (these names and codes are case insensitive but required to include form- or URL-encoded spaces and punctuation marks); the language is unset by default.",
+ "name": "language",
+ "description": "[Planned] The commonly spelled name or [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php) of the language to chat in (these names and codes are case insensitive but required to include form- or URL-encoded spaces and punctuation marks); the language is unset by default.",
"schema": { "type": "string", "example": null },
"in": "query"
},
{
- "name": "display (planned)",
- "description": "The commonly spelled name or [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php) of the chat interface’s display language (these names and codes are case insensitive but required to include form- or URL-encoded spaces and punctuation marks); the display language is unset by default or set to the language if given.",
+ "name": "display",
+ "description": "[Planned] The commonly spelled name or [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php) of the chat interface’s display language (these names and codes are case insensitive but required to include form- or URL-encoded spaces and punctuation marks); the display language is unset by default or set to the language if given.",
"schema": { "type": "string", "example": null },
"in": "query"
},
@@ -262,7 +526,7 @@
"description": "The format to output to.",
"schema": {
"type": "string",
- "enum": ["json", "rendered", "raw"],
+ "enum": ["json", "rendered", "raw (planned)"],
"default": "json",
"example": "rendered"
},
@@ -276,9 +540,19 @@
},
{
"name": "mode",
- "description": "The synchronous or asynchronous mode of completion retrieval (see the [async resource](ai/completions)).",
+ "description": "The synchronous or asynchronous mode of completion retrieval (retrieve an async completion by providing the returned ID).",
"schema": { "$ref": "#/components/schemas/Mode" },
"in": "query"
+ },
+ {
+ "name": "callback",
+ "description": "The encoded HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN to notify when the (prerequisite) async completion has been retrieved; any SQS queue must grant `sqs:SendMessage` permission to the `arn:aws:iam::180363035301:role/api-instance` AWS principal.",
+ "schema": {
+ "type": "string",
+ "maxLength": 2047,
+ "example": "https%3A%2F%2Fwebhook.site%2F02e249f8-1faf-4fab-bcf5-78ce683e85a8"
+ },
+ "in": "query"
}
],
"responses": {
@@ -290,20 +564,34 @@
}
},
"202": {
- "description": "A job identifier.",
+ "description": "A job identifier or the conversation status.",
"content": {
"application/json": {
"schema": {
- "type": "object",
- "properties": {
- "id": {
- "description": "The identifier to retrieve the queued conversation with.",
- "type": "string",
- "format": "uuid",
- "example": "1851dab8-4619-409f-893f-47dd3a180bc3"
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "The identifier to retrieve the queued conversation with.",
+ "type": "string",
+ "format": "uuid",
+ "example": "1851dab8-4619-409f-893f-47dd3a180bc3"
+ }
+ },
+ "required": ["id"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "status": {
+ "description": "The status of the queued conversation.",
+ "allOf": [{ "$ref": "#/components/schemas/Status" }]
+ }
+ },
+ "required": ["status"]
}
- },
- "required": ["id"]
+ ]
}
}
}
@@ -320,8 +608,8 @@
},
"/ai/completions": {
"get": {
- "summary": "Async completions",
- "description": "Retrieve the completion or status of a queued conversation.",
+ "summary": "Async completion",
+ "description": "Retrieve the completion or status of a queued conversation – use `GET /ai?id=...`.",
"parameters": [
{
"name": "id",
@@ -337,31 +625,185 @@
],
"responses": {
"200": {
- "description": "The AI completion.",
+ "description": "The AI completion or status.",
"content": {
- "application/json": { "schema": { "$ref": "#/components/schemas/Completion" } },
+ "application/json": {
+ "schema": {
+ "oneOf": [
+ { "$ref": "#/components/schemas/Completion" },
+ {
+ "type": "object",
+ "properties": {
+ "status": {
+ "description": "The status of the queued conversation.",
+ "allOf": [{ "$ref": "#/components/schemas/Status" }]
+ }
+ },
+ "required": ["status"]
+ }
+ ]
+ }
+ },
"text/html": { "schema": { "type": "string" } }
}
},
+ "401": { "$ref": "#/components/responses/authenticationError" },
+ "403": { "$ref": "#/components/responses/authorizationError" },
+ "402": { "$ref": "#/components/responses/creditsError" },
+ "422": { "$ref": "#/components/responses/paramsError" },
+ "500": { "$ref": "#/components/responses/unknownError" }
+ },
+ "deprecated": true
+ }
+ },
+ "/ai/batches": {
+ "post": {
+ "summary": "Batch completions",
+ "description": "Queue multiple LLM conversations for later retrieval.",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "prompts": {
+ "description": "The questions or instructions to use as chat prompts.",
+ "type": "array",
+ "items": { "type": "string", "maxLength": 2047 },
+ "minItems": 1,
+ "maxItems": 1000,
+ "example": [
+ "Where can I buy a vintage guitar?",
+ "Help me find guitar repair shops nearby.",
+ "Do any local stores sell vinyl records?",
+ "[Additional prompts here]"
+ ]
+ },
+ "model": {
+ "description": "The AI model to chat with.",
+ "type": "string",
+ "enum": ["chatgpt", "gemini", "perplexity", "copilot"],
+ "default": "chatgpt",
+ "example": "gemini"
+ },
+ "device": {
+ "description": "The name as returned by the [devices resource](devices) of the device to emulate chatting on (these names are case insensitive but must include form- or URL-encoded spaces and punctuation marks); device emulation is unused by default.",
+ "type": "string",
+ "example": null
+ },
+ "country": {
+ "description": "The [two-letter ISO code](https://www.iso.org/obp/ui/#search/code) of the country to chat from (these codes are case insensitive); a random country is used by default.",
+ "type": "string",
+ "example": null
+ },
+ "subdivision": {
+ "description": "The alphanumeric second part (proceeding the separator) of a [first-level subdivision code](https://www.iso.org/obp/ui/#search/code) in the (prerequisite) country to chat from (these codes are case insensitive); a random subdivision is used by default.",
+ "type": "string",
+ "example": null
+ },
+ "city": {
+ "description": "The [commonly spelled name](https://www.geonames.org/) of the city in the (prerequisite) country to chat from (these names are temporarily case sensitive and required to include form- or URL-encoded spaces and punctuation marks); a random city is used by default.",
+ "type": "string",
+ "example": null
+ },
+ "language": {
+ "description": "[Planned] The commonly spelled name or [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php) of the language to chat in (these names and codes are case insensitive but required to include form- or URL-encoded spaces and punctuation marks); the language is unset by default.",
+ "type": "string",
+ "example": null
+ },
+ "display": {
+ "description": "[Planned] The commonly spelled name or [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php) of the chat interface’s display language (these names and codes are case insensitive but required to include form- or URL-encoded spaces and punctuation marks); the display language is unset by default or set to the language if given.",
+ "type": "string",
+ "example": null
+ },
+ "format": {
+ "description": "The format to output to.",
+ "type": "string",
+ "enum": ["json", "rendered", "raw (planned)"],
+ "default": "json",
+ "example": "rendered"
+ },
+ "expiration": {
+ "description": "The age in days of when to consider a cached completion expired, where `0` disables caching.",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 1,
+ "default": 1,
+ "example": 0
+ },
+ "callback": {
+ "description": "The HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN to notify when the batch completions have been retrieved; any SQS queue must grant `sqs:SendMessage` permission to the `arn:aws:iam::180363035301:role/api-instance` AWS principal.",
+ "type": "string",
+ "maxLength": 2047,
+ "example": "https://webhook.site/02e249f8-1faf-4fab-bcf5-78ce683e85a8"
+ }
+ },
+ "required": ["prompts"]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
"202": {
- "description": "The conversation status.",
+ "description": "A job identifier and confirmation count.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
- "status": {
- "description": "The status of the queued conversation.",
- "allOf": [{ "$ref": "#/components/schemas/Status" }]
+ "id": {
+ "description": "The identifier to retrieve the batch conversations with.",
+ "type": "string",
+ "format": "uuid",
+ "example": "a0482596-c2ad-445a-a435-727002156a20"
+ },
+ "count": {
+ "description": "The number of prompts in the batch as confirmed by the API.",
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 1000,
+ "example": 37
}
},
- "required": ["status"]
+ "required": ["id", "count"]
}
}
}
},
"401": { "$ref": "#/components/responses/authenticationError" },
"403": { "$ref": "#/components/responses/authorizationError" },
+ "402": { "$ref": "#/components/responses/creditsError" },
+ "422": { "$ref": "#/components/responses/paramsError" },
+ "500": { "$ref": "#/components/responses/unknownError" }
+ }
+ },
+ "get": {
+ "summary": "Batch completions",
+ "description": "Retrieve the status of batch conversations.",
+ "parameters": [
+ {
+ "name": "id",
+ "description": "The identifier returned by the [batch AI endpoint](../batching) of the bulk conversations to retrieve.",
+ "schema": {
+ "type": "string",
+ "format": "uuid",
+ "example": "a0482596-c2ad-445a-a435-727002156a20"
+ },
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The batch status.",
+ "content": {
+ "application/json": { "schema": { "$ref": "#/components/schemas/BatchStatus" } }
+ }
+ },
+ "401": { "$ref": "#/components/responses/authenticationError" },
+ "403": { "$ref": "#/components/responses/authorizationError" },
+ "402": { "$ref": "#/components/responses/creditsError" },
"422": { "$ref": "#/components/responses/paramsError" },
"500": { "$ref": "#/components/responses/unknownError" }
}
@@ -398,14 +840,33 @@
"/browser": {
"get": {
"summary": "Content",
- "description": "Retrieve web content or queue content for later retrieval.",
+ "description": "Retrieve web content or queue a request for later retrieval; connect to `wss://render.joinmassive.com/browser` for CDP interaction.",
+ "x-websocket": {
+ "url": "wss://render.joinmassive.com/browser",
+ "protocol": "Chrome DevTools Protocol",
+ "description": "Interact with webpages by connecting Puppeteer, Playwright, or another CDP-compatible automation framework."
+ },
"parameters": [
+ {
+ "name": "id",
+ "description": "The identifier returned by this endpoint of the queued request to retrieve; if given, all other params are ignored; either an ID or URL is required.",
+ "schema": {
+ "type": "string",
+ "format": "uuid",
+ "example": "21cb972e-0e0f-47bb-9ce9-65b99e9cee77"
+ },
+ "in": "query"
+ },
{
"name": "url",
- "description": "The URL of the page to browse; any unsafe characters require [URL encoding](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding).",
- "schema": { "type": "string", "format": "uri", "example": "https://example.com/" },
- "in": "query",
- "required": true
+ "description": "The URL of the page to browse; any unsafe characters require [URL encoding](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding); either a URL or ID is required.",
+ "schema": {
+ "type": "string",
+ "format": "uri",
+ "maxLength": 2047,
+ "example": "https://example.com/"
+ },
+ "in": "query"
},
{
"name": "difficulty",
@@ -500,9 +961,19 @@
},
{
"name": "mode",
- "description": "The synchronous or asynchronous mode of content retrieval (see the [async resource](browser/content)).",
+ "description": "The synchronous or asynchronous mode of content retrieval (retrieve async content by providing the returned ID).",
"schema": { "$ref": "#/components/schemas/Mode" },
"in": "query"
+ },
+ {
+ "name": "callback",
+ "description": "The encoded HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN to notify when the (prerequisite) async content has been retrieved; any SQS queue must grant `sqs:SendMessage` permission to the `arn:aws:iam::180363035301:role/api-instance` AWS principal.",
+ "schema": {
+ "type": "string",
+ "maxLength": 2047,
+ "example": "https%3A%2F%2Fwebhook.site%2F02e249f8-1faf-4fab-bcf5-78ce683e85a8"
+ },
+ "in": "query"
}
],
"responses": {
@@ -511,20 +982,34 @@
"content": { "text/html": { "schema": { "type": "string" } } }
},
"202": {
- "description": "A job identifier.",
+ "description": "A job identifier or the request status.",
"content": {
"application/json": {
"schema": {
- "type": "object",
- "properties": {
- "id": {
- "description": "The identifier to retrieve the queued request with.",
- "type": "string",
- "format": "uuid",
- "example": "21cb972e-0e0f-47bb-9ce9-65b99e9cee77"
+ "oneOf": [
+ {
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "The identifier to retrieve the queued request with.",
+ "type": "string",
+ "format": "uuid",
+ "example": "21cb972e-0e0f-47bb-9ce9-65b99e9cee77"
+ }
+ },
+ "required": ["id"]
+ },
+ {
+ "type": "object",
+ "properties": {
+ "status": {
+ "description": "The status of the queued request.",
+ "allOf": [{ "$ref": "#/components/schemas/Status" }]
+ }
+ },
+ "required": ["status"]
}
- },
- "required": ["id"]
+ ]
}
}
}
@@ -544,7 +1029,7 @@
"/browser/content": {
"get": {
"summary": "Async content",
- "description": "Retrieve the content or status of a queued request.",
+ "description": "Retrieve the content or status of a queued request – use `GET /browser?id=...`.",
"parameters": [
{
"name": "id",
@@ -560,12 +1045,9 @@
],
"responses": {
"200": {
- "description": "The request content.",
- "content": { "text/html": { "schema": { "type": "string" } } }
- },
- "202": {
- "description": "The request status.",
+ "description": "The request content or status.",
"content": {
+ "text/html": { "schema": { "type": "string" } },
"application/json": {
"schema": {
"type": "object",
@@ -582,6 +1064,179 @@
},
"401": { "$ref": "#/components/responses/authenticationError" },
"403": { "$ref": "#/components/responses/authorizationError" },
+ "402": { "$ref": "#/components/responses/creditsError" },
+ "422": { "$ref": "#/components/responses/paramsError" },
+ "500": { "$ref": "#/components/responses/unknownError" }
+ },
+ "deprecated": true
+ }
+ },
+ "/browser/batches": {
+ "post": {
+ "summary": "Batch content",
+ "description": "Queue multiple web requests for later retrieval.",
+ "requestBody": {
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "urls": {
+ "description": "The URLs of the pages to browse.",
+ "type": "array",
+ "items": { "type": "string", "format": "uri", "maxLength": 2047 },
+ "minItems": 1,
+ "maxItems": 1000,
+ "example": [
+ "https://guitars.com/",
+ "https://cartervintage.com/",
+ "https://musiccityvintageguitars.com/",
+ "[Additional URLs here]"
+ ]
+ },
+ "difficulty": {
+ "description": "The difficulty pool to attempt to access the URLs from.",
+ "type": "string",
+ "enum": ["low", "medium", "high"],
+ "default": "low",
+ "example": "low"
+ },
+ "speed": {
+ "description": "The speed to attempt to access the URLs at, where `ridiculous` is 30 percent faster on average than `light` speed.",
+ "type": "string",
+ "enum": ["light", "ridiculous", "ludicrous (planned)"],
+ "default": "light",
+ "example": "light"
+ },
+ "device": {
+ "description": "The name as returned by the [devices resource](devices) of the device to emulate browsing on (these names are case insensitive); device emulation is unused by default.",
+ "type": "string",
+ "example": null
+ },
+ "country": {
+ "description": "The [two-letter ISO code](https://www.iso.org/obp/ui/#search/code) of the country to browse from (these codes are case insensitive); a random country is used by default.",
+ "type": "string",
+ "example": null
+ },
+ "subdivision": {
+ "description": "The alphanumeric second part (proceeding the separator) of a [first-level subdivision code](https://www.iso.org/obp/ui/#search/code) in the (prerequisite) country to browse from (these codes are case insensitive); a random subdivision is used by default.",
+ "type": "string",
+ "example": null
+ },
+ "city": {
+ "description": "The [commonly spelled name](https://www.geonames.org/) of the city in the (prerequisite) country to browse from (these names are temporarily case sensitive); a random city is used by default.",
+ "type": "string",
+ "example": null
+ },
+ "captcha": {
+ "description": "The intended resolution of any detected captcha, where `rejected` results in a `403` response.",
+ "type": "string",
+ "enum": ["solved", "ignored", "rejected"],
+ "default": "solved",
+ "example": "rejected"
+ },
+ "readiness": {
+ "description": "The standard ready event to await before snapshotting browsed content.",
+ "type": "string",
+ "enum": ["load", "domcontentloaded"],
+ "default": "load",
+ "example": "load"
+ },
+ "delay": {
+ "description": "The number of supplemental seconds to delay before snapshotting browsed content; no delay is used by default.",
+ "type": "number",
+ "minimum": 0.1,
+ "maximum": 10,
+ "example": null
+ },
+ "format": {
+ "description": "The format to output to.",
+ "type": "string",
+ "enum": ["rendered", "raw", "markdown"],
+ "default": "rendered",
+ "example": "markdown"
+ },
+ "expiration": {
+ "description": "The age in days of when to consider cached content expired, where `0` disables caching.",
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 1,
+ "default": 1,
+ "example": 0
+ },
+ "callback": {
+ "description": "The HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN to notify when the batch content has been retrieved; any SQS queue must grant `sqs:SendMessage` permission to the `arn:aws:iam::180363035301:role/api-instance` AWS principal.",
+ "type": "string",
+ "maxLength": 2047,
+ "example": "https://webhook.site/02e249f8-1faf-4fab-bcf5-78ce683e85a8"
+ }
+ },
+ "required": ["urls"]
+ }
+ }
+ },
+ "required": true
+ },
+ "responses": {
+ "202": {
+ "description": "A job identifier and confirmation count.",
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "description": "The identifier to retrieve the batch content with.",
+ "type": "string",
+ "format": "uuid",
+ "example": "a0482596-c2ad-445a-a435-727002156a20"
+ },
+ "count": {
+ "description": "The number of requests in the batch as confirmed by the API.",
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 1000,
+ "example": 37
+ }
+ },
+ "required": ["id", "count"]
+ }
+ }
+ }
+ },
+ "401": { "$ref": "#/components/responses/authenticationError" },
+ "403": { "$ref": "#/components/responses/authorizationError" },
+ "402": { "$ref": "#/components/responses/creditsError" },
+ "422": { "$ref": "#/components/responses/paramsError" },
+ "500": { "$ref": "#/components/responses/unknownError" }
+ }
+ },
+ "get": {
+ "summary": "Batch content",
+ "description": "Retrieve the status of batch requests.",
+ "parameters": [
+ {
+ "name": "id",
+ "description": "The identifier returned by the [batch browser endpoint](../batching) of the bulk requests to retrieve.",
+ "schema": {
+ "type": "string",
+ "format": "uuid",
+ "example": "a0482596-c2ad-445a-a435-727002156a20"
+ },
+ "in": "query",
+ "required": true
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "The batch status.",
+ "content": {
+ "application/json": { "schema": { "$ref": "#/components/schemas/BatchStatus" } }
+ }
+ },
+ "401": { "$ref": "#/components/responses/authenticationError" },
+ "403": { "$ref": "#/components/responses/authorizationError" },
+ "402": { "$ref": "#/components/responses/creditsError" },
"422": { "$ref": "#/components/responses/paramsError" },
"500": { "$ref": "#/components/responses/unknownError" }
}
@@ -626,9 +1281,10 @@
"type": "object",
"properties": {
"email": {
- "description": "The email address of the user to create an account for; the address is case insensitive and can be up to 255 characters, regardless of character encoding.",
+ "description": "The email address of the user to create an account for; the address is case insensitive and can be in any character encoding.",
"type": "string",
"format": "email",
+ "maxLength": 255,
"example": "user@example.com"
},
"credits": {
@@ -1052,7 +1708,7 @@
"engine": {
"description": "The queried search engine.",
"type": "string",
- "enum": ["google"],
+ "enum": ["google", "bing (coming soon)"],
"example": "google"
},
"terms": {
@@ -1189,10 +1845,10 @@
"section": { "$ref": "#/components/schemas/Section" },
"feature": { "type": "integer", "minimum": 1 },
"position": { "type": "integer", "minimum": 1 },
- "url": { "type": "string" },
+ "url": { "type": "string", "format": "uri" },
"title": { "type": "string" },
"site": { "type": "string" },
- "visibleUrl": { "type": "string" },
+ "visibleUrl": { "type": "string", "format": "uri" },
"snippet": { "type": "string" },
"sitelinks": { "type": "array", "items": { "$ref": "#/components/schemas/Sitelink" } }
},
@@ -1245,7 +1901,7 @@
"type": "object",
"properties": {
"position": { "type": "integer", "minimum": 1 },
- "url": { "type": "string" },
+ "url": { "type": "string", "format": "uri" },
"title": { "type": "string" },
"snippet": { "type": "string" }
},
@@ -1291,13 +1947,13 @@
"type": "string",
"example": "
-
- Generate popular LLM completions.
-
-
- Unblock difficult-to-crawl websites.
-
-
- Extract top real-time search results.
-
-
-
-## Configuration
-
-Options that apply across every endpoint.
-
-
-
- Make requests from anywhere.
-
-
- Defer responses for later retrieval.
-
-
-
-## Managing your account
-
-
-
- Understand API credits and billing.
-
-
- Track API usage and performance.
-
-
-
-## API reference
-
-Prefer to jump straight to the spec? The [API reference](/web-render/reference/ai) documents
-every parameter, response field, and status code for each endpoint.
diff --git a/web-render/reference/ai/batches.mdx b/web-render/reference/ai/batches.mdx
new file mode 100644
index 0000000..7bb8f6f
--- /dev/null
+++ b/web-render/reference/ai/batches.mdx
@@ -0,0 +1,3 @@
+---
+openapi: post /ai/batches
+---
diff --git a/web-render/reference/ai/status.mdx b/web-render/reference/ai/status.mdx
new file mode 100644
index 0000000..2d1e3a0
--- /dev/null
+++ b/web-render/reference/ai/status.mdx
@@ -0,0 +1,3 @@
+---
+openapi: get /ai/batches
+---
diff --git a/web-render/reference/browser/batches.mdx b/web-render/reference/browser/batches.mdx
new file mode 100644
index 0000000..62c66f8
--- /dev/null
+++ b/web-render/reference/browser/batches.mdx
@@ -0,0 +1,3 @@
+---
+openapi: post /browser/batches
+---
diff --git a/web-render/reference/browser/status.mdx b/web-render/reference/browser/status.mdx
new file mode 100644
index 0000000..1a2bd92
--- /dev/null
+++ b/web-render/reference/browser/status.mdx
@@ -0,0 +1,3 @@
+---
+openapi: get /browser/batches
+---
diff --git a/web-render/reference/search/batches.mdx b/web-render/reference/search/batches.mdx
new file mode 100644
index 0000000..949e2ca
--- /dev/null
+++ b/web-render/reference/search/batches.mdx
@@ -0,0 +1,3 @@
+---
+openapi: post /search/batches
+---
diff --git a/web-render/reference/search/devices.mdx b/web-render/reference/search/devices.mdx
new file mode 100644
index 0000000..6846d93
--- /dev/null
+++ b/web-render/reference/search/devices.mdx
@@ -0,0 +1,3 @@
+---
+openapi: get /search/devices
+---
diff --git a/web-render/reference/search/status.mdx b/web-render/reference/search/status.mdx
new file mode 100644
index 0000000..ba759ff
--- /dev/null
+++ b/web-render/reference/search/status.mdx
@@ -0,0 +1,3 @@
+---
+openapi: get /search/batches
+---
diff --git a/web-render/scheduling.mdx b/web-render/scheduling.mdx
index cac403f..03390c6 100644
--- a/web-render/scheduling.mdx
+++ b/web-render/scheduling.mdx
@@ -6,9 +6,9 @@ description: Defer responses for later retrieval.
---
import { apiEndpoint } from '/snippets/whitelabel/config.mdx';
-import Search from '/snippets/whitelabel/search/mode.mdx';
-import Ai from '/snippets/whitelabel/ai/mode.mdx';
-import Browser from '/snippets/whitelabel/browser/mode.mdx';
+import Terms from '/snippets/whitelabel/search/mode.mdx';
+import Prompt from '/snippets/whitelabel/ai/mode.mdx';
+import Url from '/snippets/whitelabel/browser/mode.mdx';
import Results from '/snippets/whitelabel/search/id.mdx';
import Completion from '/snippets/whitelabel/ai/id.mdx';
import Content from '/snippets/whitelabel/browser/id.mdx';
@@ -17,11 +17,17 @@ import Content from '/snippets/whitelabel/browser/id.mdx';
An API call that includes the `mode=async` key-value pair will be satisfied asynchronously:
-
+
-
+
+
+
+
-
+
+
+
+
In response, the API will provide a JSON job identifier:
@@ -29,23 +35,20 @@ In response, the API will provide a JSON job identifier:
{ "id": "078fd246-f0f7-44a0-aabb-cadd7b12454f" }
```
-```json
-{ "id": "1851dab8-4619-409f-893f-47dd3a180bc3" }
-```
-
-```json
-{ "id": "21cb972e-0e0f-47bb-9ce9-65b99e9cee77" }
-```
-
-The job results, completion, or content or job status can be requested at any time thereafter by
-passing the identifier to **https://{apiEndpoint}/search/results**,
-**https://{apiEndpoint}/ai/completions**, or **https://{apiEndpoint}/browser/content**:
+The job results, completion, or content or job status can be requested any time thereafter by
+passing back the ID:
-
+
+
+
+
-
+
+
+
+
The results, completion, or content, if ready, will be returned in the same format as synchronous results, a
@@ -45,16 +47,41 @@ The keys and values **{companyName}** supports are as follows:
| Key | Required | Value |
| :----------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `terms` | ✅ | The broad or exact word or phrase of up to 255 characters to query (e.g., `foo bar baz` or `"foo bar baz"`); the terms require [form or URL encoding](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding) (with any space character being replaced by a `+` character or `%20` sequence) |
+| `engine` | ⬜ | The search engine to use, `google` or `bing` (coming soon); `google` is the default engine |
| `serps` | ⬜ | The number of results pages to retrieve, from `1` to `10` inclusive; `1` is the default count |
| `size` | ⬜ | The number of results per page to retrieve, from `0` to `100` inclusive; page size is unset by default |
| `offset` | ⬜ | The number of initial results to skip, from `0` to `100` inclusive; results aren't offset by default |
+| `device` | ⬜ | The name as returned by the [devices resource](#device-emulation) of the device to emulate searching on (these names are case insensitive but must include form- or URL-encoded spaces and punctuation marks); device emulation is unused by default |
| `uule` | ⬜ | The [proprietary encoded string](https://moz.com/blog/geolocation-the-ultimate-tip-to-emulate-local-search) for emulating the location to search from; if possible, the actual location is searched from not emulated; location emulation is unused by default |
| `language` | ⬜ | The commonly spelled name, [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php), or [Google code](https://www.google.com/advanced_search) of the language to search in (these names and codes are case insensitive but required to include form- or URL-encoded spaces and punctuation marks); the language is unset by default |
| `display` | ⬜ | The commonly spelled name, [two-letter ISO code](https://www.loc.gov/standards/iso639-2/php/code_list.php), or [Google code](https://www.google.com/advanced_search) of the search interface's display language (these names and codes are case insensitive but required to include form- or URL-encoded spaces and punctuation marks); the display language is unset by default or set to the language if given |
-| `url` | ⬜ | The encoded URL of the results page to retrieve; if given, the URL overrides any of the above params |
+| `url` | ⬜ | The encoded URL of up to 2,047 characters of the results page to retrieve; if given, the URL overrides any of the above params |
| `awaiting` | ⬜ | The lazy result features to wait to load, an `ai` overview, "People also ask" `answers`, or sponsored `ads`; any AI overview, which can take significant time to generate, is awaited for a maximum of **1 minute**; multiple features can be provided by repeating the key; lazy results aren't awaited by default |
| `expiration` | ⬜ | The age in days of when to consider cached results expired, where `0` disables caching; `1` is the default number of days before expiration |
-| `callback` | ⬜ | The HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN to notify when the (prerequisite) asynchronous results have been retrieved |
+| `callback` | ⬜ | The encoded HTTP or HTTPS callback URL or Amazon SQS queue URL or ARN of up to 2,047 characters to notify when the (prerequisite) asynchronous results have been retrieved; any SQS queue must grant `sqs:SendMessage` permission to the `arn:aws:iam::180363035301:role/api-instance` AWS principal |
+
+### Device emulation
+
+The `device` param lets you fetch device-specific results, rather than the default desktop results.
+For a list of supported smartphone and tablet devices, make a request with your API token and no
+params to **https://{apiEndpoint}/search/devices**:
+
+
+
+The API will return JSON that contains an alphabetized array of device names:
+
+```json
+[
+ "Blackberry PlayBook",
+ "Blackberry PlayBook landscape",
+ "BlackBerry Z30",
+ "[Remaining device names here]"
+]
+```
+
+Then, pass your device name of choice into your search request:
+
+
### Response format
@@ -64,10 +91,18 @@ feature infinite scrolling, the HTML of each page will be separated from the nex
## Additional examples
+### Results omission
+
+### Alternative URL
+
+### Lazy results
+
+### Push notification
+