talordata-serp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -0
- package/package.json +30 -0
- package/src/index.js +184 -0
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# TalorData JavaScript SDK
|
|
2
|
+
|
|
3
|
+
A lightweight JavaScript SDK for the TalorData SERP API.
|
|
4
|
+
|
|
5
|
+
Repository: [Talordata/talordata-serp-javascript](https://github.com/Talordata/talordata-serp-javascript)
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install talordata-serp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
const { Client } = require("talordata-serp");
|
|
17
|
+
|
|
18
|
+
async function main() {
|
|
19
|
+
const client = new Client({ apiToken: process.env.TALORDATA_API_TOKEN });
|
|
20
|
+
|
|
21
|
+
const result = await client.search({
|
|
22
|
+
engine: "google",
|
|
23
|
+
q: "car",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
console.log(result.search_metadata?.status);
|
|
27
|
+
console.log(result.search_information?.query_displayed);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
main().catch(console.error);
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
- `examples/basic-search.js`: basic `json=1` search example.
|
|
36
|
+
- `examples/json-plus-html.js`: `json=2` example with both HTML and parsed JSON.
|
|
37
|
+
- `examples/html-output.js`: `json=3` example that prints HTML output.
|
|
38
|
+
|
|
39
|
+
You can also set the token once in your shell:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
export TALORDATA_API_TOKEN=your_token
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Then use the top-level helper:
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
const talordata = require("talordata-serp");
|
|
49
|
+
|
|
50
|
+
async function main() {
|
|
51
|
+
const result = await talordata.search({
|
|
52
|
+
engine: "google",
|
|
53
|
+
q: "car",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
console.log(result);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
main().catch(console.error);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API Design
|
|
63
|
+
|
|
64
|
+
- `new Client({ apiToken })`: create a reusable client.
|
|
65
|
+
- `client.search(...)`: send a form-encoded `POST` request to `/serp/v1/request` and return the parsed result.
|
|
66
|
+
- `client.searchJson(...)`: alias of `search(...)`.
|
|
67
|
+
- `client.searchHtml(...)`: return the HTML string for `json=3`.
|
|
68
|
+
- `client.rawSearch(...)`: return the raw HTTP response body.
|
|
69
|
+
|
|
70
|
+
## JSON Modes
|
|
71
|
+
|
|
72
|
+
- `json=1`: returns parsed JSON data. This is the default mode used by `client.search(...)`.
|
|
73
|
+
- `json=2`: returns both `html` and `json`. The SDK automatically parses `data.json` into an object when possible.
|
|
74
|
+
- `json=3`: returns HTML. The SDK unwraps the HTML string from the API response.
|
|
75
|
+
|
|
76
|
+
## Example With URL
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
const { Client } = require("talordata-serp");
|
|
80
|
+
|
|
81
|
+
async function main() {
|
|
82
|
+
const client = new Client({ apiToken: process.env.TALORDATA_API_TOKEN });
|
|
83
|
+
|
|
84
|
+
const result = await client.search({
|
|
85
|
+
url: "https://www.google.com/search",
|
|
86
|
+
q: "car",
|
|
87
|
+
json: 1,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
console.log(result);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
main().catch(console.error);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Notes
|
|
97
|
+
|
|
98
|
+
- Auth uses the `Authorization: Bearer <token>` header.
|
|
99
|
+
- Requests are sent as `application/x-www-form-urlencoded`.
|
|
100
|
+
- Boolean params are normalized to `"1"` and `"0"` before sending.
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "talordata-serp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "JavaScript SDK for TalorData SERP API",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"files": [
|
|
8
|
+
"src"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node --test"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": ">=18"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"talordata",
|
|
18
|
+
"serp",
|
|
19
|
+
"sdk",
|
|
20
|
+
"search"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/Talordata/talordata-serp-javascript.git"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
const DEFAULT_BASE_URL = "https://serpapi.talordata.net";
|
|
2
|
+
const REQUEST_PATH = "/serp/v1/request";
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
4
|
+
|
|
5
|
+
class TalorDataError extends Error {}
|
|
6
|
+
|
|
7
|
+
class APITokenNotProvidedError extends TalorDataError {
|
|
8
|
+
constructor() {
|
|
9
|
+
super("Please provide `apiToken` or set the `TALORDATA_API_TOKEN` environment variable.");
|
|
10
|
+
this.name = "APITokenNotProvidedError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class HTTPError extends TalorDataError {
|
|
15
|
+
constructor(statusCode, message, body = null) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = "HTTPError";
|
|
18
|
+
this.statusCode = statusCode;
|
|
19
|
+
this.body = body;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function resolveToken(providedToken) {
|
|
24
|
+
return (
|
|
25
|
+
providedToken ||
|
|
26
|
+
process.env.TALORDATA_API_TOKEN ||
|
|
27
|
+
process.env.TALORDATA_SERP_API_TOKEN ||
|
|
28
|
+
null
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function buildUrl(baseUrl, path) {
|
|
33
|
+
if (path.startsWith("http://") || path.startsWith("https://")) {
|
|
34
|
+
return path;
|
|
35
|
+
}
|
|
36
|
+
const normalizedBaseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
37
|
+
return `${normalizedBaseUrl}${path.startsWith("/") ? path : `/${path}`}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeParams(params = {}) {
|
|
41
|
+
const normalized = {};
|
|
42
|
+
for (const [key, value] of Object.entries(params)) {
|
|
43
|
+
if (value === undefined || value === null) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (typeof value === "boolean") {
|
|
47
|
+
normalized[key] = value ? "1" : "0";
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
normalized[key] = String(value);
|
|
51
|
+
}
|
|
52
|
+
return normalized;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function unwrapPayload(payload) {
|
|
56
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) {
|
|
57
|
+
return payload;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "data")) {
|
|
61
|
+
return payload;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const data = payload.data;
|
|
65
|
+
if (typeof data === "string") {
|
|
66
|
+
return data;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
70
|
+
const nextData = { ...data };
|
|
71
|
+
if (typeof nextData.json === "string") {
|
|
72
|
+
try {
|
|
73
|
+
nextData.json = JSON.parse(nextData.json);
|
|
74
|
+
} catch (_) {
|
|
75
|
+
// Keep the raw string when the backend does not return valid JSON.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return nextData;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return payload;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function fromHttpResponse(response) {
|
|
85
|
+
const text = await response.text();
|
|
86
|
+
try {
|
|
87
|
+
const payload = JSON.parse(text);
|
|
88
|
+
return unwrapPayload(payload);
|
|
89
|
+
} catch (_) {
|
|
90
|
+
return text;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
class Client {
|
|
95
|
+
constructor({ apiToken, apiKey, token, timeout = DEFAULT_TIMEOUT_MS, baseUrl = DEFAULT_BASE_URL } = {}) {
|
|
96
|
+
this.apiToken = resolveToken(apiToken || apiKey || token);
|
|
97
|
+
this.timeout = timeout;
|
|
98
|
+
this.baseUrl = baseUrl;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async request(method, path, params = {}, { apiToken, headers = {}, signal } = {}) {
|
|
102
|
+
const token = resolveToken(apiToken || this.apiToken);
|
|
103
|
+
if (!token) {
|
|
104
|
+
throw new APITokenNotProvidedError();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const requestHeaders = {
|
|
108
|
+
Authorization: `Bearer ${token}`,
|
|
109
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
110
|
+
Accept: "application/json, text/html;q=0.9, */*;q=0.8",
|
|
111
|
+
"User-Agent": "talordata-serp-javascript/0.1.0",
|
|
112
|
+
...headers,
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const init = {
|
|
116
|
+
method: method.toUpperCase(),
|
|
117
|
+
headers: requestHeaders,
|
|
118
|
+
body: new URLSearchParams(normalizeParams(params)),
|
|
119
|
+
signal: signal || AbortSignal.timeout(this.timeout),
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const response = await fetch(buildUrl(this.baseUrl, path), init);
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
const body = await response.text();
|
|
125
|
+
throw new HTTPError(response.status, `HTTP ${response.status}`, body);
|
|
126
|
+
}
|
|
127
|
+
return response;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async search(params = {}, options = {}) {
|
|
131
|
+
const payload = { ...params };
|
|
132
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "json")) {
|
|
133
|
+
payload.json = "1";
|
|
134
|
+
}
|
|
135
|
+
const response = await this.request("POST", REQUEST_PATH, payload, options);
|
|
136
|
+
return fromHttpResponse(response);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async searchJson(params = {}, options = {}) {
|
|
140
|
+
return this.search(params, options);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async searchHtml(params = {}, options = {}) {
|
|
144
|
+
const payload = { ...params, json: "3" };
|
|
145
|
+
const response = await this.request("POST", REQUEST_PATH, payload, options);
|
|
146
|
+
const result = await fromHttpResponse(response.clone());
|
|
147
|
+
if (typeof result === "string") {
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
150
|
+
return response.text();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async rawSearch(params = {}, options = {}) {
|
|
154
|
+
const response = await this.request("POST", REQUEST_PATH, params, options);
|
|
155
|
+
return response.text();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function search(params = {}, options = {}) {
|
|
160
|
+
return new Client().search(params, options);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function searchJson(params = {}, options = {}) {
|
|
164
|
+
return new Client().searchJson(params, options);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function searchHtml(params = {}, options = {}) {
|
|
168
|
+
return new Client().searchHtml(params, options);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function rawSearch(params = {}, options = {}) {
|
|
172
|
+
return new Client().rawSearch(params, options);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = {
|
|
176
|
+
APITokenNotProvidedError,
|
|
177
|
+
Client,
|
|
178
|
+
HTTPError,
|
|
179
|
+
TalorDataError,
|
|
180
|
+
rawSearch,
|
|
181
|
+
search,
|
|
182
|
+
searchHtml,
|
|
183
|
+
searchJson,
|
|
184
|
+
};
|