@gtm-router/sdk
v1.1.0
Published
Official TypeScript SDK for the GTM Router API — GTM data enrichment
Downloads
136
Maintainers
Readme
@gtm-router/sdk
Official TypeScript SDK for the GTM Router API — one API for all your Go-to-Market data enrichment needs.
- Zero runtime dependencies — uses Node.js built-in
fetch - Full TypeScript support — every input and output is typed
- ESM + CJS — works everywhere
- Automatic retries — exponential backoff on 5xx errors
- Auto-polling — async operations (bond, waterfall, bulk) poll until completion
Installation
npm install @gtm-router/sdk
# or
pnpm add @gtm-router/sdk
# or
yarn add @gtm-router/sdkRequires Node.js 18+.
Quick Start
import { GTMRouter } from "@gtm-router/sdk";
const gtmRouter = new GTMRouter({ apiKey: "sk_live_xxxxx" });
// Find an email
const result = await gtm-router.email.find({
firstName: "John",
lastName: "Doe",
domain: "acme.com",
});
console.log(result.data?.email); // "[email protected]"Configuration
const gtmRouter = new GTMRouter({
apiKey: "sk_live_xxxxx", // Required
baseUrl: "https://gtmrouter.io/api/v1", // Default
timeout: 30000, // Request timeout in ms (default: 30s)
maxRetries: 2, // Retry count for 5xx errors (default: 2)
retryDelay: 1000, // Initial retry delay in ms (default: 1s)
pollInterval: 3000, // Polling interval for async ops (default: 3s)
pollTimeout: 300000, // Polling timeout for async ops (default: 5min)
});API Reference
gtm-router.email.find(params)
Find an email address from a person's name and company domain.
const result = await gtm-router.email.find({
firstName: "John",
lastName: "Doe",
domain: "acme.com",
mode: "fastest", // "fastest" | "cheapest" | "waterfall"
providers: ["leadmagic"], // Optional: specific providers
verify: true, // Optional: verify the found email
});
// result.status: "found" | "not_found" | "error"
// result.data?.email: "[email protected]"
// result.data?.confidence: 0.96
// result.cost: 0.01gtm-router.email.verify(params)
Verify the deliverability of an email address.
const result = await gtm-router.email.verify({
email: "[email protected]",
mode: "cheapest",
});
// result.data?.result: "deliverable" | "undeliverable" | "risky" | "unknown"
// result.data?.isCatchAll: false
// result.data?.isDisposable: false
// result.data?.confidence: 0.99Phone
gtm-router.phone.find(params)
Find a phone number from an email, LinkedIn URL, or name + domain.
// By email
const result = await gtm-router.phone.find({ email: "[email protected]" });
// By LinkedIn
const result = await gtm-router.phone.find({ linkedinUrl: "linkedin.com/in/johndoe" });
// By name + domain
const result = await gtm-router.phone.find({
firstName: "John",
lastName: "Doe",
domain: "acme.com",
});
// result.data?.phone: "+1-555-0123"
// result.data?.type: "mobile" | "landline" | "voip" | "unknown"Bond (AI Research)
gtm-router.bond.create(params, options?)
Create an AI research task. Automatically polls until completion.
const result = await gtm-router.bond.create(
{
query: "Find top 5 engineering leaders at Intercom",
model: "anthropic/claude-sonnet-4", // Optional
webhookUrl: "https://example.com/hook", // Optional
},
{
onProgress: (status) => {
console.log(status.progress); // "Enriching profiles (3/5)..."
},
},
);
// result.data?.results: [{ fullName: "Jane Smith", title: "VP Engineering" }]
// result.data?.stepsExecuted: ["Searched PDL", "Found 5 profiles", ...]gtm-router.bond.get(id)
Get the status of a bond task by ID.
const task = await gtm-router.bond.get("bond_pqr678");Waterfall
gtm-router.waterfall.create(params, options?)
Run a multi-step enrichment waterfall. Automatically polls until completion.
const result = await gtm-router.waterfall.create({
input: { firstName: "John", lastName: "Doe", domain: "acme.com" },
steps: [
{ action: "email.find", options: { mode: "waterfall", providers: ["leadmagic", "prospeo"], verify: true } },
{ action: "phone.find", options: { mode: "cheapest" } },
],
maxCost: 0.50,
});
// result.steps: [{ step: 1, action: "email.find", status: "found", ... }]
// result.mergedProfile: { email: "[email protected]", phone: "+1-555-0123" }Bulk Operations
gtm-router.bulk.emailFind(params, options?)
Bulk email finding. Automatically polls until completion.
const result = await gtm-router.bulk.emailFind({
records: [
{ firstName: "John", lastName: "Doe", domain: "acme.com" },
{ firstName: "Jane", lastName: "Smith", domain: "example.com" },
],
mode: "cheapest",
});gtm-router.bulk.emailVerify(params, options?)
const result = await gtm-router.bulk.emailVerify({
records: [{ email: "[email protected]" }, { email: "[email protected]" }],
});gtm-router.bulk.phoneFind(params, options?)
const result = await gtm-router.bulk.phoneFind({
records: [{ email: "[email protected]" }, { linkedinUrl: "linkedin.com/in/jane" }],
});gtm-router.bulk.get(id)
Get the status of a bulk job by ID.
const job = await gtm-router.bulk.get("bulk_vwx234");
// job.status: "queued" | "processing" | "completed" | "failed"
// job.totalRecords: 100
// job.completedRecords: 75Account
gtm-router.account.get()
const account = await gtm-router.account.get();
// account.balance: 94.32
// account.rateLimits.requestsPerMinute: 60
// account.usageThisMonth.totalCost: 5.68API Keys
gtm-router.keys.create(params)
const key = await gtm-router.keys.create({ name: "Production", expiresAt: "2027-01-01" });
// key.key: "sk_live_xxxxx" (only returned on creation)gtm-router.keys.list()
const keys = await gtm-router.keys.list();gtm-router.keys.revoke(id)
await gtm-router.keys.revoke("key_abc123");Providers
gtm-router.providers.list()
const providers = await gtm-router.providers.list();
// [{ id: "leadmagic", displayName: "LeadMagic", healthStatus: "healthy", ... }]Models
gtm-router.models.list()
const models = await gtm-router.models.list();
// [{ id: "anthropic/claude-sonnet-4", name: "Claude Sonnet 4", ... }]Error Handling
The SDK throws typed errors for different failure modes:
import {
GTMRouterError,
InvalidApiKeyError,
InsufficientBalanceError,
RateLimitError,
ValidationError,
ProviderError,
TimeoutError,
} from "@gtm-router/sdk";
try {
await gtm-router.email.find({ firstName: "John", lastName: "Doe", domain: "acme.com" });
} catch (error) {
if (error instanceof RateLimitError) {
console.log(`Rate limited. Retry after ${error.retryAfter}s`);
} else if (error instanceof InsufficientBalanceError) {
console.log("Please top up your balance");
} else if (error instanceof InvalidApiKeyError) {
console.log("Check your API key");
} else if (error instanceof ValidationError) {
console.log(`Invalid request: ${error.message}`);
} else if (error instanceof ProviderError) {
console.log("Data provider issue, try again later");
} else if (error instanceof TimeoutError) {
console.log("Request timed out");
} else if (error instanceof GTMRouterError) {
console.log(`${error.code}: ${error.message} (request: ${error.requestId})`);
}
}All errors extend GTMRouterError which extends Error, so you can also catch broadly:
try {
await gtm-router.email.find(params);
} catch (error) {
if (error instanceof GTMRouterError) {
console.log(error.code, error.statusCode, error.message);
}
}Retry Behavior
- 5xx errors: Automatically retried with exponential backoff (up to
maxRetries) - 4xx errors: Never retried (thrown immediately)
- Network errors: Automatically retried
- Timeouts: Thrown as
TimeoutError
License
MIT
