@fiberai/sdk 0.0.13 → 0.0.15
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 +575 -495
- package/dist/index.cjs +341 -135
- package/dist/index.d.cts +32316 -39695
- package/dist/index.d.ts +32316 -39695
- package/dist/index.js +297 -113
- package/dist/zod.cjs +126907 -0
- package/dist/zod.d.cts +99081 -0
- package/dist/zod.d.ts +99081 -0
- package/dist/zod.js +126570 -0
- package/package.json +16 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -5,6 +5,21 @@ Official TypeScript/JavaScript SDK for the [Fiber AI](https://fiber.ai) API - Re
|
|
|
5
5
|
[](https://www.npmjs.com/package/@fiberai/sdk)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
+
## For AI agents (Cursor / Claude / Codex / ChatGPT / Copilot)
|
|
9
|
+
|
|
10
|
+
Do NOT guess operation names — read the canonical agent-facing docs first:
|
|
11
|
+
|
|
12
|
+
- **Routing index + critical rules:** https://api.fiber.ai/llms.txt
|
|
13
|
+
- **Per-operation markdown:** `https://api.fiber.ai/ai-docs/<operationId>.md`
|
|
14
|
+
(e.g. [`companySearch`](https://api.fiber.ai/ai-docs/companySearch.md),
|
|
15
|
+
[`syncQuickContactReveal`](https://api.fiber.ai/ai-docs/syncQuickContactReveal.md),
|
|
16
|
+
[`triggerExhaustiveContactEnrichment`](https://api.fiber.ai/ai-docs/triggerExhaustiveContactEnrichment.md))
|
|
17
|
+
- **Operation index:** https://api.fiber.ai/ai-docs/index.md
|
|
18
|
+
- **Full corpus (single file):** https://api.fiber.ai/llms-full.txt
|
|
19
|
+
- **OpenAPI spec:** `https://api.fiber.ai/openapi.json`
|
|
20
|
+
(send `Accept: text/markdown` for the agent-friendly variant)
|
|
21
|
+
- **MCP server:** `https://mcp.fiber.ai/mcp/v2` (also see [fiber-ai/mcp](https://github.com/fiber-ai/mcp))
|
|
22
|
+
|
|
8
23
|
## Table of Contents
|
|
9
24
|
|
|
10
25
|
- [Installation](#installation)
|
|
@@ -14,13 +29,13 @@ Official TypeScript/JavaScript SDK for the [Fiber AI](https://fiber.ai) API - Re
|
|
|
14
29
|
- [Company & People Search](#company--people-search)
|
|
15
30
|
- [Contact Enrichment](#contact-enrichment)
|
|
16
31
|
- [LinkedIn Live Enrichment](#linkedin-live-enrichment)
|
|
17
|
-
- [Saved Searches (Audiences)](#saved-searches-audiences)
|
|
18
32
|
- [Exclusion Lists](#exclusion-lists)
|
|
19
33
|
- [Google Maps Search](#google-maps-search)
|
|
20
34
|
- [AI-Powered Research](#ai-powered-research)
|
|
21
35
|
- [Advanced Usage](#advanced-usage)
|
|
22
36
|
- [Error Handling](#error-handling)
|
|
23
37
|
- [TypeScript Support](#typescript-support)
|
|
38
|
+
- [Runtime Validation with Zod](#runtime-validation-with-zod)
|
|
24
39
|
- [Rate Limits & Credits](#rate-limits--credits)
|
|
25
40
|
- [Support](#support)
|
|
26
41
|
|
|
@@ -39,48 +54,58 @@ pnpm add @fiberai/sdk
|
|
|
39
54
|
## Quick Start
|
|
40
55
|
|
|
41
56
|
```typescript
|
|
42
|
-
import { peopleSearch, companySearch, getOrgCredits } from
|
|
57
|
+
import { peopleSearch, companySearch, getOrgCredits } from "@fiberai/sdk";
|
|
43
58
|
|
|
44
59
|
// Check your organization's credit balance
|
|
45
60
|
const credits = await getOrgCredits({
|
|
46
|
-
query: { apiKey:
|
|
61
|
+
query: { apiKey: "your-api-key" },
|
|
47
62
|
});
|
|
48
63
|
|
|
49
|
-
console.log(`Available credits: ${credits.data
|
|
64
|
+
console.log(`Available credits: ${credits.data?.output.available}`);
|
|
50
65
|
|
|
51
66
|
// Search for companies
|
|
52
67
|
const companies = await companySearch({
|
|
53
68
|
body: {
|
|
54
|
-
apiKey:
|
|
69
|
+
apiKey: "your-api-key",
|
|
55
70
|
searchParams: {
|
|
56
71
|
industriesV2: {
|
|
57
|
-
anyOf: [
|
|
72
|
+
anyOf: ["Software", "Information Technology"],
|
|
58
73
|
},
|
|
74
|
+
// Bucket-valued range. Allowed bounds: 0 | 1 | 10 | 50 | 200 | 500 | 1000 | 5000 | 10000 | null.
|
|
59
75
|
employeeCountV2: {
|
|
60
|
-
lowerBoundExclusive:
|
|
61
|
-
upperBoundInclusive: 1000
|
|
76
|
+
lowerBoundExclusive: 50,
|
|
77
|
+
upperBoundInclusive: 1000,
|
|
62
78
|
},
|
|
63
79
|
headquartersCountryCode: {
|
|
64
|
-
anyOf: [
|
|
65
|
-
}
|
|
80
|
+
anyOf: ["USA"],
|
|
81
|
+
},
|
|
66
82
|
},
|
|
67
|
-
pageSize: 25
|
|
68
|
-
}
|
|
83
|
+
pageSize: 25,
|
|
84
|
+
},
|
|
69
85
|
});
|
|
70
86
|
|
|
71
87
|
// Search for people
|
|
72
88
|
const people = await peopleSearch({
|
|
73
89
|
body: {
|
|
74
|
-
apiKey:
|
|
90
|
+
apiKey: "your-api-key",
|
|
75
91
|
searchParams: {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
92
|
+
jobTitleV2: {
|
|
93
|
+
anyOf: [
|
|
94
|
+
{ type: "term", term: "CEO" },
|
|
95
|
+
{ type: "term", term: "CTO" },
|
|
96
|
+
{ type: "static-groups", groups: ["c-suite"] },
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
country3LetterCode: { anyOf: ["USA"] },
|
|
80
100
|
},
|
|
81
|
-
pageSize: 25
|
|
82
|
-
}
|
|
101
|
+
pageSize: 25,
|
|
102
|
+
},
|
|
83
103
|
});
|
|
104
|
+
|
|
105
|
+
// Every successful response also carries a `chargeInfo` envelope alongside
|
|
106
|
+
// `output`. Source of truth for cost — prefer this over the static table below.
|
|
107
|
+
console.log(people.data?.chargeInfo);
|
|
108
|
+
// e.g. { method: 'charged-now', creditsCharged: 25, lowCreditAlert: null }
|
|
84
109
|
```
|
|
85
110
|
|
|
86
111
|
## Authentication
|
|
@@ -97,13 +122,15 @@ All API requests require an API key. Get yours at [fiber.ai/app/api](https://fib
|
|
|
97
122
|
await companySearch({
|
|
98
123
|
body: {
|
|
99
124
|
apiKey: process.env.FIBERAI_API_KEY,
|
|
100
|
-
searchParams: {
|
|
101
|
-
|
|
125
|
+
searchParams: {
|
|
126
|
+
/* ... */
|
|
127
|
+
},
|
|
128
|
+
},
|
|
102
129
|
});
|
|
103
130
|
|
|
104
131
|
// GET request example
|
|
105
132
|
await getOrgCredits({
|
|
106
|
-
query: { apiKey: process.env.FIBERAI_API_KEY }
|
|
133
|
+
query: { apiKey: process.env.FIBERAI_API_KEY },
|
|
107
134
|
});
|
|
108
135
|
```
|
|
109
136
|
|
|
@@ -123,7 +150,7 @@ FIBERAI_API_KEY=your_api_key_here
|
|
|
123
150
|
Search for companies using 40+ filters including industry, location, revenue, funding, and more.
|
|
124
151
|
|
|
125
152
|
```typescript
|
|
126
|
-
import { companySearch, companyCount } from
|
|
153
|
+
import { companySearch, companyCount } from "@fiberai/sdk";
|
|
127
154
|
|
|
128
155
|
// Advanced company search
|
|
129
156
|
const result = await companySearch({
|
|
@@ -132,46 +159,51 @@ const result = await companySearch({
|
|
|
132
159
|
searchParams: {
|
|
133
160
|
// Industry filters
|
|
134
161
|
industriesV2: {
|
|
135
|
-
anyOf: [
|
|
162
|
+
anyOf: ["Software", "Cloud"],
|
|
136
163
|
},
|
|
137
|
-
|
|
164
|
+
|
|
138
165
|
// Size filters
|
|
139
166
|
employeeCountV2: {
|
|
140
167
|
lowerBoundExclusive: 50,
|
|
141
|
-
upperBoundInclusive: 500
|
|
168
|
+
upperBoundInclusive: 500,
|
|
142
169
|
},
|
|
143
|
-
|
|
170
|
+
|
|
144
171
|
// Location filters
|
|
145
172
|
headquartersCountryCode: {
|
|
146
|
-
anyOf: [
|
|
173
|
+
anyOf: ["USA"],
|
|
147
174
|
},
|
|
148
|
-
|
|
175
|
+
|
|
149
176
|
// Funding filters
|
|
150
177
|
totalFundingUSD: {
|
|
151
|
-
lowerBound: 1000000
|
|
178
|
+
lowerBound: 1000000,
|
|
152
179
|
},
|
|
153
|
-
|
|
180
|
+
|
|
154
181
|
// Keywords filter
|
|
155
182
|
keywords: {
|
|
156
|
-
containsAny: [
|
|
157
|
-
}
|
|
183
|
+
containsAny: ["venture-backed-startup"],
|
|
184
|
+
},
|
|
158
185
|
},
|
|
159
186
|
pageSize: 50,
|
|
160
|
-
cursor: null // For pagination
|
|
161
|
-
}
|
|
187
|
+
cursor: null, // For pagination
|
|
188
|
+
},
|
|
162
189
|
});
|
|
163
190
|
|
|
164
|
-
console.log(`Found ${result.data
|
|
191
|
+
console.log(`Found ${result.data?.output.data.length} companies`);
|
|
192
|
+
console.log(
|
|
193
|
+
`Cursor for next page: ${result.data?.output.nextCursor ?? "none"}`,
|
|
194
|
+
);
|
|
165
195
|
|
|
166
196
|
// Get total count before searching
|
|
167
197
|
const count = await companyCount({
|
|
168
198
|
body: {
|
|
169
199
|
apiKey: process.env.FIBERAI_API_KEY,
|
|
170
|
-
searchParams: {
|
|
171
|
-
|
|
200
|
+
searchParams: {
|
|
201
|
+
/* same filters */
|
|
202
|
+
},
|
|
203
|
+
},
|
|
172
204
|
});
|
|
173
205
|
|
|
174
|
-
console.log(`Total companies matching: ${count.data
|
|
206
|
+
console.log(`Total companies matching: ${count.data?.output.count}`);
|
|
175
207
|
```
|
|
176
208
|
|
|
177
209
|
#### People Search
|
|
@@ -179,244 +211,283 @@ console.log(`Total companies matching: ${count.data.output.count}`);
|
|
|
179
211
|
Find decision-makers and key contacts with precise targeting.
|
|
180
212
|
|
|
181
213
|
```typescript
|
|
182
|
-
import { peopleSearch, peopleSearchCount } from
|
|
214
|
+
import { peopleSearch, peopleSearchCount } from "@fiberai/sdk";
|
|
183
215
|
|
|
184
216
|
const people = await peopleSearch({
|
|
185
217
|
body: {
|
|
186
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
218
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
187
219
|
searchParams: {
|
|
188
|
-
// Job title
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
220
|
+
// Job title filter. Mix free-form `term` matches with curated
|
|
221
|
+
// `static-groups` ('founder' | 'c-suite' | 'board-member') and
|
|
222
|
+
// `dynamic-groups` ('vp' | 'director' | 'management' | 'entry-level' | ...).
|
|
223
|
+
jobTitleV2: {
|
|
224
|
+
anyOf: [
|
|
225
|
+
{ type: "term", term: "CEO" },
|
|
226
|
+
{ type: "term", term: "Chief Executive Officer" },
|
|
227
|
+
{ type: "static-groups", groups: ["founder", "c-suite"] },
|
|
228
|
+
],
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
// Country filter — ISO 3166-1 alpha-3 codes, not city names.
|
|
232
|
+
country3LetterCode: { anyOf: ["USA"] },
|
|
233
|
+
|
|
234
|
+
// Geographic radius filter (use this, not "cities"/"countries").
|
|
196
235
|
location: {
|
|
197
|
-
|
|
198
|
-
|
|
236
|
+
unionAll: [
|
|
237
|
+
{
|
|
238
|
+
strategy: "radial-distance",
|
|
239
|
+
center: { latitude: 37.7749, longitude: -122.4194 }, // San Francisco
|
|
240
|
+
radius: { unit: "miles", quantity: 50 },
|
|
241
|
+
},
|
|
242
|
+
],
|
|
199
243
|
},
|
|
200
|
-
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
244
|
+
|
|
245
|
+
// Curated tags. Allowed values include 'decision-maker', 'c-suite',
|
|
246
|
+
// 'experienced-executive', 'second-time-founder', 'phd', etc.
|
|
247
|
+
tags: { anyOf: ["decision-maker", "c-suite"] },
|
|
248
|
+
|
|
249
|
+
// Education filter — match by school identifier (LinkedIn id, slug,
|
|
250
|
+
// or domain), not by free-form school name.
|
|
251
|
+
education: {
|
|
252
|
+
anyOf: [
|
|
253
|
+
{
|
|
254
|
+
school: {
|
|
255
|
+
anyOf: [{ domain: "stanford.edu" }, { domain: "harvard.edu" }],
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
],
|
|
205
259
|
},
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
// Education filters
|
|
211
|
-
schools: ['Stanford University', 'Harvard University']
|
|
260
|
+
|
|
261
|
+
getDetailedWorkExperience: true,
|
|
262
|
+
getDetailedEducation: true,
|
|
212
263
|
},
|
|
264
|
+
|
|
265
|
+
// `currentCompanies` is a TOP-LEVEL body field, NOT inside searchParams.
|
|
266
|
+
// It expects concrete identifiers, not industry filters. To filter by
|
|
267
|
+
// industry/size/etc, use `syncCombinedSearch` and put company filters
|
|
268
|
+
// under `companyParams`.
|
|
269
|
+
currentCompanies: [
|
|
270
|
+
{ domain: "example.com" },
|
|
271
|
+
{ linkedinSlugOrURL: "https://linkedin.com/company/example" },
|
|
272
|
+
],
|
|
273
|
+
|
|
213
274
|
pageSize: 100,
|
|
214
|
-
|
|
215
|
-
getDetailedEducation: true
|
|
216
|
-
}
|
|
275
|
+
},
|
|
217
276
|
});
|
|
218
277
|
|
|
219
|
-
// Access profile data
|
|
220
|
-
people.data
|
|
221
|
-
console.log(`${profile.name}
|
|
278
|
+
// Access profile data — `output.data` is a direct array, no `.items` wrapper.
|
|
279
|
+
people.data?.output.data.forEach((profile) => {
|
|
280
|
+
console.log(`${profile.name} — ${profile.headline}`);
|
|
222
281
|
console.log(`LinkedIn: ${profile.url}`);
|
|
223
|
-
|
|
224
|
-
|
|
282
|
+
|
|
283
|
+
// There is no `profile.currentJob` field. Current role lives in
|
|
284
|
+
// `experiences[]` filtered by `is_current`.
|
|
285
|
+
const currentRole = profile.experiences?.find((e) => e.is_current);
|
|
286
|
+
if (currentRole) {
|
|
287
|
+
console.log(`Current: ${currentRole.title} at ${currentRole.company_name}`);
|
|
225
288
|
}
|
|
226
289
|
});
|
|
290
|
+
|
|
291
|
+
console.log(`Charged: ${people.data?.chargeInfo.method}`);
|
|
227
292
|
```
|
|
228
293
|
|
|
229
294
|
#### Combined Search (Companies + People)
|
|
230
295
|
|
|
231
|
-
|
|
296
|
+
Return matching companies and their employees in a single synchronous call. Filter on the company side (`companyParams`) and the person side (`profileParams`) at the same time. For larger result sets you can also run `companySearch` and `peopleSearch` independently and paginate each.
|
|
232
297
|
|
|
233
298
|
```typescript
|
|
234
|
-
import {
|
|
299
|
+
import { syncCombinedSearch } from "@fiberai/sdk";
|
|
235
300
|
|
|
236
|
-
|
|
237
|
-
const searchTask = await combinedSearch({
|
|
301
|
+
const result = await syncCombinedSearch({
|
|
238
302
|
body: {
|
|
239
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
240
|
-
|
|
303
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
304
|
+
companyParams: {
|
|
241
305
|
industriesV2: {
|
|
242
|
-
anyOf: [
|
|
306
|
+
anyOf: ["Software"],
|
|
243
307
|
},
|
|
244
308
|
employeeCountV2: {
|
|
245
|
-
lowerBoundExclusive:
|
|
246
|
-
}
|
|
309
|
+
lowerBoundExclusive: 200,
|
|
310
|
+
},
|
|
247
311
|
},
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
312
|
+
profileParams: {
|
|
313
|
+
jobTitleV2: {
|
|
314
|
+
anyOf: [
|
|
315
|
+
{ type: "term", term: "VP of Sales" },
|
|
316
|
+
{ type: "term", term: "Sales Director" },
|
|
317
|
+
{ type: "static-groups", groups: ["c-suite"] },
|
|
318
|
+
{ type: "dynamic-groups", groups: ["vp", "director"] },
|
|
319
|
+
],
|
|
320
|
+
},
|
|
251
321
|
},
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
322
|
+
companyItemLimit: 25,
|
|
323
|
+
profileItemLimit: 100,
|
|
324
|
+
},
|
|
255
325
|
});
|
|
256
326
|
|
|
257
|
-
|
|
327
|
+
result.data?.output.companies?.forEach((company) => {
|
|
328
|
+
console.log(company.preferred_name);
|
|
329
|
+
});
|
|
258
330
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const companies = await pollCombinedSearch({
|
|
263
|
-
body: {
|
|
264
|
-
apiKey: process.env.FIBERAI_API_KEY,
|
|
265
|
-
searchId,
|
|
266
|
-
entityType: 'company',
|
|
267
|
-
cursor: companyCursor,
|
|
268
|
-
pageSize: 25
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
// Process companies
|
|
273
|
-
companies.data.output.data.items.forEach(company => {
|
|
274
|
-
console.log(company.preferred_name);
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
companyCursor = companies.data.output.nextCursor;
|
|
278
|
-
} while (companyCursor);
|
|
279
|
-
|
|
280
|
-
// Poll for prospects
|
|
281
|
-
let prospectCursor = null;
|
|
282
|
-
do {
|
|
283
|
-
const prospects = await pollCombinedSearch({
|
|
284
|
-
body: {
|
|
285
|
-
apiKey: process.env.FIBERAI_API_KEY,
|
|
286
|
-
searchId,
|
|
287
|
-
entityType: 'profile',
|
|
288
|
-
cursor: prospectCursor,
|
|
289
|
-
pageSize: 100
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// Process profiles
|
|
294
|
-
prospects.data.output.data.items.forEach(profile => {
|
|
295
|
-
console.log(`${profile.name} - ${profile.headline}`);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
prospectCursor = prospects.data.output.nextCursor;
|
|
299
|
-
} while (prospectCursor);
|
|
331
|
+
result.data?.output.profiles?.forEach((profile) => {
|
|
332
|
+
console.log(`${profile.name} - ${profile.headline}`);
|
|
333
|
+
});
|
|
300
334
|
```
|
|
301
335
|
|
|
336
|
+
> See [`syncCombinedSearch`](https://api.fiber.ai/ai-docs/syncCombinedSearch.md) for the full parameter surface, or drive the two-step flow via `companySearch` + `peopleSearch` when you need cursor-based pagination.
|
|
337
|
+
|
|
302
338
|
### Contact Enrichment
|
|
303
339
|
|
|
304
|
-
|
|
340
|
+
Reveal emails and phone numbers for a known LinkedIn profile. Fiber exposes a **three-tier public contract** — pick the tier that matches your latency, coverage, and cost tradeoffs:
|
|
341
|
+
|
|
342
|
+
| Tier | Operation | Shape | When to use |
|
|
343
|
+
| ---------- | ------------------------------------------------------------------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
344
|
+
| Default | `syncQuickContactReveal` | Sync, one HTTP call | Default single-profile reveal. Fast, good coverage. |
|
|
345
|
+
| Premium | `syncTurboContactEnrichment` | Sync, one HTTP call | You need the absolute fastest response and want the widest first-pass waterfall. Premium cost. |
|
|
346
|
+
| Exhaustive | `triggerExhaustiveContactEnrichment` + `pollExhaustiveContactEnrichmentResult` | Async, trigger + poll | Highest coverage. Kick off a background waterfall that tries every available provider, then poll for results. |
|
|
305
347
|
|
|
306
|
-
|
|
348
|
+
For lists of 10–2000 identifiers, use the batch endpoints instead (see [Batch Contact Reveal](#batch-contact-reveal)).
|
|
349
|
+
|
|
350
|
+
#### Default — `syncQuickContactReveal`
|
|
307
351
|
|
|
308
352
|
```typescript
|
|
309
|
-
import {
|
|
353
|
+
import { syncQuickContactReveal } from "@fiberai/sdk";
|
|
310
354
|
|
|
311
|
-
|
|
312
|
-
const task = await triggerContactEnrichment({
|
|
355
|
+
const result = await syncQuickContactReveal({
|
|
313
356
|
body: {
|
|
314
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
315
|
-
linkedinUrl:
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
357
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
358
|
+
linkedinUrl: "https://www.linkedin.com/in/example",
|
|
359
|
+
enrichmentType: {
|
|
360
|
+
getWorkEmails: true,
|
|
361
|
+
getPersonalEmails: true,
|
|
362
|
+
getPhoneNumbers: true,
|
|
320
363
|
},
|
|
321
|
-
|
|
322
|
-
}
|
|
364
|
+
// Emails are bounce-validated by default. Set validateEmails: false to skip.
|
|
365
|
+
},
|
|
323
366
|
});
|
|
324
367
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
while (!done) {
|
|
328
|
-
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
|
|
329
|
-
|
|
330
|
-
const result = await pollContactEnrichmentResult({
|
|
331
|
-
body: {
|
|
332
|
-
apiKey: process.env.FIBERAI_API_KEY,
|
|
333
|
-
taskId: task.data.output.taskId
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
done = result.data.output.done;
|
|
338
|
-
|
|
339
|
-
if (done) {
|
|
340
|
-
const profile = result.data.output.profile;
|
|
341
|
-
console.log('Emails:', profile.emails);
|
|
342
|
-
console.log('Phone numbers:', profile.phoneNumbers);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
368
|
+
console.log("Emails:", result.data?.output.profile?.emails);
|
|
369
|
+
console.log("Phones:", result.data?.output.profile?.phoneNumbers);
|
|
345
370
|
```
|
|
346
371
|
|
|
347
|
-
####
|
|
372
|
+
#### Premium — `syncTurboContactEnrichment`
|
|
348
373
|
|
|
349
374
|
```typescript
|
|
350
|
-
import {
|
|
375
|
+
import { syncTurboContactEnrichment } from "@fiberai/sdk";
|
|
351
376
|
|
|
352
|
-
|
|
353
|
-
const result = await syncContactEnrichment({
|
|
377
|
+
const result = await syncTurboContactEnrichment({
|
|
354
378
|
body: {
|
|
355
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
356
|
-
linkedinUrl:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
}
|
|
362
|
-
}
|
|
379
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
380
|
+
linkedinUrl: "https://www.linkedin.com/in/example",
|
|
381
|
+
enrichmentType: {
|
|
382
|
+
getWorkEmails: true,
|
|
383
|
+
getPersonalEmails: true,
|
|
384
|
+
getPhoneNumbers: true,
|
|
385
|
+
},
|
|
386
|
+
},
|
|
363
387
|
});
|
|
364
388
|
|
|
365
|
-
console.log(
|
|
389
|
+
console.log("Emails:", result.data?.output.profile?.emails);
|
|
366
390
|
```
|
|
367
391
|
|
|
368
|
-
####
|
|
392
|
+
#### Exhaustive (async waterfall) — `triggerExhaustiveContactEnrichment` + `pollExhaustiveContactEnrichmentResult`
|
|
369
393
|
|
|
370
|
-
|
|
394
|
+
`triggerExhaustiveContactEnrichment` starts a background waterfall that returns the highest coverage at the cost of latency. It returns a `taskId` immediately; poll `pollExhaustiveContactEnrichmentResult` every 5–15s until `output.done === true`.
|
|
371
395
|
|
|
372
396
|
```typescript
|
|
373
|
-
import {
|
|
397
|
+
import {
|
|
398
|
+
triggerExhaustiveContactEnrichment,
|
|
399
|
+
pollExhaustiveContactEnrichmentResult,
|
|
400
|
+
} from "@fiberai/sdk";
|
|
374
401
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
402
|
+
const trigger: Awaited<ReturnType<typeof triggerExhaustiveContactEnrichment>> =
|
|
403
|
+
await triggerExhaustiveContactEnrichment({
|
|
404
|
+
body: {
|
|
405
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
406
|
+
linkedinUrl: "https://www.linkedin.com/in/example",
|
|
407
|
+
enrichmentType: {
|
|
408
|
+
getWorkEmails: true,
|
|
409
|
+
getPersonalEmails: true,
|
|
410
|
+
getPhoneNumbers: true,
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const taskId: string = trigger.data!.output.taskId;
|
|
416
|
+
|
|
417
|
+
let done: boolean = false;
|
|
418
|
+
while (!done) {
|
|
419
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 5000));
|
|
420
|
+
|
|
421
|
+
const poll: Awaited<ReturnType<typeof pollExhaustiveContactEnrichmentResult>> =
|
|
422
|
+
await pollExhaustiveContactEnrichmentResult({
|
|
423
|
+
body: { apiKey: process.env.FIBERAI_API_KEY!, taskId },
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
done = poll.data?.output.done ?? false;
|
|
427
|
+
if (done) {
|
|
428
|
+
console.log("Emails:", poll.data?.output.profile.emails);
|
|
429
|
+
console.log("Phones:", poll.data?.output.profile.phoneNumbers);
|
|
430
|
+
console.log("Status:", poll.data?.output.profile.status);
|
|
389
431
|
}
|
|
390
|
-
}
|
|
432
|
+
}
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
#### Batch Contact Reveal — `startBatchContactEnrichment` + `pollBatchContactEnrichment`
|
|
436
|
+
|
|
437
|
+
For 10–2000 LinkedIn identifiers in one task. `startBatchContactEnrichment` charges credits up front and returns a `taskId`; `pollBatchContactEnrichment` returns paginated results with both per-task progress (`output.overallStats`) and `output.done` to gate the loop. For larger lists or repeatable workflows, upload a CSV-backed audience and enrich it through the audience workflow instead.
|
|
391
438
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
439
|
+
```typescript
|
|
440
|
+
import {
|
|
441
|
+
startBatchContactEnrichment,
|
|
442
|
+
pollBatchContactEnrichment,
|
|
443
|
+
} from "@fiberai/sdk";
|
|
395
444
|
|
|
396
|
-
|
|
397
|
-
await
|
|
398
|
-
|
|
399
|
-
const results = await pollBatchContactEnrichment({
|
|
445
|
+
const start: Awaited<ReturnType<typeof startBatchContactEnrichment>> =
|
|
446
|
+
await startBatchContactEnrichment({
|
|
400
447
|
body: {
|
|
401
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
results.data.output.pageResults.forEach(person => {
|
|
413
|
-
if (person.outputs) {
|
|
414
|
-
console.log('LinkedIn:', person.inputs.linkedinUrl.value);
|
|
415
|
-
console.log('Emails:', person.outputs.emails);
|
|
416
|
-
console.log('Phones:', person.outputs.phoneNumbers);
|
|
417
|
-
console.log('---');
|
|
418
|
-
}
|
|
448
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
449
|
+
personDetails: [
|
|
450
|
+
{ linkedinUrl: { value: "https://www.linkedin.com/in/example1" } },
|
|
451
|
+
{ linkedinUrl: { value: "https://www.linkedin.com/in/example2" } },
|
|
452
|
+
],
|
|
453
|
+
enrichmentTypes: {
|
|
454
|
+
getWorkEmails: true,
|
|
455
|
+
getPersonalEmails: true,
|
|
456
|
+
getPhoneNumbers: true,
|
|
457
|
+
},
|
|
458
|
+
},
|
|
419
459
|
});
|
|
460
|
+
|
|
461
|
+
const taskId: string = start.data!.output.taskId;
|
|
462
|
+
console.log(`Queued ${start.data!.output.numPeopleEnqueued} profiles`);
|
|
463
|
+
|
|
464
|
+
let cursor: string | null | undefined = undefined;
|
|
465
|
+
let done: boolean = false;
|
|
466
|
+
|
|
467
|
+
while (!done) {
|
|
468
|
+
await new Promise<void>((resolve) => setTimeout(resolve, 10000));
|
|
469
|
+
|
|
470
|
+
const poll: Awaited<ReturnType<typeof pollBatchContactEnrichment>> =
|
|
471
|
+
await pollBatchContactEnrichment({
|
|
472
|
+
body: {
|
|
473
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
474
|
+
taskId,
|
|
475
|
+
cursor,
|
|
476
|
+
take: 100,
|
|
477
|
+
},
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
if (!poll.data) break;
|
|
481
|
+
|
|
482
|
+
for (const row of poll.data.output.pageResults) {
|
|
483
|
+
console.log(row.inputs.linkedinUrl.value, row.outputs?.emails);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
done = poll.data.output.done;
|
|
487
|
+
cursor = poll.data.output.nextCursor ?? null;
|
|
488
|
+
|
|
489
|
+
// `nextCursor` may go null before `done` flips — pause and re-poll if so.
|
|
490
|
+
if (!done && !cursor) await new Promise<void>((r) => setTimeout(r, 5000));
|
|
420
491
|
}
|
|
421
492
|
```
|
|
422
493
|
|
|
@@ -427,134 +498,76 @@ Get real-time data from LinkedIn with live scraping.
|
|
|
427
498
|
#### Live Profile Enrichment
|
|
428
499
|
|
|
429
500
|
```typescript
|
|
430
|
-
import { profileLiveEnrich } from
|
|
501
|
+
import { profileLiveEnrich } from "@fiberai/sdk";
|
|
431
502
|
|
|
432
503
|
const profile = await profileLiveEnrich({
|
|
433
504
|
body: {
|
|
434
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
435
|
-
|
|
436
|
-
|
|
505
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
506
|
+
// `identifier` accepts a slug ('williamhgates'), a full LinkedIn URL,
|
|
507
|
+
// a Sales Navigator URN ('ACwAAA...'), or a numeric LinkedIn user ID.
|
|
508
|
+
identifier: "https://www.linkedin.com/in/example",
|
|
509
|
+
},
|
|
437
510
|
});
|
|
438
511
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
512
|
+
// `output` is a discriminated union: { found: true; profile: ... } |
|
|
513
|
+
// { found: false; message: string }. Narrow on `output.found` before
|
|
514
|
+
// touching `output.profile`.
|
|
515
|
+
const out = profile.data?.output;
|
|
516
|
+
if (out?.found) {
|
|
517
|
+
console.log(out.profile.name);
|
|
518
|
+
console.log(out.profile.summary);
|
|
519
|
+
console.log(out.profile.experiences);
|
|
520
|
+
console.log(out.profile.detailed_education);
|
|
521
|
+
} else if (out) {
|
|
522
|
+
console.warn("not found:", out.message);
|
|
523
|
+
}
|
|
443
524
|
```
|
|
444
525
|
|
|
445
526
|
#### Live Company Enrichment
|
|
446
527
|
|
|
447
528
|
```typescript
|
|
448
|
-
import { companyLiveEnrich } from
|
|
529
|
+
import { companyLiveEnrich } from "@fiberai/sdk";
|
|
449
530
|
|
|
450
531
|
const company = await companyLiveEnrich({
|
|
451
532
|
body: {
|
|
452
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
453
|
-
|
|
454
|
-
|
|
533
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
534
|
+
// The `type` discriminator picks how `value` is interpreted:
|
|
535
|
+
// 'slug' — LinkedIn slug, e.g. 'microsoft'
|
|
536
|
+
// 'orgId' — LinkedIn organization id, e.g. '1441'
|
|
537
|
+
// 'liUrl' — full LinkedIn URL, e.g. 'https://www.linkedin.com/company/microsoft'
|
|
538
|
+
type: "liUrl",
|
|
539
|
+
value: "https://www.linkedin.com/company/example",
|
|
540
|
+
},
|
|
455
541
|
});
|
|
456
542
|
|
|
457
|
-
|
|
458
|
-
console.log(company.data
|
|
459
|
-
console.log(company.data
|
|
543
|
+
// Company fields live under `output.company`, not directly on `output`.
|
|
544
|
+
console.log(company.data?.output.company.headline);
|
|
545
|
+
console.log(company.data?.output.company.description);
|
|
546
|
+
console.log(company.data?.output.company.follower_count);
|
|
460
547
|
```
|
|
461
548
|
|
|
462
549
|
#### Fetch LinkedIn Posts
|
|
463
550
|
|
|
464
551
|
```typescript
|
|
465
|
-
import { profilePostsLiveFetch, companyPostsLiveFetch } from
|
|
552
|
+
import { profilePostsLiveFetch, companyPostsLiveFetch } from "@fiberai/sdk";
|
|
466
553
|
|
|
467
|
-
// Get profile posts
|
|
554
|
+
// Get profile posts. `identifier` accepts a slug, full LinkedIn URL,
|
|
555
|
+
// or Sales Navigator URN — same shape as `profileLiveEnrich`.
|
|
468
556
|
const profilePosts = await profilePostsLiveFetch({
|
|
469
557
|
body: {
|
|
470
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
471
|
-
|
|
472
|
-
cursor: null // For pagination
|
|
473
|
-
}
|
|
558
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
559
|
+
identifier: "https://www.linkedin.com/in/example",
|
|
560
|
+
cursor: null, // For pagination
|
|
561
|
+
},
|
|
474
562
|
});
|
|
475
563
|
|
|
476
|
-
// Get company posts
|
|
564
|
+
// Get company posts. `identifier` accepts a LinkedIn slug, URL, or org ID.
|
|
477
565
|
const companyPosts = await companyPostsLiveFetch({
|
|
478
566
|
body: {
|
|
479
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
480
|
-
|
|
481
|
-
cursor: null
|
|
482
|
-
}
|
|
483
|
-
});
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
### Saved Searches (Audiences)
|
|
487
|
-
|
|
488
|
-
Create, manage, and run saved searches with automatic updates.
|
|
489
|
-
|
|
490
|
-
```typescript
|
|
491
|
-
import {
|
|
492
|
-
createSavedSearch,
|
|
493
|
-
listSavedSearch,
|
|
494
|
-
manuallySpawnSavedSearchRun,
|
|
495
|
-
getSavedSearchRunStatus,
|
|
496
|
-
getSavedSearchRunCompanies,
|
|
497
|
-
getSavedSearchRunProfiles
|
|
498
|
-
} from '@fiberai/sdk';
|
|
499
|
-
|
|
500
|
-
// Create a saved search
|
|
501
|
-
const savedSearch = await createSavedSearch({
|
|
502
|
-
body: {
|
|
503
|
-
apiKey: process.env.FIBERAI_API_KEY,
|
|
504
|
-
name: 'Tech Executives in SF',
|
|
505
|
-
searchParams: {
|
|
506
|
-
companySearchParams: {
|
|
507
|
-
industriesV2: {
|
|
508
|
-
anyOf: ['Software']
|
|
509
|
-
}
|
|
510
|
-
},
|
|
511
|
-
personSearchParams: {
|
|
512
|
-
title: ['CEO', 'CTO'],
|
|
513
|
-
seniority: ['Executive']
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
});
|
|
518
|
-
|
|
519
|
-
// List all saved searches
|
|
520
|
-
const searches = await listSavedSearch({
|
|
521
|
-
body: {
|
|
522
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
// Manually run a saved search
|
|
527
|
-
const run = await manuallySpawnSavedSearchRun({
|
|
528
|
-
body: {
|
|
529
|
-
apiKey: process.env.FIBERAI_API_KEY,
|
|
530
|
-
savedSearchId: savedSearch.data.output.savedSearchId
|
|
531
|
-
}
|
|
532
|
-
});
|
|
533
|
-
|
|
534
|
-
// Check run status
|
|
535
|
-
const status = await getSavedSearchRunStatus({
|
|
536
|
-
body: {
|
|
537
|
-
apiKey: process.env.FIBERAI_API_KEY,
|
|
538
|
-
runId: run.data.output.runId
|
|
539
|
-
}
|
|
540
|
-
});
|
|
541
|
-
|
|
542
|
-
// Get companies from run
|
|
543
|
-
const companies = await getSavedSearchRunCompanies({
|
|
544
|
-
body: {
|
|
545
|
-
apiKey: process.env.FIBERAI_API_KEY,
|
|
546
|
-
runId: run.data.output.runId,
|
|
547
|
-
pageSize: 100
|
|
548
|
-
}
|
|
549
|
-
});
|
|
550
|
-
|
|
551
|
-
// Get profiles from run
|
|
552
|
-
const profiles = await getSavedSearchRunProfiles({
|
|
553
|
-
body: {
|
|
554
|
-
apiKey: process.env.FIBERAI_API_KEY,
|
|
555
|
-
runId: run.data.output.runId,
|
|
556
|
-
pageSize: 100
|
|
557
|
-
}
|
|
567
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
568
|
+
identifier: "https://www.linkedin.com/company/example",
|
|
569
|
+
cursor: null,
|
|
570
|
+
},
|
|
558
571
|
});
|
|
559
572
|
```
|
|
560
573
|
|
|
@@ -567,16 +580,16 @@ import {
|
|
|
567
580
|
createCompanyExclusionList,
|
|
568
581
|
addCompaniesToExclusionList,
|
|
569
582
|
getExcludedCompaniesForExclusionList,
|
|
570
|
-
createCompanyExclusionListFromAudience
|
|
571
|
-
} from
|
|
583
|
+
createCompanyExclusionListFromAudience,
|
|
584
|
+
} from "@fiberai/sdk";
|
|
572
585
|
|
|
573
586
|
// Create an exclusion list
|
|
574
587
|
const list = await createCompanyExclusionList({
|
|
575
588
|
body: {
|
|
576
589
|
apiKey: process.env.FIBERAI_API_KEY,
|
|
577
|
-
name:
|
|
578
|
-
isOrganizationWide: true
|
|
579
|
-
}
|
|
590
|
+
name: "Competitors",
|
|
591
|
+
isOrganizationWide: true,
|
|
592
|
+
},
|
|
580
593
|
});
|
|
581
594
|
|
|
582
595
|
// Add companies to the list
|
|
@@ -585,20 +598,20 @@ await addCompaniesToExclusionList({
|
|
|
585
598
|
apiKey: process.env.FIBERAI_API_KEY,
|
|
586
599
|
listId: list.data.output.listId,
|
|
587
600
|
companies: [
|
|
588
|
-
{ domain:
|
|
589
|
-
{ domain:
|
|
590
|
-
]
|
|
591
|
-
}
|
|
601
|
+
{ domain: "competitor1.com", linkedinUrl: null },
|
|
602
|
+
{ domain: "competitor2.com", linkedinUrl: null },
|
|
603
|
+
],
|
|
604
|
+
},
|
|
592
605
|
});
|
|
593
606
|
|
|
594
607
|
// Create exclusion list from an existing audience
|
|
595
608
|
const audienceList = await createCompanyExclusionListFromAudience({
|
|
596
609
|
body: {
|
|
597
610
|
apiKey: process.env.FIBERAI_API_KEY,
|
|
598
|
-
audienceId:
|
|
599
|
-
name:
|
|
600
|
-
isOrganizationWide: true
|
|
601
|
-
}
|
|
611
|
+
audienceId: "audience-123",
|
|
612
|
+
name: "Existing Customers",
|
|
613
|
+
isOrganizationWide: true,
|
|
614
|
+
},
|
|
602
615
|
});
|
|
603
616
|
|
|
604
617
|
// View excluded companies
|
|
@@ -606,8 +619,8 @@ const excluded = await getExcludedCompaniesForExclusionList({
|
|
|
606
619
|
body: {
|
|
607
620
|
apiKey: process.env.FIBERAI_API_KEY,
|
|
608
621
|
exclusionListId: list.data.output.listId,
|
|
609
|
-
pageSize: 100
|
|
610
|
-
}
|
|
622
|
+
pageSize: 100,
|
|
623
|
+
},
|
|
611
624
|
});
|
|
612
625
|
```
|
|
613
626
|
|
|
@@ -619,41 +632,54 @@ Search for local businesses on Google Maps.
|
|
|
619
632
|
import {
|
|
620
633
|
googleMapsSearch,
|
|
621
634
|
checkGoogleMapsResults,
|
|
622
|
-
pollGoogleMapsResults
|
|
623
|
-
} from
|
|
635
|
+
pollGoogleMapsResults,
|
|
636
|
+
} from "@fiberai/sdk";
|
|
624
637
|
|
|
625
|
-
// Start Google Maps search
|
|
638
|
+
// Start Google Maps search. `query` is the keywords only (no location info)
|
|
639
|
+
// and `strategy` is required — see below for the available strategies.
|
|
626
640
|
const search = await googleMapsSearch({
|
|
627
641
|
body: {
|
|
628
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
629
|
-
query:
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
642
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
643
|
+
query: "coffee shops",
|
|
644
|
+
maxResults: 100,
|
|
645
|
+
strategy: {
|
|
646
|
+
// Options:
|
|
647
|
+
// { strategy: 'whole-usa' }
|
|
648
|
+
// { strategy: 'specific-areas', unionAll: [{ regionType: 'circle', center: {latitude, longitude}, radiusMiles }] }
|
|
649
|
+
// { strategy: 'world-cities', countriesAndRegions: { unionAll: ['USA', 'GBR', ...] } }
|
|
650
|
+
strategy: "specific-areas",
|
|
651
|
+
unionAll: [
|
|
652
|
+
{
|
|
653
|
+
regionType: "circle",
|
|
654
|
+
center: { latitude: 37.7749, longitude: -122.4194 },
|
|
655
|
+
radiusMiles: 10,
|
|
656
|
+
},
|
|
657
|
+
],
|
|
658
|
+
},
|
|
659
|
+
},
|
|
633
660
|
});
|
|
634
661
|
|
|
635
|
-
|
|
662
|
+
const searchID = search.data!.output.searchID;
|
|
663
|
+
|
|
664
|
+
// Check search progress.
|
|
636
665
|
const progress = await checkGoogleMapsResults({
|
|
637
|
-
body: {
|
|
638
|
-
apiKey: process.env.FIBERAI_API_KEY,
|
|
639
|
-
searchID: search.data.output.projectID
|
|
640
|
-
}
|
|
666
|
+
body: { apiKey: process.env.FIBERAI_API_KEY!, searchID },
|
|
641
667
|
});
|
|
642
668
|
|
|
643
|
-
console.log(`Progress: ${progress.data
|
|
669
|
+
console.log(`Progress: ${progress.data?.output.percentageCompleted}%`);
|
|
644
670
|
|
|
645
|
-
// Poll for results
|
|
646
|
-
if (progress.data
|
|
671
|
+
// Poll for results once complete.
|
|
672
|
+
if (progress.data?.output.status === "COMPLETED") {
|
|
647
673
|
const results = await pollGoogleMapsResults({
|
|
648
674
|
body: {
|
|
649
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
650
|
-
|
|
651
|
-
pageSize: 50
|
|
652
|
-
}
|
|
675
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
676
|
+
searchID,
|
|
677
|
+
pageSize: 50,
|
|
678
|
+
},
|
|
653
679
|
});
|
|
654
|
-
|
|
655
|
-
results.data
|
|
656
|
-
console.log(`${place.name}
|
|
680
|
+
|
|
681
|
+
results.data?.output.results.forEach((place) => {
|
|
682
|
+
console.log(`${place.name} — ${place.address}`);
|
|
657
683
|
console.log(`Rating: ${place.rating}, Reviews: ${place.numReviews}`);
|
|
658
684
|
console.log(`Website: ${place.website}`);
|
|
659
685
|
});
|
|
@@ -665,37 +691,42 @@ if (progress.data.output.status === 'COMPLETED') {
|
|
|
665
691
|
Use AI agents for intelligent company research and domain lookup.
|
|
666
692
|
|
|
667
693
|
```typescript
|
|
668
|
-
import { domainLookupTrigger, domainLookupPolling } from
|
|
694
|
+
import { domainLookupTrigger, domainLookupPolling } from "@fiberai/sdk";
|
|
669
695
|
|
|
670
|
-
// Trigger domain lookup
|
|
696
|
+
// Trigger domain lookup. `overAllContext` is required and helps the agent
|
|
697
|
+
// disambiguate similar names; `companyInfo` is an array of objects (not
|
|
698
|
+
// a list of bare strings).
|
|
671
699
|
const lookup = await domainLookupTrigger({
|
|
672
700
|
body: {
|
|
673
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
701
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
702
|
+
overAllContext: "YC startups in the US",
|
|
703
|
+
companyInfo: [
|
|
704
|
+
{ name: "Acme Corp" },
|
|
705
|
+
{ name: "Globex", country: "USA" },
|
|
706
|
+
{ name: "Initech" },
|
|
707
|
+
],
|
|
708
|
+
},
|
|
680
709
|
});
|
|
681
710
|
|
|
682
|
-
|
|
711
|
+
const domainAgentRunId = lookup.data!.output.domainAgentRunId;
|
|
712
|
+
|
|
713
|
+
// Poll for results.
|
|
683
714
|
let lookupDone = false;
|
|
684
715
|
while (!lookupDone) {
|
|
685
|
-
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
686
|
-
|
|
716
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
717
|
+
|
|
687
718
|
const results = await domainLookupPolling({
|
|
688
719
|
body: {
|
|
689
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
690
|
-
domainAgentRunId
|
|
691
|
-
pageSize: 10
|
|
692
|
-
}
|
|
720
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
721
|
+
domainAgentRunId,
|
|
722
|
+
pageSize: 10,
|
|
723
|
+
},
|
|
693
724
|
});
|
|
694
|
-
|
|
695
|
-
lookupDone = results.data
|
|
696
|
-
|
|
725
|
+
|
|
726
|
+
lookupDone = results.data?.output.status === "DONE";
|
|
727
|
+
|
|
697
728
|
if (lookupDone) {
|
|
698
|
-
results.data
|
|
729
|
+
results.data?.output.data.forEach((company) => {
|
|
699
730
|
console.log(`${company.companyName}: ${company.bestDomain}`);
|
|
700
731
|
console.log(`Confidence: ${company.confidence}/10`);
|
|
701
732
|
console.log(`Rationale: ${company.rationale}`);
|
|
@@ -706,53 +737,62 @@ while (!lookupDone) {
|
|
|
706
737
|
|
|
707
738
|
## Advanced Usage
|
|
708
739
|
|
|
709
|
-
###
|
|
740
|
+
### Configuring the built-in client
|
|
741
|
+
|
|
742
|
+
The SDK exports a pre-configured `client` that already points at `https://api.fiber.ai`. Every operation uses it by default — you do **not** need to call `createClient` to make requests.
|
|
743
|
+
|
|
744
|
+
If you need to customize headers, swap in a custom `fetch`, or add request/response interceptors, mutate the shared instance:
|
|
710
745
|
|
|
711
746
|
```typescript
|
|
712
|
-
import {
|
|
747
|
+
import { client } from "@fiberai/sdk";
|
|
713
748
|
|
|
714
|
-
//
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
// Add custom headers, interceptors, etc.
|
|
749
|
+
// One-time configuration: extra headers, a custom fetch, etc.
|
|
750
|
+
client.setConfig({
|
|
751
|
+
headers: { "X-Trace-Id": "my-app/1.0.0" },
|
|
718
752
|
});
|
|
719
753
|
|
|
720
|
-
//
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
client: customClient,
|
|
725
|
-
body: { /* ... */ }
|
|
754
|
+
// Log every outgoing request — handy as a poor man's debug mode.
|
|
755
|
+
client.interceptors.request.use((request) => {
|
|
756
|
+
console.log(`[fiberai] ${request.method} ${request.url}`);
|
|
757
|
+
return request;
|
|
726
758
|
});
|
|
727
759
|
```
|
|
728
760
|
|
|
761
|
+
`createClient` from the package is for the rare case where you want a second, independent client (e.g. multi-tenant apps). Pass it via the `client:` option on any operation.
|
|
762
|
+
|
|
729
763
|
### Pagination Helper
|
|
730
764
|
|
|
731
765
|
```typescript
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
766
|
+
import type { CompanySearchData, CompanySearchResponse } from "@fiberai/sdk";
|
|
767
|
+
|
|
768
|
+
async function* paginateCompanies(
|
|
769
|
+
initial: CompanySearchData,
|
|
770
|
+
): AsyncGenerator<
|
|
771
|
+
NonNullable<CompanySearchResponse["data"]>["output"]["data"]
|
|
772
|
+
> {
|
|
773
|
+
let cursor: string | null | undefined = initial.body.cursor ?? null;
|
|
774
|
+
|
|
735
775
|
do {
|
|
736
|
-
const result = await
|
|
737
|
-
...
|
|
738
|
-
body: {
|
|
739
|
-
...params.body,
|
|
740
|
-
cursor
|
|
741
|
-
}
|
|
776
|
+
const result = await companySearch({
|
|
777
|
+
...initial,
|
|
778
|
+
body: { ...initial.body, cursor },
|
|
742
779
|
});
|
|
743
|
-
|
|
744
|
-
|
|
780
|
+
|
|
781
|
+
if (!result.data) break;
|
|
782
|
+
yield result.data.output.data;
|
|
745
783
|
cursor = result.data.output.nextCursor;
|
|
746
784
|
} while (cursor);
|
|
747
785
|
}
|
|
748
786
|
|
|
749
787
|
// Usage
|
|
750
|
-
for await (const companies of
|
|
788
|
+
for await (const companies of paginateCompanies({
|
|
751
789
|
body: {
|
|
752
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
753
|
-
searchParams: {
|
|
754
|
-
|
|
755
|
-
|
|
790
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
791
|
+
searchParams: {
|
|
792
|
+
/* ... */
|
|
793
|
+
},
|
|
794
|
+
pageSize: 100,
|
|
795
|
+
},
|
|
756
796
|
})) {
|
|
757
797
|
console.log(`Processing batch of ${companies.length} companies`);
|
|
758
798
|
// Process batch
|
|
@@ -769,22 +809,22 @@ import {
|
|
|
769
809
|
getIndustries,
|
|
770
810
|
getTags,
|
|
771
811
|
getNaicsCodes,
|
|
772
|
-
getAccelerators
|
|
773
|
-
} from
|
|
812
|
+
getAccelerators,
|
|
813
|
+
} from "@fiberai/sdk";
|
|
774
814
|
|
|
775
815
|
// Get available regions for filtering
|
|
776
816
|
const regions = await getRegions({
|
|
777
|
-
query: { apiKey: process.env.FIBERAI_API_KEY }
|
|
817
|
+
query: { apiKey: process.env.FIBERAI_API_KEY },
|
|
778
818
|
});
|
|
779
819
|
|
|
780
820
|
// Get available industries
|
|
781
821
|
const industries = await getIndustries({
|
|
782
|
-
query: { apiKey: process.env.FIBERAI_API_KEY }
|
|
822
|
+
query: { apiKey: process.env.FIBERAI_API_KEY },
|
|
783
823
|
});
|
|
784
824
|
|
|
785
825
|
// Get profile and company tags
|
|
786
826
|
const tags = await getTags({
|
|
787
|
-
query: { apiKey: process.env.FIBERAI_API_KEY }
|
|
827
|
+
query: { apiKey: process.env.FIBERAI_API_KEY },
|
|
788
828
|
});
|
|
789
829
|
```
|
|
790
830
|
|
|
@@ -792,28 +832,44 @@ const tags = await getTags({
|
|
|
792
832
|
|
|
793
833
|
### Standard Error Handling
|
|
794
834
|
|
|
835
|
+
By default, every operation returns `{ data, error, response }`. Branch on the HTTP status from `response.status` rather than string-matching the error body — error payloads are not guaranteed to contain a parseable `message` for every failure mode.
|
|
836
|
+
|
|
795
837
|
```typescript
|
|
796
|
-
import { companySearch } from
|
|
838
|
+
import { companySearch } from "@fiberai/sdk";
|
|
797
839
|
|
|
798
840
|
const result = await companySearch({
|
|
799
841
|
body: {
|
|
800
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
801
|
-
searchParams: {
|
|
802
|
-
|
|
842
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
843
|
+
searchParams: {
|
|
844
|
+
/* ... */
|
|
845
|
+
},
|
|
846
|
+
},
|
|
803
847
|
});
|
|
804
848
|
|
|
805
849
|
if (result.error) {
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
850
|
+
switch (result.response.status) {
|
|
851
|
+
case 400:
|
|
852
|
+
console.error("Bad request:", result.error);
|
|
853
|
+
break;
|
|
854
|
+
case 401:
|
|
855
|
+
console.error("Invalid API key");
|
|
856
|
+
break;
|
|
857
|
+
case 402:
|
|
858
|
+
// 402 errors carry an `outOfCreditsAlert` with a top-up link.
|
|
859
|
+
console.error("Insufficient credits:", result.error);
|
|
860
|
+
break;
|
|
861
|
+
case 429:
|
|
862
|
+
console.error("Rate limit exceeded — back off and retry");
|
|
863
|
+
break;
|
|
864
|
+
default:
|
|
865
|
+
console.error(
|
|
866
|
+
`Request failed (${result.response.status}):`,
|
|
867
|
+
result.error,
|
|
868
|
+
);
|
|
814
869
|
}
|
|
815
870
|
} else {
|
|
816
|
-
console.log(
|
|
871
|
+
console.log("Success:", result.data?.output);
|
|
872
|
+
console.log("Charged:", result.data?.chargeInfo);
|
|
817
873
|
}
|
|
818
874
|
```
|
|
819
875
|
|
|
@@ -821,86 +877,109 @@ if (result.error) {
|
|
|
821
877
|
|
|
822
878
|
```typescript
|
|
823
879
|
try {
|
|
824
|
-
const result = await companySearch
|
|
880
|
+
const result = await companySearch({
|
|
825
881
|
body: {
|
|
826
|
-
apiKey: process.env.FIBERAI_API_KEY
|
|
827
|
-
searchParams: {
|
|
882
|
+
apiKey: process.env.FIBERAI_API_KEY!,
|
|
883
|
+
searchParams: {
|
|
884
|
+
/* ... */
|
|
885
|
+
},
|
|
828
886
|
},
|
|
829
|
-
throwOnError: true
|
|
887
|
+
throwOnError: true,
|
|
830
888
|
});
|
|
831
|
-
|
|
832
|
-
// result.data is guaranteed to exist
|
|
889
|
+
|
|
890
|
+
// result.data is guaranteed to exist when throwOnError is true.
|
|
833
891
|
console.log(result.data.output);
|
|
834
892
|
} catch (error) {
|
|
835
|
-
console.error(
|
|
893
|
+
console.error("Request failed:", error);
|
|
836
894
|
}
|
|
837
895
|
```
|
|
838
896
|
|
|
839
897
|
### Common HTTP Error Codes
|
|
840
898
|
|
|
841
|
-
| Code | Meaning
|
|
842
|
-
|
|
843
|
-
| 400
|
|
844
|
-
| 401
|
|
845
|
-
| 402
|
|
846
|
-
| 403
|
|
847
|
-
| 404
|
|
848
|
-
| 429
|
|
849
|
-
| 500
|
|
899
|
+
| Code | Meaning | Solution |
|
|
900
|
+
| ---- | --------------------- | ------------------------------------------ |
|
|
901
|
+
| 400 | Bad Request | Check your request parameters |
|
|
902
|
+
| 401 | Unauthorized | Verify your API key is valid |
|
|
903
|
+
| 402 | Payment Required | Insufficient credits - top up your account |
|
|
904
|
+
| 403 | Forbidden | You don't have access to this resource |
|
|
905
|
+
| 404 | Not Found | Resource doesn't exist |
|
|
906
|
+
| 429 | Too Many Requests | Rate limit exceeded - slow down requests |
|
|
907
|
+
| 500 | Internal Server Error | Contact support |
|
|
850
908
|
|
|
851
909
|
## TypeScript Support
|
|
852
910
|
|
|
853
911
|
All SDK methods are fully typed with TypeScript.
|
|
854
912
|
|
|
855
913
|
```typescript
|
|
856
|
-
import
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
PollContactEnrichmentResultResponse
|
|
863
|
-
} from '@fiberai/sdk';
|
|
864
|
-
|
|
865
|
-
// Type-safe request
|
|
866
|
-
const searchParams: CompanySearchData = {
|
|
914
|
+
import { companySearch } from "@fiberai/sdk";
|
|
915
|
+
|
|
916
|
+
// SDK functions return `{ data, error, response }`. Annotate the awaited call
|
|
917
|
+
// with `Awaited<ReturnType<typeof fn>>` — the exported `<Op>Response` /
|
|
918
|
+
// `<Op>Errors` types describe the inner body union, NOT the wrapper.
|
|
919
|
+
const result: Awaited<ReturnType<typeof companySearch>> = await companySearch({
|
|
867
920
|
body: {
|
|
868
921
|
apiKey: process.env.FIBERAI_API_KEY!,
|
|
869
922
|
searchParams: {
|
|
870
|
-
industriesV2: {
|
|
871
|
-
anyOf: ['Software']
|
|
872
|
-
},
|
|
923
|
+
industriesV2: { anyOf: ["Software"] },
|
|
873
924
|
employeeCountV2: {
|
|
874
|
-
lowerBoundExclusive:
|
|
875
|
-
upperBoundInclusive: 1000
|
|
876
|
-
}
|
|
925
|
+
lowerBoundExclusive: 50,
|
|
926
|
+
upperBoundInclusive: 1000,
|
|
927
|
+
},
|
|
877
928
|
},
|
|
878
|
-
pageSize: 25
|
|
879
|
-
}
|
|
880
|
-
};
|
|
929
|
+
pageSize: 25,
|
|
930
|
+
},
|
|
931
|
+
});
|
|
932
|
+
|
|
933
|
+
if (result.data) {
|
|
934
|
+
console.log(`${result.data.output.data.length} companies`);
|
|
935
|
+
console.log(result.data.chargeInfo);
|
|
936
|
+
}
|
|
881
937
|
|
|
882
|
-
|
|
938
|
+
// Need a name for the request shape (e.g. building a request in one place,
|
|
939
|
+
// passing it elsewhere)? Derive it from the function — the bare `<Op>Data`
|
|
940
|
+
// export carries an internal `url` literal and is not meant to be
|
|
941
|
+
// hand-constructed.
|
|
942
|
+
type CompanySearchArgs = Parameters<typeof companySearch>[0];
|
|
883
943
|
```
|
|
884
944
|
|
|
885
|
-
|
|
945
|
+
> **Naming convention:** every operation `foo` ships companion types:
|
|
946
|
+
> `FooResponse` (200 body union) and `FooErrors` (4xx/5xx body union). Use `Parameters<typeof foo>[0]` for input args and `Awaited<ReturnType<typeof foo>>` for the result envelope. The same applies to `peopleSearch`, `syncQuickContactReveal`, etc.
|
|
947
|
+
|
|
948
|
+
### Runtime Validation with Zod
|
|
886
949
|
|
|
887
|
-
|
|
950
|
+
Every request and response shape is also published as a [Zod](https://zod.dev) schema, generated from the same OpenAPI spec as the TypeScript types. They live behind the dedicated `@fiberai/sdk/zod` subpath so apps that don't need runtime validation pay zero bundle cost — the main entry stays under 50 KB while the Zod bundle (~3–6 MB depending on the API surface) is only loaded if you import it.
|
|
888
951
|
|
|
889
952
|
```typescript
|
|
890
|
-
import {
|
|
891
|
-
import {
|
|
953
|
+
import { zPeopleSearchData, zCompanySearchData } from "@fiberai/sdk/zod";
|
|
954
|
+
import { peopleSearch } from "@fiberai/sdk";
|
|
892
955
|
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
956
|
+
const rawInput: unknown = JSON.parse(req.body);
|
|
957
|
+
|
|
958
|
+
const parsed: ReturnType<typeof zPeopleSearchData.parse> =
|
|
959
|
+
zPeopleSearchData.parse(rawInput);
|
|
960
|
+
|
|
961
|
+
const result: Awaited<ReturnType<typeof peopleSearch>> =
|
|
962
|
+
await peopleSearch(parsed);
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
Use `safeParse` if you want to handle validation failures without throwing. Zod 4 ships top-level error helpers — use `z.flattenError` for form-friendly output or `z.treeifyError` for nested shapes:
|
|
966
|
+
|
|
967
|
+
```typescript
|
|
968
|
+
import { z } from "zod";
|
|
969
|
+
import { zCompanySearchData } from "@fiberai/sdk/zod";
|
|
899
970
|
|
|
900
|
-
|
|
901
|
-
|
|
971
|
+
const parsed: ReturnType<typeof zCompanySearchData.safeParse> =
|
|
972
|
+
zCompanySearchData.safeParse(req.body);
|
|
973
|
+
|
|
974
|
+
if (!parsed.success) {
|
|
975
|
+
return res.status(400).json({ errors: z.flattenError(parsed.error) });
|
|
976
|
+
}
|
|
902
977
|
```
|
|
903
978
|
|
|
979
|
+
> **Naming convention:** every TypeScript type `Foo` has a matching `zFoo` schema. So `PeopleSearchData` ↔ `zPeopleSearchData`, `CompanySearchResponse` ↔ `zCompanySearchResponse`, etc.
|
|
980
|
+
|
|
981
|
+
`zod` ships as a runtime dependency of `@fiberai/sdk` and is externalized from the bundle (so package managers dedupe with whatever Zod copy your app already has). If you don't import `@fiberai/sdk/zod`, your bundler tree-shakes the schemas out and you never pay for them.
|
|
982
|
+
|
|
904
983
|
## Rate Limits & Credits
|
|
905
984
|
|
|
906
985
|
### Rate Limits
|
|
@@ -914,43 +993,44 @@ Each endpoint has its own rate limit. Common limits:
|
|
|
914
993
|
|
|
915
994
|
### Credit Costs
|
|
916
995
|
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
996
|
+
Pricing varies by plan and is configured per-organization. **Don't hard-code costs into your app** — every successful response carries a `chargeInfo` envelope alongside `output` that tells you exactly what was charged for that call:
|
|
997
|
+
|
|
998
|
+
```typescript
|
|
999
|
+
const result = await peopleSearch({
|
|
1000
|
+
/* ... */
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
console.log(result.data?.chargeInfo);
|
|
1004
|
+
// {
|
|
1005
|
+
// method: 'charged-now', // 'charged-now' | 'charging-later' | 'charged-for-async-process' | 'free'
|
|
1006
|
+
// creditsCharged: 25, // present when method !== 'free'
|
|
1007
|
+
// lowCreditAlert: null // populated with a top-up URL when running low
|
|
1008
|
+
// }
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
For a non-charging dry-run estimate of contact enrichment costs, use [`estimateEnrichmentCost`](https://api.fiber.ai/ai-docs/estimateEnrichmentCost.md). For the per-org pricing breakdown across operations, inspect `getOrgCredits().data.output.creditsPerOperation`.
|
|
932
1012
|
|
|
933
1013
|
### Free Endpoints
|
|
934
1014
|
|
|
935
|
-
The following endpoints
|
|
1015
|
+
The following endpoints never charge credits:
|
|
936
1016
|
|
|
937
|
-
- `getOrgCredits`
|
|
938
|
-
- `getRegions`, `getLanguages`, `getTimeZones`, `getIndustries`, `getTags`, `getNaicsCodes`, `getAccelerators`
|
|
939
|
-
- All exclusion
|
|
1017
|
+
- `getOrgCredits` — Check credit balance
|
|
1018
|
+
- `getRegions`, `getLanguages`, `getTimeZones`, `getIndustries`, `getTags`, `getNaicsCodes`, `getAccelerators` — Utility endpoints
|
|
1019
|
+
- All exclusion-list management endpoints
|
|
940
1020
|
|
|
941
1021
|
### Check Your Credits
|
|
942
1022
|
|
|
943
1023
|
```typescript
|
|
944
|
-
import { getOrgCredits } from
|
|
1024
|
+
import { getOrgCredits } from "@fiberai/sdk";
|
|
945
1025
|
|
|
946
1026
|
const credits = await getOrgCredits({
|
|
947
|
-
query: { apiKey: process.env.FIBERAI_API_KEY }
|
|
1027
|
+
query: { apiKey: process.env.FIBERAI_API_KEY! },
|
|
948
1028
|
});
|
|
949
1029
|
|
|
950
|
-
console.log(`Available: ${credits.data
|
|
951
|
-
console.log(`Used: ${credits.data
|
|
952
|
-
console.log(`Max: ${credits.data
|
|
953
|
-
console.log(`Resets on: ${credits.data
|
|
1030
|
+
console.log(`Available: ${credits.data?.output.available}`);
|
|
1031
|
+
console.log(`Used: ${credits.data?.output.used}`);
|
|
1032
|
+
console.log(`Max: ${credits.data?.output.max}`);
|
|
1033
|
+
console.log(`Resets on: ${credits.data?.output.usagePeriodResetsOn}`);
|
|
954
1034
|
```
|
|
955
1035
|
|
|
956
1036
|
## Support
|