@spyglasshq/sdk 0.1.0 → 0.1.2

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 (77) hide show
  1. package/README.md +248 -66
  2. package/dist/cjs/BaseClient.js +3 -3
  3. package/dist/cjs/Client.d.ts +4 -4
  4. package/dist/cjs/Client.js +3 -3
  5. package/dist/cjs/api/errors/BadRequestError.d.ts +3 -3
  6. package/dist/cjs/api/errors/ContentTooLargeError.d.ts +3 -3
  7. package/dist/cjs/api/errors/ForbiddenError.d.ts +3 -3
  8. package/dist/cjs/api/errors/InternalServerError.d.ts +3 -3
  9. package/dist/cjs/api/errors/MethodNotAllowedError.d.ts +3 -3
  10. package/dist/cjs/api/errors/NotFoundError.d.ts +3 -3
  11. package/dist/cjs/api/errors/PaymentRequiredError.d.ts +3 -3
  12. package/dist/cjs/api/errors/TooManyRequestsError.d.ts +3 -3
  13. package/dist/cjs/api/errors/UnauthorizedError.d.ts +3 -3
  14. package/dist/cjs/api/resources/account/client/Client.d.ts +9 -9
  15. package/dist/cjs/api/resources/account/client/Client.js +15 -15
  16. package/dist/cjs/api/resources/indexing/client/Client.d.ts +19 -19
  17. package/dist/cjs/api/resources/indexing/client/Client.js +31 -31
  18. package/dist/cjs/api/resources/search/client/Client.d.ts +93 -89
  19. package/dist/cjs/api/resources/search/client/Client.js +153 -149
  20. package/dist/cjs/api/resources/search/client/requests/GetBrandLandingPagesRequest.d.ts +2 -2
  21. package/dist/cjs/api/resources/search/client/requests/GetBrandMediaInsightsRequest.d.ts +2 -2
  22. package/dist/cjs/api/resources/search/client/requests/{GetMediaV2Request.d.ts → GetMediaRequest.d.ts} +13 -13
  23. package/dist/cjs/api/resources/search/client/requests/{GetMediaV2Request.js → GetMediaRequest.js} +14 -14
  24. package/dist/{esm/api/resources/search/client/requests/SearchBrandsV2Request.d.mts → cjs/api/resources/search/client/requests/SearchBrandsRequest.d.ts} +4 -4
  25. package/dist/cjs/api/resources/search/client/requests/{SearchBrandsV2Request.js → SearchBrandsRequest.js} +5 -5
  26. package/dist/cjs/api/resources/search/client/requests/index.d.ts +2 -2
  27. package/dist/cjs/api/resources/search/client/requests/index.js +5 -5
  28. package/dist/cjs/api/resources/search/types/GetAccountResponse.d.ts +2 -2
  29. package/dist/cjs/api/resources/search/types/{GetMediaV2Response.d.ts → GetMediaResponse.d.ts} +3 -3
  30. package/dist/cjs/api/resources/search/types/{GetMediaV2Response.js → GetMediaResponse.js} +5 -5
  31. package/dist/{esm/api/resources/search/types/SearchBrandsV2Response.d.mts → cjs/api/resources/search/types/SearchBrandsResponse.d.ts} +3 -3
  32. package/dist/cjs/api/resources/search/types/{SearchBrandsV2Response.js → SearchBrandsResponse.js} +5 -5
  33. package/dist/cjs/api/resources/search/types/index.d.ts +2 -2
  34. package/dist/cjs/api/resources/search/types/index.js +2 -2
  35. package/dist/cjs/index.d.ts +2 -2
  36. package/dist/cjs/index.js +3 -3
  37. package/dist/cjs/version.d.ts +1 -1
  38. package/dist/cjs/version.js +1 -1
  39. package/dist/esm/BaseClient.mjs +3 -3
  40. package/dist/esm/Client.d.mts +4 -4
  41. package/dist/esm/Client.mjs +1 -1
  42. package/dist/esm/api/errors/BadRequestError.d.mts +3 -3
  43. package/dist/esm/api/errors/ContentTooLargeError.d.mts +3 -3
  44. package/dist/esm/api/errors/ForbiddenError.d.mts +3 -3
  45. package/dist/esm/api/errors/InternalServerError.d.mts +3 -3
  46. package/dist/esm/api/errors/MethodNotAllowedError.d.mts +3 -3
  47. package/dist/esm/api/errors/NotFoundError.d.mts +3 -3
  48. package/dist/esm/api/errors/PaymentRequiredError.d.mts +3 -3
  49. package/dist/esm/api/errors/TooManyRequestsError.d.mts +3 -3
  50. package/dist/esm/api/errors/UnauthorizedError.d.mts +3 -3
  51. package/dist/esm/api/resources/account/client/Client.d.mts +9 -9
  52. package/dist/esm/api/resources/account/client/Client.mjs +15 -15
  53. package/dist/esm/api/resources/indexing/client/Client.d.mts +19 -19
  54. package/dist/esm/api/resources/indexing/client/Client.mjs +31 -31
  55. package/dist/esm/api/resources/search/client/Client.d.mts +93 -89
  56. package/dist/esm/api/resources/search/client/Client.mjs +153 -149
  57. package/dist/esm/api/resources/search/client/requests/GetBrandLandingPagesRequest.d.mts +2 -2
  58. package/dist/esm/api/resources/search/client/requests/GetBrandMediaInsightsRequest.d.mts +2 -2
  59. package/dist/esm/api/resources/search/client/requests/{GetMediaV2Request.d.mts → GetMediaRequest.d.mts} +13 -13
  60. package/dist/esm/api/resources/search/client/requests/{GetMediaV2Request.mjs → GetMediaRequest.mjs} +13 -13
  61. package/dist/{cjs/api/resources/search/client/requests/SearchBrandsV2Request.d.ts → esm/api/resources/search/client/requests/SearchBrandsRequest.d.mts} +4 -4
  62. package/dist/esm/api/resources/search/client/requests/{SearchBrandsV2Request.mjs → SearchBrandsRequest.mjs} +4 -4
  63. package/dist/esm/api/resources/search/client/requests/index.d.mts +2 -2
  64. package/dist/esm/api/resources/search/client/requests/index.mjs +2 -2
  65. package/dist/esm/api/resources/search/types/GetAccountResponse.d.mts +2 -2
  66. package/dist/esm/api/resources/search/types/{GetMediaV2Response.d.mts → GetMediaResponse.d.mts} +3 -3
  67. package/dist/esm/api/resources/search/types/{GetMediaV2Response.mjs → GetMediaResponse.mjs} +4 -4
  68. package/dist/{cjs/api/resources/search/types/SearchBrandsV2Response.d.ts → esm/api/resources/search/types/SearchBrandsResponse.d.mts} +3 -3
  69. package/dist/esm/api/resources/search/types/{SearchBrandsV2Response.mjs → SearchBrandsResponse.mjs} +4 -4
  70. package/dist/esm/api/resources/search/types/index.d.mts +2 -2
  71. package/dist/esm/api/resources/search/types/index.mjs +2 -2
  72. package/dist/esm/index.d.mts +2 -2
  73. package/dist/esm/index.mjs +2 -2
  74. package/dist/esm/version.d.mts +1 -1
  75. package/dist/esm/version.mjs +1 -1
  76. package/package.json +17 -2
  77. package/reference.md +31 -27
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <div align="center">
2
- <img src="./assets/wordmark.svg" alt="Spyglass" width="360">
2
+ <img src="https://raw.githubusercontent.com/spyglass-so/spyglass-typescript/main/assets/wordmark.svg" alt="Spyglass" width="360">
3
3
 
4
4
  <h3>The API layer for the social internet</h3>
5
5
  <p>Official TypeScript & Node.js SDK for the Spyglass API — search brands, creators, and the ads they run.</p>
@@ -18,7 +18,7 @@
18
18
  <br>
19
19
 
20
20
  <div align="center">
21
- <a href="./LICENSE">
21
+ <a href="https://github.com/spyglass-so/spyglass-typescript/blob/main/LICENSE">
22
22
  <img src="https://img.shields.io/github/license/spyglass-so/spyglass-typescript" alt="License">
23
23
  </a>
24
24
  <a href="https://github.com/spyglass-so/spyglass-typescript/stargazers">
@@ -34,7 +34,32 @@
34
34
 
35
35
  ---
36
36
 
37
- Official TypeScript client for the [Spyglass API](https://docs.spyglass.so). Server-side only. Looking for Python? See [`spyglass-so/spyglass-python`](https://github.com/spyglass-so/spyglass-python).
37
+ **`@spyglasshq/sdk`** is the official TypeScript and Node.js client for the [Spyglass API](https://docs.spyglass.so). Resolve brands and creators, search across millions of Meta, Instagram, and TikTok creatives with natural-language briefs or structured filters, and extract hooks, personas, landing pages, and public comments.
38
+
39
+ > Looking for Python? See [`spyglass-so/spyglass-python`](https://github.com/spyglass-so/spyglass-python) (`pip install spyglass-sdk`).
40
+
41
+ ## Table of Contents
42
+
43
+ - [Installation](#installation)
44
+ - [Quickstart](#quickstart)
45
+ - [Core Workflows](#core-workflows)
46
+ - [Search Brands](#1-search-brands)
47
+ - [Search Ad Creatives & Social Media](#2-search-ad-creatives--social-media)
48
+ - [Discover Creators](#3-discover-creators)
49
+ - [Creative Insights & Landing Pages](#4-creative-insights--landing-pages)
50
+ - [Post Comments](#5-post-comments)
51
+ - [Index a New Brand](#6-index-a-new-brand)
52
+ - [Check Team Credit Usage](#7-check-team-credit-usage)
53
+ - [API Reference Overview](#api-reference-overview)
54
+ - [TypeScript Types](#typescript-types)
55
+ - [Error Handling](#error-handling)
56
+ - [Advanced Configuration](#advanced-configuration)
57
+ - [Retries, Timeouts & Abort Signals](#retries-timeouts--abort-signals)
58
+ - [Raw Response & Headers](#raw-response--headers)
59
+ - [Tree-Shakeable Subpackage Imports](#tree-shakeable-subpackage-imports)
60
+ - [Logging](#logging)
61
+ - [Runtime Compatibility](#runtime-compatibility)
62
+ - [Billing & Conventions](#billing--conventions)
38
63
 
39
64
  ## Installation
40
65
 
@@ -42,21 +67,26 @@ Official TypeScript client for the [Spyglass API](https://docs.spyglass.so). Ser
42
67
  npm install @spyglasshq/sdk
43
68
  # or
44
69
  pnpm add @spyglasshq/sdk
70
+ # or
71
+ yarn add @spyglasshq/sdk
72
+ # or
73
+ bun add @spyglasshq/sdk
45
74
  ```
46
75
 
47
76
  ## Quickstart
48
77
 
49
- Create an API key in [Settings → API](https://app.spyglass.so). Keys start with `sg_live_` and are shown once.
78
+ 1. Create an API key in **[Settings → API](https://app.spyglass.so)**. Keys start with `sg_live_` and are shown once.
79
+ 2. Initialize `Spyglass` and make your first call:
50
80
 
51
81
  ```ts
52
- import { SpyglassClient } from "@spyglasshq/sdk";
82
+ import { Spyglass } from "@spyglasshq/sdk";
53
83
 
54
- const client = new SpyglassClient({
84
+ const client = new Spyglass({
55
85
  token: process.env.SPYGLASS_API_KEY!,
56
86
  });
57
87
 
58
- // 1. Resolve a brand
59
- const { brands } = await client.search.searchBrandsV2({
88
+ // 1. Resolve a brand by name
89
+ const { brands } = await client.search.brands({
60
90
  names: ["Nike"],
61
91
  limit: 1,
62
92
  });
@@ -64,142 +94,294 @@ const { brands } = await client.search.searchBrandsV2({
64
94
  const brand = brands[0];
65
95
 
66
96
  if (brand) {
67
- // 2. Pull the creatives that brand is running
68
- const { media } = await client.search.getMediaV2({
97
+ // 2. Search the creatives that brand is running
98
+ const { media } = await client.search.getMedia({
69
99
  brandId: brand.id,
70
- query: "running shoes comfort",
71
- limit: 20,
100
+ query: "marathon running shoes comfort",
101
+ limit: 10,
72
102
  });
73
103
 
74
104
  for (const ad of media) {
75
- console.log(ad.id, ad.hook, ad.landingPageUrl);
105
+ console.log({
106
+ id: ad.id,
107
+ hook: ad.hook,
108
+ coreAngle: ad.coreAngle,
109
+ targetPersona: ad.targetPersona,
110
+ daysRun: ad.daysRun,
111
+ url: ad.url,
112
+ });
76
113
  }
77
114
  }
78
115
  ```
79
116
 
80
- ## What You Can Query
117
+ ---
118
+
119
+ ## Core Workflows
120
+
121
+ ### 1. Search Brands
122
+
123
+ Search advertisers by name (`names`), social handle (`igUsername`, `tiktokUsername`), known IDs (`brandIds`), or a natural-language brief (`query`). You can also combine `names` + `query` to rank a specific set of brands against a brief.
124
+
125
+ ```ts
126
+ // Fuzzy name lookup
127
+ const byName = await client.search.brands({
128
+ names: ["Athletic Greens", "AG1"],
129
+ limit: 5,
130
+ });
131
+
132
+ // Natural-language discovery
133
+ const byBrief = await client.search.brands({
134
+ query: "DTC electrolyte and hydration powder brands",
135
+ limit: 10,
136
+ });
137
+ ```
138
+
139
+ ### 2. Search Ad Creatives & Social Media
140
+
141
+ Pass `brandId` to inspect a specific brand or creator's creatives, or omit `brandId` and pass `query` to search across the entire Spyglass index. Combine semantic search with structured filters:
142
+
143
+ ```ts
144
+ // Optional: look up valid product taxonomy paths first (free call)
145
+ const { taxonomies } = await client.search.taxonomies({
146
+ query: "running shoes",
147
+ });
148
+
149
+ const { media, count } = await client.search.getMedia({
150
+ query: "UGC founder story explaining product benefits",
151
+ channel: ["META_AD", "TIKTOK"],
152
+ format: ["VIDEO"],
153
+ status: "ACTIVE",
154
+ taxonomy: taxonomies.slice(0, 2),
155
+ minDaysRun: 14,
156
+ limit: 20,
157
+ offset: 0,
158
+ });
159
+ ```
160
+
161
+ Each item in `media` includes creative intelligence extracted by Spyglass:
162
+ - **Copy & Strategy:** `hook`, `summary`, `coreAngle`, `targetPersona`, `painPoints`, `sellingPoints`
163
+ - **Creative Metadata:** `type` (`VIDEO`, `IMAGE`, `CAROUSEL`, `DPA`, `DCO`), `durationInSeconds`, `url`, `urls`, `thumbnailUrl`, `ugcCharacter`
164
+ - **Distribution & Performance:** `platforms`, `startAt`, `endAt`, `daysRun`, `viewCount`, `likeCount`, `commentCount`, `impressionPercentile`
165
+
166
+ ### 3. Discover Creators
167
+
168
+ Search creators by handle, name, or natural-language brief, and filter by demographic and profile attributes (`gender`, `ageRange`, `ethnicity`, `location`, `language`):
169
+
170
+ ```ts
171
+ const { creators } = await client.search.creators({
172
+ query: "skincare routine and dermatology tips",
173
+ gender: "female",
174
+ ageRange: ["25-34"],
175
+ location: ["US"],
176
+ language: ["en"],
177
+ limit: 20,
178
+ });
179
+ ```
180
+
181
+ ### 4. Creative Insights & Landing Pages
182
+
183
+ Pull clustered creative themes (`HOOK`, `USP`, `PERSONA`, `MEDIA_MIX`) and ranked destination landing pages for any brand:
184
+
185
+ ```ts
186
+ // Ranked hooks + weekly volume breakdown over the last 90 days
187
+ const insights = await client.search.getBrandMediaInsights({
188
+ id: brand.id,
189
+ type: "HOOK",
190
+ maxDaysBack: 90,
191
+ limit: 10,
192
+ });
193
+
194
+ // Top landing pages where the brand sends ad traffic
195
+ const { landingPages } = await client.search.getBrandLandingPages({
196
+ id: brand.id,
197
+ showInactive: "false",
198
+ limit: 20,
199
+ });
200
+ ```
201
+
202
+ ### 5. Post Comments
81
203
 
82
- | Method | Endpoint | What it answers |
83
- | --- | --- | --- |
84
- | `client.search.searchBrandsV2(...)` | `POST /brands` | Which brand is this? Who else is in this space? |
85
- | `client.search.creators(...)` | `POST /creators` | Which creator is this? Who matches this audience profile? |
86
- | `client.search.getAccount(...)` | `GET /account/{id}` | What do we know about a brand or creator ID you already have? |
87
- | `client.search.getMediaV2(...)` | `POST /media` | What creatives is this account running, or what's out there for this theme? |
88
- | `client.search.taxonomies(...)` | `GET /taxonomies` | Which product-category paths match this phrase? |
89
- | `client.search.getBrandMediaInsights(...)` | `GET /account/{id}/insights` | What hooks, USPs, personas, and formats does it lean on? |
90
- | `client.search.getBrandLandingPages(...)` | `GET /account/{id}/landing-pages` | Where does its ad traffic go? |
91
- | `client.search.getComments(...)` | `POST /comments` | What are people saying on this Instagram or TikTok post? |
92
- | `client.indexing.indexBrand(...)` | `POST /account/index` | Index a brand we don't have yet by website or social URL. |
93
- | `client.indexing.indexBrandStatus(...)` | `GET /account/{id}/index-status` | Has that brand finished indexing? |
94
- | `client.account.getCreditUsage()` | `GET /team/credit-usage` | How many monthly credits does this team have left? |
204
+ Fetch public comments from Instagram posts/reels (by shortcode or URL) and TikTok videos (by video ID or URL):
95
205
 
96
- Full parameters and response schemas are documented in [`reference.md`](./reference.md) and at [docs.spyglass.so](https://docs.spyglass.so).
206
+ ```ts
207
+ const { posts } = await client.search.getComments({
208
+ instagramIds: ["https://www.instagram.com/reel/C2xY9abcD1e/"],
209
+ tiktokIds: ["https://www.tiktok.com/@nike/video/7234567890123456789"],
210
+ limit: 1, // comment pages per post
211
+ });
212
+ ```
97
213
 
98
- ## Indexing a New Brand
214
+ ### 6. Index a New Brand
99
215
 
100
- If Spyglass doesn't have a brand yet, queue it by website or social profile URL and poll until `status` is `"ready"`:
216
+ If Spyglass doesn't have a brand yet, submit its website or social profile URL (`POST /account/index` is free) and poll until `status` is `"ready"`:
101
217
 
102
218
  ```ts
103
219
  const indexed = await client.indexing.indexBrand({
104
- websiteUrl: "https://www.nike.com",
220
+ websiteUrl: "https://www.on.com",
105
221
  });
106
222
 
107
- if (indexed.status === "indexing") {
108
- let status = indexed.status;
109
- while (status === "indexing") {
110
- await new Promise((r) => setTimeout(r, 3000));
111
- const check = await client.indexing.indexBrandStatus({ id: indexed.brandId });
112
- status = check.status;
113
- }
223
+ // "already_exists" can be queried immediately; "indexing" queues a background sync
224
+ let status = indexed.status;
225
+ while (status === "indexing") {
226
+ await new Promise((resolve) => setTimeout(resolve, 3000));
227
+ const check = await client.indexing.indexBrandStatus({ id: indexed.brandId });
228
+ status = check.status;
229
+ }
230
+
231
+ if (status === "ready" || status === "already_exists") {
232
+ const account = await client.search.getAccount({ id: indexed.brandId });
233
+ console.log("Indexed account:", account.name);
114
234
  }
115
235
  ```
116
236
 
117
- ## Request & Response Types
237
+ ### 7. Check Team Credit Usage
118
238
 
119
- All request and response types are exported under the `Spyglass` namespace:
239
+ Check your team's remaining monthly credits and current billing window (free call):
120
240
 
121
241
  ```ts
122
- import { Spyglass } from "@spyglasshq/sdk";
242
+ const usage = await client.account.getCreditUsage();
243
+ console.log(`Remaining: ${usage.remainingCredits} / ${usage.planCredits}`);
244
+ ```
123
245
 
124
- const request: Spyglass.SearchBrandsV2Request = {
125
- names: ["Nike"],
126
- limit: 5,
246
+ ---
247
+
248
+ ## API Reference Overview
249
+
250
+ | SDK Method | HTTP Endpoint | Cost | Rate Limit |
251
+ | --- | --- | --- | --- |
252
+ | `client.search.brands(req)` | `POST /brands` | $0.0020 / brand returned | 120 / min |
253
+ | `client.search.creators(req)` | `POST /creators` | $0.0020 / creator returned | 120 / min |
254
+ | `client.search.getAccount({ id })` | `GET /account/{id}` | $0.0020 / account returned | 120 / min |
255
+ | `client.search.getMedia(req)` | `POST /media` | $0.0010 / creative returned | 120 / min |
256
+ | `client.search.taxonomies({ query })` | `GET /taxonomies` | Free | 120 / min |
257
+ | `client.search.getBrandMediaInsights(req)` | `GET /account/{id}/insights` | $0.0010 / cluster returned | 120 / min |
258
+ | `client.search.getBrandLandingPages(req)` | `GET /account/{id}/landing-pages` | $0.0050 / landing page returned | 120 / min |
259
+ | `client.search.getComments(req)` | `POST /comments` | $0.0100 / live page fetched | 20 / min |
260
+ | `client.indexing.indexBrand(req)` | `POST /account/index` | Free | 50 / min |
261
+ | `client.indexing.indexBrandStatus({ id })` | `GET /account/{id}/index-status` | Free | 300 / min |
262
+ | `client.account.getCreditUsage()` | `GET /team/credit-usage` | Free | 120 / min |
263
+
264
+ Full method signatures and parameter types are available in [`reference.md`](https://github.com/spyglass-so/spyglass-typescript/blob/main/reference.md) and at [docs.spyglass.so](https://docs.spyglass.so).
265
+
266
+ ---
267
+
268
+ ## TypeScript Types
269
+
270
+ All request and response types are exported under the `spyglass` namespace:
271
+
272
+ ```ts
273
+ import { spyglass } from "@spyglasshq/sdk";
274
+
275
+ const request: spyglass.GetMediaRequest = {
276
+ query: "summer sale",
277
+ channel: ["META_AD"],
278
+ limit: 25,
127
279
  };
280
+
281
+ type MediaItem = spyglass.GetMediaResponse.Media.Item;
128
282
  ```
129
283
 
130
284
  ## Error Handling
131
285
 
132
- Non-2xx responses throw a `SpyglassError` with status code, parsed body, and raw response metadata:
286
+ When the API returns a non-2xx status code (`400`, `401`, `402` out of credits, `404`, `429` rate limit, or `5xx`), the SDK throws a `SpyglassError` (or a status-specific subclass such as `PaymentRequiredError` or `TooManyRequestsError`):
133
287
 
134
288
  ```ts
135
289
  import { SpyglassError } from "@spyglasshq/sdk";
136
290
 
137
291
  try {
138
- await client.search.searchBrandsV2({ names: ["Nike"], limit: 5 });
292
+ await client.search.brands({ names: ["Nike"], limit: 5 });
139
293
  } catch (err) {
140
294
  if (err instanceof SpyglassError) {
141
- console.error(err.statusCode, err.message, err.body);
295
+ console.error("Status:", err.statusCode);
296
+ console.error("Message:", err.message);
297
+ console.error("Body:", err.body);
142
298
  }
143
299
  }
144
300
  ```
145
301
 
146
- ## Advanced Options
302
+ ---
303
+
304
+ ## Advanced Configuration
147
305
 
148
- ### Retries & Timeouts
306
+ ### Retries, Timeouts & Abort Signals
149
307
 
150
- Requests default to a 60-second timeout and up to 2 automatic retries with exponential backoff on `408`, `429`, and `5xx` responses. Override per request:
308
+ The SDK defaults to a **60-second timeout** and **2 automatic retries** with exponential backoff on `408`, `429`, and `5xx` responses. You can configure these on the client or override them per request:
151
309
 
152
310
  ```ts
153
- const response = await client.search.searchBrandsV2(
154
- { names: ["Nike"], limit: 5 },
311
+ const controller = new AbortController();
312
+
313
+ const response = await client.search.getMedia(
314
+ { query: "protein bar", limit: 10 },
155
315
  {
156
316
  timeoutInSeconds: 30,
157
317
  maxRetries: 3,
158
318
  abortSignal: controller.signal,
319
+ headers: { "X-Custom-Header": "value" },
159
320
  },
160
321
  );
161
322
  ```
162
323
 
163
324
  ### Raw Response & Headers
164
325
 
165
- Use `.withRawResponse()` to inspect HTTP headers (such as rate-limit or credit headers):
326
+ Chain `.withRawResponse()` on any request promise to inspect HTTP status codes and headers alongside parsed data:
166
327
 
167
328
  ```ts
168
329
  const { data, rawResponse } = await client.search
169
- .searchBrandsV2({ names: ["Nike"], limit: 1 })
330
+ .brands({ names: ["Nike"], limit: 1 })
170
331
  .withRawResponse();
171
332
 
172
333
  console.log(data.brands);
173
- console.log(rawResponse.headers);
334
+ console.log(rawResponse.headers.get("retry-after"));
174
335
  ```
175
336
 
176
- ### Subpackage Imports
337
+ ### Tree-Shakeable Subpackage Imports
177
338
 
178
- Import individual resource clients directly for smaller bundle sizes:
339
+ Import individual resource clients directly for minimal bundle sizes:
179
340
 
180
341
  ```ts
181
342
  import { SearchClient } from "@spyglasshq/sdk/search";
343
+ import { IndexingClient } from "@spyglasshq/sdk/indexing";
344
+ import { AccountClient } from "@spyglasshq/sdk/account";
345
+ ```
346
+
347
+ ### Logging
348
+
349
+ Enable debug or info logging on the client:
350
+
351
+ ```ts
352
+ import { Spyglass, logging } from "@spyglasshq/sdk";
353
+
354
+ const client = new Spyglass({
355
+ token: process.env.SPYGLASS_API_KEY!,
356
+ logging: {
357
+ level: logging.LogLevel.Debug,
358
+ logger: new logging.ConsoleLogger(),
359
+ silent: false,
360
+ },
361
+ });
182
362
  ```
183
363
 
184
364
  ### Runtime Compatibility
185
365
 
186
- - Node.js 18+
187
- - Bun 1.0+
188
- - Deno v1.25+
189
- - Vercel / Cloudflare Workers
366
+ Zero external runtime dependencies. Works out of the box in:
367
+ - **Node.js** 18+
368
+ - **Bun** 1.0+
369
+ - **Deno** v1.25+
370
+ - **Vercel Functions** & **Cloudflare Workers**
190
371
 
191
372
  ## Billing & Conventions
192
373
 
193
- - **Credits:** Most calls spend monthly credits per row returned (`count` in the response). Always bound requests with `limit`. See [Billing docs](https://docs.spyglass.so/documentation/get-started/billing).
194
- - **Pagination:** Page through results with `offset` and `limit`. `count` is the number of rows in the current response.
195
- - **Server-to-server:** The API does not enable browser CORS; call it from your backend or agent runtime.
374
+ - **Credits per row:** Most endpoints charge against your monthly plan credits per item returned (`count` in the response). Always bound requests with `limit`. Semantic `query` searches on `/media`, `/brands`, and `/creators` run a language model and bill a minimum of 1 row even when 0 rows match. See [Billing docs](https://docs.spyglass.so/documentation/get-started/billing).
375
+ - **Pagination:** Page through results with `offset` and `limit`. `count` is the number of rows in *this* response (not the total across all pages). Stop paging when `count < limit`.
376
+ - **Server-side only:** The Spyglass API is designed for backend services, data pipelines, and AI agents. Do not expose your `sg_live_` key in browser client code.
196
377
 
197
378
  ## Support
198
379
 
199
380
  - **Documentation:** [docs.spyglass.so](https://docs.spyglass.so)
200
- - **Issues:** [GitHub Issues](https://github.com/spyglass-so/spyglass-typescript/issues)
381
+ - **API Playground:** [app.spyglass.so/playground](https://app.spyglass.so/playground)
382
+ - **Issues:** [github.com/spyglass-so/spyglass-typescript/issues](https://github.com/spyglass-so/spyglass-typescript/issues)
201
383
  - **Email:** [support@spyglass.so](mailto:support@spyglass.so)
202
384
 
203
385
  ## License
204
386
 
205
- [MIT](./LICENSE)
387
+ [MIT](https://github.com/spyglass-so/spyglass-typescript/blob/main/LICENSE)
@@ -42,9 +42,9 @@ const core = __importStar(require("./core/index.js"));
42
42
  function normalizeClientOptions(options) {
43
43
  const headers = (0, headers_js_1.mergeHeaders)({
44
44
  "X-Fern-Language": "JavaScript",
45
- "X-Fern-SDK-Name": "@spyglass/sdk",
46
- "X-Fern-SDK-Version": "0.1.0",
47
- "User-Agent": "@spyglass/sdk/0.1.0",
45
+ "X-Fern-SDK-Name": "@spyglasshq/sdk",
46
+ "X-Fern-SDK-Version": "0.1.2",
47
+ "User-Agent": "@spyglasshq/sdk/0.1.2",
48
48
  "X-Fern-Runtime": core.RUNTIME.type,
49
49
  "X-Fern-Runtime-Version": core.RUNTIME.version,
50
50
  }, options === null || options === void 0 ? void 0 : options.headers);
@@ -4,17 +4,17 @@ import { SearchClient } from "./api/resources/search/client/Client.js";
4
4
  import type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js";
5
5
  import { type NormalizedClientOptionsWithAuth } from "./BaseClient.js";
6
6
  import * as core from "./core/index.js";
7
- export declare namespace SpyglassClient {
7
+ export declare namespace Spyglass {
8
8
  type Options = BaseClientOptions;
9
9
  interface RequestOptions extends BaseRequestOptions {
10
10
  }
11
11
  }
12
- export declare class SpyglassClient {
13
- protected readonly _options: NormalizedClientOptionsWithAuth<SpyglassClient.Options>;
12
+ export declare class Spyglass {
13
+ protected readonly _options: NormalizedClientOptionsWithAuth<Spyglass.Options>;
14
14
  protected _search: SearchClient | undefined;
15
15
  protected _account: AccountClient | undefined;
16
16
  protected _indexing: IndexingClient | undefined;
17
- constructor(options: SpyglassClient.Options);
17
+ constructor(options: Spyglass.Options);
18
18
  get search(): SearchClient;
19
19
  get account(): AccountClient;
20
20
  get indexing(): IndexingClient;
@@ -43,13 +43,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
43
43
  });
44
44
  };
45
45
  Object.defineProperty(exports, "__esModule", { value: true });
46
- exports.SpyglassClient = void 0;
46
+ exports.Spyglass = void 0;
47
47
  const Client_js_1 = require("./api/resources/account/client/Client.js");
48
48
  const Client_js_2 = require("./api/resources/indexing/client/Client.js");
49
49
  const Client_js_3 = require("./api/resources/search/client/Client.js");
50
50
  const BaseClient_js_1 = require("./BaseClient.js");
51
51
  const core = __importStar(require("./core/index.js"));
52
- class SpyglassClient {
52
+ class Spyglass {
53
53
  constructor(options) {
54
54
  this._options = (0, BaseClient_js_1.normalizeClientOptionsWithAuth)(options);
55
55
  }
@@ -90,4 +90,4 @@ class SpyglassClient {
90
90
  });
91
91
  }
92
92
  }
93
- exports.SpyglassClient = SpyglassClient;
93
+ exports.Spyglass = Spyglass;
@@ -1,7 +1,7 @@
1
1
  import type * as core from "../../core/index.js";
2
2
  import * as errors from "../../errors/index.js";
3
- import type * as Spyglass from "../index.js";
3
+ import type * as spyglass from "../index.js";
4
4
  export declare class BadRequestError extends errors.SpyglassError {
5
- readonly body: Spyglass.Error_;
6
- constructor(body: Spyglass.Error_, rawResponse?: core.RawResponse);
5
+ readonly body: spyglass.Error_;
6
+ constructor(body: spyglass.Error_, rawResponse?: core.RawResponse);
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import type * as core from "../../core/index.js";
2
2
  import * as errors from "../../errors/index.js";
3
- import type * as Spyglass from "../index.js";
3
+ import type * as spyglass from "../index.js";
4
4
  export declare class ContentTooLargeError extends errors.SpyglassError {
5
- readonly body: Spyglass.Error_;
6
- constructor(body: Spyglass.Error_, rawResponse?: core.RawResponse);
5
+ readonly body: spyglass.Error_;
6
+ constructor(body: spyglass.Error_, rawResponse?: core.RawResponse);
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import type * as core from "../../core/index.js";
2
2
  import * as errors from "../../errors/index.js";
3
- import type * as Spyglass from "../index.js";
3
+ import type * as spyglass from "../index.js";
4
4
  export declare class ForbiddenError extends errors.SpyglassError {
5
- readonly body: Spyglass.Error_;
6
- constructor(body: Spyglass.Error_, rawResponse?: core.RawResponse);
5
+ readonly body: spyglass.Error_;
6
+ constructor(body: spyglass.Error_, rawResponse?: core.RawResponse);
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import type * as core from "../../core/index.js";
2
2
  import * as errors from "../../errors/index.js";
3
- import type * as Spyglass from "../index.js";
3
+ import type * as spyglass from "../index.js";
4
4
  export declare class InternalServerError extends errors.SpyglassError {
5
- readonly body: Spyglass.Error_;
6
- constructor(body: Spyglass.Error_, rawResponse?: core.RawResponse);
5
+ readonly body: spyglass.Error_;
6
+ constructor(body: spyglass.Error_, rawResponse?: core.RawResponse);
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import type * as core from "../../core/index.js";
2
2
  import * as errors from "../../errors/index.js";
3
- import type * as Spyglass from "../index.js";
3
+ import type * as spyglass from "../index.js";
4
4
  export declare class MethodNotAllowedError extends errors.SpyglassError {
5
- readonly body: Spyglass.Error_;
6
- constructor(body: Spyglass.Error_, rawResponse?: core.RawResponse);
5
+ readonly body: spyglass.Error_;
6
+ constructor(body: spyglass.Error_, rawResponse?: core.RawResponse);
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import type * as core from "../../core/index.js";
2
2
  import * as errors from "../../errors/index.js";
3
- import type * as Spyglass from "../index.js";
3
+ import type * as spyglass from "../index.js";
4
4
  export declare class NotFoundError extends errors.SpyglassError {
5
- readonly body: Spyglass.Error_;
6
- constructor(body: Spyglass.Error_, rawResponse?: core.RawResponse);
5
+ readonly body: spyglass.Error_;
6
+ constructor(body: spyglass.Error_, rawResponse?: core.RawResponse);
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import type * as core from "../../core/index.js";
2
2
  import * as errors from "../../errors/index.js";
3
- import type * as Spyglass from "../index.js";
3
+ import type * as spyglass from "../index.js";
4
4
  export declare class PaymentRequiredError extends errors.SpyglassError {
5
- readonly body: Spyglass.Error_;
6
- constructor(body: Spyglass.Error_, rawResponse?: core.RawResponse);
5
+ readonly body: spyglass.Error_;
6
+ constructor(body: spyglass.Error_, rawResponse?: core.RawResponse);
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import type * as core from "../../core/index.js";
2
2
  import * as errors from "../../errors/index.js";
3
- import type * as Spyglass from "../index.js";
3
+ import type * as spyglass from "../index.js";
4
4
  export declare class TooManyRequestsError extends errors.SpyglassError {
5
- readonly body: Spyglass.Error_;
6
- constructor(body: Spyglass.Error_, rawResponse?: core.RawResponse);
5
+ readonly body: spyglass.Error_;
6
+ constructor(body: spyglass.Error_, rawResponse?: core.RawResponse);
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import type * as core from "../../core/index.js";
2
2
  import * as errors from "../../errors/index.js";
3
- import type * as Spyglass from "../index.js";
3
+ import type * as spyglass from "../index.js";
4
4
  export declare class UnauthorizedError extends errors.SpyglassError {
5
- readonly body: Spyglass.Error_;
6
- constructor(body: Spyglass.Error_, rawResponse?: core.RawResponse);
5
+ readonly body: spyglass.Error_;
6
+ constructor(body: spyglass.Error_, rawResponse?: core.RawResponse);
7
7
  }
@@ -1,7 +1,7 @@
1
1
  import type { BaseClientOptions, BaseRequestOptions } from "../../../../BaseClient.js";
2
2
  import { type NormalizedClientOptionsWithAuth } from "../../../../BaseClient.js";
3
3
  import * as core from "../../../../core/index.js";
4
- import * as Spyglass from "../../../index.js";
4
+ import * as spyglass from "../../../index.js";
5
5
  export declare namespace AccountClient {
6
6
  type Options = BaseClientOptions;
7
7
  interface RequestOptions extends BaseRequestOptions {
@@ -24,19 +24,19 @@ export declare class AccountClient {
24
24
  *
25
25
  * @param {AccountClient.RequestOptions} requestOptions - Request-specific configuration.
26
26
  *
27
- * @throws {@link Spyglass.BadRequestError}
28
- * @throws {@link Spyglass.UnauthorizedError}
29
- * @throws {@link Spyglass.PaymentRequiredError}
30
- * @throws {@link Spyglass.ForbiddenError}
31
- * @throws {@link Spyglass.MethodNotAllowedError}
32
- * @throws {@link Spyglass.TooManyRequestsError}
33
- * @throws {@link Spyglass.InternalServerError}
27
+ * @throws {@link spyglass.BadRequestError}
28
+ * @throws {@link spyglass.UnauthorizedError}
29
+ * @throws {@link spyglass.PaymentRequiredError}
30
+ * @throws {@link spyglass.ForbiddenError}
31
+ * @throws {@link spyglass.MethodNotAllowedError}
32
+ * @throws {@link spyglass.TooManyRequestsError}
33
+ * @throws {@link spyglass.InternalServerError}
34
34
  * @throws {@link errors.SpyglassError}
35
35
  * @throws {@link errors.SpyglassTimeoutError}
36
36
  *
37
37
  * @example
38
38
  * await client.account.getCreditUsage()
39
39
  */
40
- getCreditUsage(requestOptions?: AccountClient.RequestOptions): core.HttpResponsePromise<Spyglass.GetCreditUsageResponse>;
40
+ getCreditUsage(requestOptions?: AccountClient.RequestOptions): core.HttpResponsePromise<spyglass.GetCreditUsageResponse>;
41
41
  private __getCreditUsage;
42
42
  }