Skip to content
Open
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
103 changes: 99 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
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,18 +195,24 @@ 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)
- [getAccount](#getaccount)
- [Parameters](#parameters-6)
- [Examples](#examples-6)
- [getLocations](#getlocations)
- [Parameters](#parameters-7)
- [Examples](#examples-7)

### getJson

Expand Down Expand Up @@ -234,6 +259,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 @@ -325,6 +375,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 response = await getJson({
engine: "google",
api_key: API_KEY,
async: true,
q: "coffee",
});
const { id } = response.search_metadata;
await delay(1000); // wait for the request to be processed.

// async/await
const markdown = await getMdBySearchId(id, { api_key: API_KEY });

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

### getAccount

Get account information of an API key. <https://serpapi.com/account-api>
Expand Down
7 changes: 7 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
"test:cov": "rm -rf cov_profile && deno task test --coverage=cov_profile && deno coverage cov_profile",
"npm": "deno run -A scripts/build_npm.ts"
},
"imports": {
"@deno/dnt": "https://deno.land/x/dnt@0.40.0/mod.ts",
"@std/dotenv": "https://deno.land/std@0.173.0/dotenv/mod.ts",
"@std/testing/asserts": "https://deno.land/std@0.170.0/testing/asserts.ts",
"@std/testing/bdd": "https://deno.land/std@0.170.0/testing/bdd.ts",
"@std/testing/mock": "https://deno.land/std@0.170.0/testing/mock.ts"
},
"fmt": {
"exclude": ["npm/", "examples/node", "smoke_tests/"]
},
Expand Down
2 changes: 1 addition & 1 deletion examples/deno/basic_example.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { loadSync } from "https://deno.land/std@0.173.0/dotenv/mod.ts";
import { loadSync } from "@std/dotenv";
import { config, getJson } from "../../mod.ts";

const { API_KEY: apiKey } = loadSync();
Expand Down
2 changes: 1 addition & 1 deletion examples/deno/pagination_example.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { loadSync } from "https://deno.land/std@0.173.0/dotenv/mod.ts";
import { loadSync } from "@std/dotenv";
import { config, getJson } from "../../mod.ts";

const { API_KEY: apiKey } = loadSync();
Expand Down
2 changes: 2 additions & 0 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ export {
getJson,
getJsonBySearchId,
getLocations,
getMd,
getMdBySearchId,
} from "./src/serpapi.ts";
2 changes: 1 addition & 1 deletion scripts/build_npm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { build, emptyDir } from "https://deno.land/x/dnt@0.40.0/mod.ts";
import { build, emptyDir } from "@deno/dnt";
import { version } from "../version.ts";

await emptyDir("./npm");
Expand Down
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
123 changes: 123 additions & 0 deletions src/serpapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,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 @@ -256,6 +339,46 @@ 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 response = await getJson({ engine: "google", api_key: API_KEY, async: true, q: "coffee" });
* const { id } = response.search_metadata;
* await delay(1000); // wait for the request to be processed.
*
* // async/await
* const markdown = await getMdBySearchId(id, { api_key: API_KEY });
*
* // callback
* getMdBySearchId(id, { 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