@spyglasshq/sdk 0.1.0 → 0.1.1
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 +248 -66
- package/dist/cjs/BaseClient.js +3 -3
- package/dist/cjs/Client.d.ts +4 -4
- package/dist/cjs/Client.js +3 -3
- package/dist/cjs/api/errors/BadRequestError.d.ts +3 -3
- package/dist/cjs/api/errors/ContentTooLargeError.d.ts +3 -3
- package/dist/cjs/api/errors/ForbiddenError.d.ts +3 -3
- package/dist/cjs/api/errors/InternalServerError.d.ts +3 -3
- package/dist/cjs/api/errors/MethodNotAllowedError.d.ts +3 -3
- package/dist/cjs/api/errors/NotFoundError.d.ts +3 -3
- package/dist/cjs/api/errors/PaymentRequiredError.d.ts +3 -3
- package/dist/cjs/api/errors/TooManyRequestsError.d.ts +3 -3
- package/dist/cjs/api/errors/UnauthorizedError.d.ts +3 -3
- package/dist/cjs/api/resources/account/client/Client.d.ts +9 -9
- package/dist/cjs/api/resources/account/client/Client.js +15 -15
- package/dist/cjs/api/resources/indexing/client/Client.d.ts +19 -19
- package/dist/cjs/api/resources/indexing/client/Client.js +31 -31
- package/dist/cjs/api/resources/search/client/Client.d.ts +85 -85
- package/dist/cjs/api/resources/search/client/Client.js +145 -145
- package/dist/cjs/api/resources/search/client/requests/GetBrandLandingPagesRequest.d.ts +2 -2
- package/dist/cjs/api/resources/search/client/requests/GetBrandMediaInsightsRequest.d.ts +2 -2
- package/dist/cjs/api/resources/search/client/requests/{GetMediaV2Request.d.ts → GetMediaRequest.d.ts} +13 -13
- package/dist/cjs/api/resources/search/client/requests/{GetMediaV2Request.js → GetMediaRequest.js} +14 -14
- package/dist/{esm/api/resources/search/client/requests/SearchBrandsV2Request.d.mts → cjs/api/resources/search/client/requests/SearchBrandsRequest.d.ts} +4 -4
- package/dist/cjs/api/resources/search/client/requests/{SearchBrandsV2Request.js → SearchBrandsRequest.js} +5 -5
- package/dist/cjs/api/resources/search/client/requests/index.d.ts +2 -2
- package/dist/cjs/api/resources/search/client/requests/index.js +5 -5
- package/dist/cjs/api/resources/search/types/GetAccountResponse.d.ts +2 -2
- package/dist/cjs/api/resources/search/types/{GetMediaV2Response.d.ts → GetMediaResponse.d.ts} +3 -3
- package/dist/cjs/api/resources/search/types/{GetMediaV2Response.js → GetMediaResponse.js} +5 -5
- package/dist/{esm/api/resources/search/types/SearchBrandsV2Response.d.mts → cjs/api/resources/search/types/SearchBrandsResponse.d.ts} +3 -3
- package/dist/cjs/api/resources/search/types/{SearchBrandsV2Response.js → SearchBrandsResponse.js} +5 -5
- package/dist/cjs/api/resources/search/types/index.d.ts +2 -2
- package/dist/cjs/api/resources/search/types/index.js +2 -2
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/index.js +3 -3
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/BaseClient.mjs +3 -3
- package/dist/esm/Client.d.mts +4 -4
- package/dist/esm/Client.mjs +1 -1
- package/dist/esm/api/errors/BadRequestError.d.mts +3 -3
- package/dist/esm/api/errors/ContentTooLargeError.d.mts +3 -3
- package/dist/esm/api/errors/ForbiddenError.d.mts +3 -3
- package/dist/esm/api/errors/InternalServerError.d.mts +3 -3
- package/dist/esm/api/errors/MethodNotAllowedError.d.mts +3 -3
- package/dist/esm/api/errors/NotFoundError.d.mts +3 -3
- package/dist/esm/api/errors/PaymentRequiredError.d.mts +3 -3
- package/dist/esm/api/errors/TooManyRequestsError.d.mts +3 -3
- package/dist/esm/api/errors/UnauthorizedError.d.mts +3 -3
- package/dist/esm/api/resources/account/client/Client.d.mts +9 -9
- package/dist/esm/api/resources/account/client/Client.mjs +15 -15
- package/dist/esm/api/resources/indexing/client/Client.d.mts +19 -19
- package/dist/esm/api/resources/indexing/client/Client.mjs +31 -31
- package/dist/esm/api/resources/search/client/Client.d.mts +85 -85
- package/dist/esm/api/resources/search/client/Client.mjs +145 -145
- package/dist/esm/api/resources/search/client/requests/GetBrandLandingPagesRequest.d.mts +2 -2
- package/dist/esm/api/resources/search/client/requests/GetBrandMediaInsightsRequest.d.mts +2 -2
- package/dist/esm/api/resources/search/client/requests/{GetMediaV2Request.d.mts → GetMediaRequest.d.mts} +13 -13
- package/dist/esm/api/resources/search/client/requests/{GetMediaV2Request.mjs → GetMediaRequest.mjs} +13 -13
- package/dist/{cjs/api/resources/search/client/requests/SearchBrandsV2Request.d.ts → esm/api/resources/search/client/requests/SearchBrandsRequest.d.mts} +4 -4
- package/dist/esm/api/resources/search/client/requests/{SearchBrandsV2Request.mjs → SearchBrandsRequest.mjs} +4 -4
- package/dist/esm/api/resources/search/client/requests/index.d.mts +2 -2
- package/dist/esm/api/resources/search/client/requests/index.mjs +2 -2
- package/dist/esm/api/resources/search/types/GetAccountResponse.d.mts +2 -2
- package/dist/esm/api/resources/search/types/{GetMediaV2Response.d.mts → GetMediaResponse.d.mts} +3 -3
- package/dist/esm/api/resources/search/types/{GetMediaV2Response.mjs → GetMediaResponse.mjs} +4 -4
- package/dist/{cjs/api/resources/search/types/SearchBrandsV2Response.d.ts → esm/api/resources/search/types/SearchBrandsResponse.d.mts} +3 -3
- package/dist/esm/api/resources/search/types/{SearchBrandsV2Response.mjs → SearchBrandsResponse.mjs} +4 -4
- package/dist/esm/api/resources/search/types/index.d.mts +2 -2
- package/dist/esm/api/resources/search/types/index.mjs +2 -2
- package/dist/esm/index.d.mts +2 -2
- package/dist/esm/index.mjs +2 -2
- package/dist/esm/version.d.mts +1 -1
- package/dist/esm/version.mjs +1 -1
- package/package.json +17 -2
- package/reference.md +23 -23
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<img src="
|
|
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="
|
|
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
|
-
|
|
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)
|
|
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 {
|
|
82
|
+
import { Spyglass } from "@spyglasshq/sdk";
|
|
53
83
|
|
|
54
|
-
const client = new
|
|
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.
|
|
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.
|
|
68
|
-
const { media } = await client.search.
|
|
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:
|
|
100
|
+
query: "marathon running shoes comfort",
|
|
101
|
+
limit: 10,
|
|
72
102
|
});
|
|
73
103
|
|
|
74
104
|
for (const ad of media) {
|
|
75
|
-
console.log(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
214
|
+
### 6. Index a New Brand
|
|
99
215
|
|
|
100
|
-
If Spyglass doesn't have a brand yet,
|
|
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.
|
|
220
|
+
websiteUrl: "https://www.on.com",
|
|
105
221
|
});
|
|
106
222
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
237
|
+
### 7. Check Team Credit Usage
|
|
118
238
|
|
|
119
|
-
|
|
239
|
+
Check your team's remaining monthly credits and current billing window (free call):
|
|
120
240
|
|
|
121
241
|
```ts
|
|
122
|
-
|
|
242
|
+
const usage = await client.account.getCreditUsage();
|
|
243
|
+
console.log(`Remaining: ${usage.remainingCredits} / ${usage.planCredits}`);
|
|
244
|
+
```
|
|
123
245
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
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.
|
|
292
|
+
await client.search.brands({ names: ["Nike"], limit: 5 });
|
|
139
293
|
} catch (err) {
|
|
140
294
|
if (err instanceof SpyglassError) {
|
|
141
|
-
console.error(
|
|
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
|
-
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Advanced Configuration
|
|
147
305
|
|
|
148
|
-
### Retries &
|
|
306
|
+
### Retries, Timeouts & Abort Signals
|
|
149
307
|
|
|
150
|
-
|
|
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
|
|
154
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
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
|
|
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
|
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
-
|
|
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
|
|
194
|
-
- **Pagination:** Page through results with `offset` and `limit`. `count` is the number of rows in the
|
|
195
|
-
- **Server-
|
|
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
|
-
- **
|
|
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](
|
|
387
|
+
[MIT](https://github.com/spyglass-so/spyglass-typescript/blob/main/LICENSE)
|
package/dist/cjs/BaseClient.js
CHANGED
|
@@ -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": "@
|
|
46
|
-
"X-Fern-SDK-Version": "0.1.
|
|
47
|
-
"User-Agent": "@
|
|
45
|
+
"X-Fern-SDK-Name": "@spyglasshq/sdk",
|
|
46
|
+
"X-Fern-SDK-Version": "0.1.1",
|
|
47
|
+
"User-Agent": "@spyglasshq/sdk/0.1.1",
|
|
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);
|
package/dist/cjs/Client.d.ts
CHANGED
|
@@ -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
|
|
7
|
+
export declare namespace Spyglass {
|
|
8
8
|
type Options = BaseClientOptions;
|
|
9
9
|
interface RequestOptions extends BaseRequestOptions {
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
-
export declare class
|
|
13
|
-
protected readonly _options: NormalizedClientOptionsWithAuth<
|
|
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:
|
|
17
|
+
constructor(options: Spyglass.Options);
|
|
18
18
|
get search(): SearchClient;
|
|
19
19
|
get account(): AccountClient;
|
|
20
20
|
get indexing(): IndexingClient;
|
package/dist/cjs/Client.js
CHANGED
|
@@ -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.
|
|
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
|
|
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.
|
|
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
|
|
3
|
+
import type * as spyglass from "../index.js";
|
|
4
4
|
export declare class BadRequestError extends errors.SpyglassError {
|
|
5
|
-
readonly body:
|
|
6
|
-
constructor(body:
|
|
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
|
|
3
|
+
import type * as spyglass from "../index.js";
|
|
4
4
|
export declare class ContentTooLargeError extends errors.SpyglassError {
|
|
5
|
-
readonly body:
|
|
6
|
-
constructor(body:
|
|
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
|
|
3
|
+
import type * as spyglass from "../index.js";
|
|
4
4
|
export declare class ForbiddenError extends errors.SpyglassError {
|
|
5
|
-
readonly body:
|
|
6
|
-
constructor(body:
|
|
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
|
|
3
|
+
import type * as spyglass from "../index.js";
|
|
4
4
|
export declare class InternalServerError extends errors.SpyglassError {
|
|
5
|
-
readonly body:
|
|
6
|
-
constructor(body:
|
|
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
|
|
3
|
+
import type * as spyglass from "../index.js";
|
|
4
4
|
export declare class MethodNotAllowedError extends errors.SpyglassError {
|
|
5
|
-
readonly body:
|
|
6
|
-
constructor(body:
|
|
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
|
|
3
|
+
import type * as spyglass from "../index.js";
|
|
4
4
|
export declare class NotFoundError extends errors.SpyglassError {
|
|
5
|
-
readonly body:
|
|
6
|
-
constructor(body:
|
|
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
|
|
3
|
+
import type * as spyglass from "../index.js";
|
|
4
4
|
export declare class PaymentRequiredError extends errors.SpyglassError {
|
|
5
|
-
readonly body:
|
|
6
|
-
constructor(body:
|
|
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
|
|
3
|
+
import type * as spyglass from "../index.js";
|
|
4
4
|
export declare class TooManyRequestsError extends errors.SpyglassError {
|
|
5
|
-
readonly body:
|
|
6
|
-
constructor(body:
|
|
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
|
|
3
|
+
import type * as spyglass from "../index.js";
|
|
4
4
|
export declare class UnauthorizedError extends errors.SpyglassError {
|
|
5
|
-
readonly body:
|
|
6
|
-
constructor(body:
|
|
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
|
|
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
|
|
28
|
-
* @throws {@link
|
|
29
|
-
* @throws {@link
|
|
30
|
-
* @throws {@link
|
|
31
|
-
* @throws {@link
|
|
32
|
-
* @throws {@link
|
|
33
|
-
* @throws {@link
|
|
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<
|
|
40
|
+
getCreditUsage(requestOptions?: AccountClient.RequestOptions): core.HttpResponsePromise<spyglass.GetCreditUsageResponse>;
|
|
41
41
|
private __getCreditUsage;
|
|
42
42
|
}
|