Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ and this project adheres to

### Added

- Add Markdown search and archive output support with `getMd` and
`getMdBySearchId`.
- Expose `EngineParameters` type.
- Expose `InvalidArgumentError` error.

Expand Down
111 changes: 103 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
[![License](https://img.shields.io/github/license/serpapi/serpapi-javascript)](https://github.com/serpapi/serpapi-javascript/blob/master/LICENSE)
[![SerpApi Libraries](https://img.shields.io/badge/SerpApi-Libraries-blue)](https://serpapi.com/integrations)

Scrape and parse search engine results using [SerpApi](https://serpapi.com). Get
search results from Google, Bing, Baidu, Yandex, Yahoo, Home Depot, eBay and
more.
Scrape and parse search engine results using [SerpApi](https://serpapi.com).
Retrieve structured JSON, token-efficient Markdown for AI agents, or raw HTML
from Google, Bing, Baidu, Yandex, Yahoo, Home Depot, eBay and more.

| 🪧 Coming from `google-search-results-nodejs`? <br /> Check out the [migration document](https://github.com/serpapi/serpapi-javascript/blob/master/docs/migrating_from_google_search_results_nodejs.md) to find out how to upgrade. |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Expand Down Expand Up @@ -94,8 +94,27 @@ console.log(response);
[Deno](https://deno.land/x/serpapi).
- Promises and async/await support.
- Callbacks support.
- JSON, HTML, and token-efficient Markdown response formats.
- [Examples in JavaScript/TypeScript on Node.js/Deno using ESM/CommonJS, and more](https://github.com/serpapi/serpapi-javascript/tree/master/examples).

## Markdown output for AI agents

Use `getMd` to get token-efficient Markdown optimized for LLMs and AI agents:

```js
import { getMd } from "serpapi";

const markdown = await getMd({
engine: "google",
api_key: API_KEY,
q: "coffee",
});
```

Archived results are also available as Markdown with `getMdBySearchId`.

Learn more about [SerpApi Markdown output](https://serpapi.com/markdown-output).

## Configuration

You can declare a global `api_key` and `timeout` value by modifying the `config`
Expand Down Expand Up @@ -176,21 +195,27 @@ for a manual approach:
- [getHtml](#gethtml)
- [Parameters](#parameters-1)
- [Examples](#examples-1)
- [getJsonBySearchId](#getjsonbysearchid)
- [getMd](#getmd)
- [Parameters](#parameters-2)
- [Examples](#examples-2)
- [getHtmlBySearchId](#gethtmlbysearchid)
- [getJsonBySearchId](#getjsonbysearchid)
- [Parameters](#parameters-3)
- [Examples](#examples-3)
- [getAccount](#getaccount)
- [getHtmlBySearchId](#gethtmlbysearchid)
- [Parameters](#parameters-4)
- [Examples](#examples-4)
- [getLocations](#getlocations)
- [getMdBySearchId](#getmdbysearchid)
- [Parameters](#parameters-5)
- [Examples](#examples-5)
- [uploadImage](#uploadimage)
- [getAccount](#getaccount)
- [Parameters](#parameters-6)
- [Examples](#examples-6)
- [getLocations](#getlocations)
- [Parameters](#parameters-7)
- [Examples](#examples-7)
- [uploadImage](#uploadimage)
- [Parameters](#parameters-8)
- [Examples](#examples-8)

### getJson

Expand Down Expand Up @@ -237,6 +262,31 @@ const html = await getHtml({ engine: "google", api_key: API_KEY, q: "coffee" });
getHtml({ engine: "google", api_key: API_KEY, q: "coffee" }, console.log);
```

### getMd

Get a Markdown response based on search parameters.

#### Parameters

- `parameters`
**[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)**
search query parameters for the engine
- `callback` **fn?** optional callback

#### Examples

```javascript
// async/await
const markdown = await getMd({
engine: "google",
api_key: API_KEY,
q: "coffee",
});

// callback
getMd({ engine: "google", api_key: API_KEY, q: "coffee" }, console.log);
```

### getJsonBySearchId

Get a JSON response given a search ID.
Expand Down Expand Up @@ -328,6 +378,51 @@ const html = await getHtmlBySearchId(id, { api_key: API_KEY });
getHtmlBySearchId(id, { api_key: API_KEY }, console.log);
```

### getMdBySearchId

Get a Markdown response given a search ID.

- This search ID can be obtained from the `search_metadata.id` key in the
response.
- Typically used together with the `async` parameter.
- Accepts an optional callback.

#### Parameters

- `searchId`
**[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)**
search ID
- `parameters`
**[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)**
(optional, default `{}`)

- `parameters.api_key`
**[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)?**
API key
- `parameters.timeout`
**[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
timeout in milliseconds
- `callback` **fn?** optional callback

#### Examples

```javascript
const markdown = await getMd({
engine: "google",
api_key: API_KEY,
q: "coffee",
});
const idMatch = markdown.match(/^ id:\s*(.+)$/m);
if (!idMatch) throw new Error("Search ID missing from Markdown frontmatter");
const searchId = idMatch[1].trim();

// async/await
const archivedMarkdown = await getMdBySearchId(searchId, { api_key: API_KEY });

// callback
getMdBySearchId(searchId, { api_key: API_KEY }, console.log);
```

### getAccount

Get account information of an API key. <https://serpapi.com/account-api>
Expand Down
2 changes: 2 additions & 0 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,7 @@ export {
getJson,
getJsonBySearchId,
getLocations,
getMd,
getMdBySearchId,
uploadImage,
} from "./src/serpapi.ts";
15 changes: 15 additions & 0 deletions smoke_tests/commonjs/commonjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ const {
config,
getJson,
getHtml,
getMd,
getJsonBySearchId,
getHtmlBySearchId,
getMdBySearchId,
getAccount,
getLocations,
} = require("serpapi");
Expand Down Expand Up @@ -89,6 +91,12 @@ const run = async () => {
});
}

{
console.log("getMd");
const markdown = await getMd(Object.assign({ engine: "google" }, params));
if (!markdown.startsWith("---")) throw new Error("Incorrect Markdown");
}

{
console.log("getJsonBySearchId");
config.api_key = apiKey;
Expand All @@ -111,6 +119,13 @@ const run = async () => {
});
}

{
console.log("getMdBySearchId");
config.api_key = apiKey;
const markdown = await getMdBySearchId(searchId);
if (!markdown.startsWith("---")) throw new Error("Incorrect Markdown");
}

{
console.log("getAccount");
config.api_key = apiKey;
Expand Down
15 changes: 15 additions & 0 deletions smoke_tests/esm/esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
getJson,
getJsonBySearchId,
getLocations,
getMd,
getMdBySearchId,
} from "serpapi";

Dotenv.config();
Expand Down Expand Up @@ -92,6 +94,12 @@ let searchId;
});
}

{
console.log("getMd");
const markdown = await getMd(Object.assign({ engine: "google" }, params));
if (!markdown.startsWith("---")) throw new Error("Incorrect Markdown");
}

{
console.log("getJsonBySearchId");
config.api_key = apiKey;
Expand All @@ -114,6 +122,13 @@ let searchId;
});
}

{
console.log("getMdBySearchId");
config.api_key = apiKey;
const markdown = await getMdBySearchId(searchId);
if (!markdown.startsWith("---")) throw new Error("Incorrect Markdown");
}

{
console.log("getAccount");
config.api_key = apiKey;
Expand Down
124 changes: 124 additions & 0 deletions src/serpapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,89 @@ async function _getHtml(
return html;
}

/**
* Get Markdown response based on search parameters.
*
* @param {object} parameters Search query parameters for the engine. Refer to https://serpapi.com/search-api for parameter explanations.
* @param {fn=} callback Optional callback.
* @example
* // async/await
* const markdown = await getMd({ engine: "google", api_key: API_KEY, q: "coffee" });
*
* // callback
* getMd({ engine: "google", api_key: API_KEY, q: "coffee" }, console.log);
*/
export function getMd(
parameters: EngineParameters,
callback?: (markdown: string) => void,
): Promise<string>;

/**
* Get Markdown response based on search parameters.
*
* @param {string} engine Engine name. Refer to https://serpapi.com/search-api for valid engines.
* @param {object} parameters Search query parameters for the engine. Refer to https://serpapi.com/search-api for parameter explanations.
* @param {fn=} callback Optional callback.
* @example
* // async/await
* const markdown = await getMd("google", { api_key: API_KEY, q: "coffee" });
*
* // callback
* getMd("google", { api_key: API_KEY, q: "coffee" }, console.log);
*/
export function getMd(
engine: string,
parameters: EngineParameters,
callback?: (markdown: string) => void,
): Promise<string>;

export function getMd(
...args:
| [
parameters: EngineParameters,
callback?: (markdown: string) => void,
]
| [
engine: string,
parameters: EngineParameters,
callback?: (markdown: string) => void,
]
): Promise<string> {
if (typeof args[0] === "string" && typeof args[1] === "object") {
const [engine, parameters, callback] = args;
const newParameters = { ...parameters, engine } as EngineParameters;
return _getMd(newParameters, callback);
} else if (
typeof args[0] === "object" &&
typeof args[1] !== "object" &&
(typeof args[1] === "undefined" || typeof args[1] === "function")
) {
const [parameters, callback] = args;
return _getMd(parameters, callback);
} else {
throw new InvalidArgumentError();
}
}

async function _getMd(
parameters: EngineParameters,
callback?: (markdown: string) => void,
): Promise<string> {
const key = validateApiKey(parameters.api_key, true);
const timeout = validateTimeout(parameters.timeout);
const markdown = await _internals.execute(
SEARCH_PATH,
{
...parameters,
api_key: key,
output: "md",
},
timeout,
);
callback?.(markdown);
return markdown;
}

/**
* Get a JSON response given a search ID.
* - This search ID can be obtained from the `search_metadata.id` key in the response.
Expand Down Expand Up @@ -259,6 +342,47 @@ export async function getHtmlBySearchId(
return html;
}

/**
* Get a Markdown response given a search ID.
* - This search ID can be obtained from the `search_metadata.id` key in the response.
* - Typically used together with the `async` parameter.
*
* @param {string} searchId Search ID.
* @param {object} parameters
* @param {string=} [parameters.api_key] API key.
* @param {number=} [parameters.timeout] Timeout in milliseconds.
* @param {fn=} callback Optional callback.
* @example
* const markdown = await getMd({ engine: "google", api_key: API_KEY, q: "coffee" });
* const idMatch = markdown.match(/^ id:\s*(.+)$/m);
* if (!idMatch) throw new Error("Search ID missing from Markdown frontmatter");
* const searchId = idMatch[1].trim();
*
* // async/await
* const archivedMarkdown = await getMdBySearchId(searchId, { api_key: API_KEY });
*
* // callback
* getMdBySearchId(searchId, { api_key: API_KEY }, console.log);
*/
export async function getMdBySearchId(
searchId: string,
parameters: GetBySearchIdParameters = {},
callback?: (markdown: string) => void,
) {
const key = validateApiKey(parameters.api_key);
const timeout = validateTimeout(parameters.timeout);
const markdown = await _internals.execute(
`${SEARCH_ARCHIVE_PATH}/${searchId}`,
{
api_key: key,
output: "md",
},
timeout,
);
callback?.(markdown);
return markdown;
}

/**
* Get account information of an API key.
*
Expand Down
Loading
Loading