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.
Files changed (3) hide show
  1. package/README.md +100 -0
  2. package/package.json +30 -0
  3. 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
+ };