Skip to content

About

Unofficial, typed, zero-dependency TypeScript SDK for the Seamless.AI public API.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

@saiprasad4/seamless-api-sdk

A small, typed, zero-dependency TypeScript SDK for the Seamless.AI public API.

Search contacts and companies, run the asynchronous email-research (reveal) flow, and read credit and rate-limit headers, without re-learning the API's undocumented quirks.

Unofficial & community-maintained. Not affiliated with, endorsed by, or sponsored by Seamless.AI, Inc. "Seamless.AI" is a trademark of its respective owner. This package only calls the documented public API on your behalf, using your own API key.

tests types deps

Why this exists

The Seamless public API has a few traps that cost real time to discover:

  • It authenticates with a bare Token: header, not Authorization: Bearer.
  • Email reveal is two-phase and asynchronous: a free search returns searchResultIds, then a paid research call returns requestIds you poll until the email resolves.
  • Responses mix camelCase and snake_case, and records arrive under different envelope keys (contacts, results, data).
  • Credit balance and rate-limit state live in response headers (X-PublicAPI-Credits, X-RateLimit-*), not the body.

The SDK wraps all of that behind a typed surface. When the vendor renames a field, every result still carries the untouched raw record, so nothing drops silently.

Install

npm install @saiprasad4/seamless-api-sdk

Requires Node 18 or newer (uses the global fetch). Works in any modern runtime with fetch (Deno, Bun, edge); you can also inject your own via options.fetch.

Quick start

import { SeamlessClient } from '@saiprasad4/seamless-api-sdk';

const client = new SeamlessClient();
const apiKey = process.env.SEAMLESS_API_KEY!;

// 1) Free search: find marketing decision-makers at Indian D2C brands
const page = await client.searchContacts(apiKey, {
  jobTitle: ['Head of Marketing', 'CMO', 'Marketing Manager'],
  contactCountry: ['India'],
  companyKeyword: ['D2C', 'consumer brand'],
  limit: 25,
});

console.log(`Found ${page.data.results.length} contacts, ${page.creditsRemaining} credits left`);

// 2) Paid reveal: research and poll until emails resolve, in one call
const revealed = await client.researchAndWait(
  apiKey,
  page.data.results.map((c) => c.searchResultId),
  { maxWaitMs: 60_000 },
);

for (const r of revealed) {
  if (r.status === 'completed') console.log(r.email, r.phone);
}

Bring-your-own key pool (by design)

Every method takes the API key as the first argument instead of holding one. That's deliberate: it lets you own key rotation and credit metering without the SDK baking in any policy. The SeamlessCall envelope returns creditsRemaining and rateLimit on every response so your pool can decide when to rotate or back off:

class KeyPool {
  constructor(private keys: string[]) {}
  private i = 0;
  next() { return this.keys[this.i++ % this.keys.length]; }
}

const pool = new KeyPool(loadKeysFromVault());
const res = await client.searchContacts(pool.next(), filters);
if ((res.creditsRemaining ?? Infinity) < 50) pool.retire(/* current key */);

Use one key per Seamless account and stay within your plan's terms. This SDK ships no credit-multiplying behavior; rotation policy is yours to define responsibly.

Error handling

Every failure is a SeamlessError subclass, so you can branch precisely:

import {
  SeamlessAuthError,        // 401/403: key invalid or out of permission
  SeamlessRateLimitError,   // 429: has .retryAfterMs
  SeamlessApiError,         // other 4xx/5xx
  SeamlessNetworkError,     // network failure / timeout (no response)
} from '@saiprasad4/seamless-api-sdk';

try {
  await client.searchContacts(key, filters);
} catch (e) {
  if (e instanceof SeamlessAuthError) pool.retire(key);
  else if (e instanceof SeamlessRateLimitError) await wait(e.retryAfterMs ?? 5000);
  else throw e;
}

Transient failures (429, 5xx, network/timeout) are retried automatically with linear backoff (maxRetries, default 3). Auth and other 4xx errors are terminal and never retried.

API

new SeamlessClient(options?: {
  baseUrl?: string;       // default https://api.seamless.ai/api/client/v1
  timeoutMs?: number;     // default 30000
  maxRetries?: number;    // default 3
  retryDelayMs?: number;  // default 1000 (linear: delay * attempt)
  logger?: { warn(msg): void };
  fetch?: typeof fetch;   // inject for testing / proxies
})
Method Cost Returns
searchContacts(key, filters) free SeamlessCall<SeamlessSearchPage<SeamlessContactResult>>
searchCompanies(key, filters) free SeamlessCall<SeamlessSearchPage<SeamlessCompanyResult>>
researchContacts(key, searchResultIds) 1 credit/contact SeamlessCall<{ requestIds: string[] }>
pollResearch(key, requestIds) free SeamlessCall<{ results: SeamlessResearchResult[] }>
researchAndWait(key, searchResultIds, opts?) 1 credit/contact SeamlessResearchResult[]

All result objects include a raw: Record<string, unknown> with the untouched vendor record.

Status & contributing

v0.x. The search, research and poll core is covered by tests against a mocked transport. Field names follow the public docs. If you hit a response shape the mappers miss, the data is still in raw, and a PR adding the candidate key is welcome.

License

MIT © Saiprasad Shankar

About

Unofficial, typed, zero-dependency TypeScript SDK for the Seamless.AI public API.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages