@fedpulse/sdk
v1.0.10
Published
Official JavaScript/TypeScript SDK for the FedPulse API — federal contracts, exclusions, entities, assistance listings, and market intelligence.
Maintainers
Readme
@fedpulse/sdk
Official JavaScript / TypeScript SDK for the FedPulse API. Search federal contract opportunities, check vendor exclusions, retrieve entity registrations, and access market intelligence — all with zero runtime dependencies.
Features
- Typed end-to-end — full TypeScript definitions for every request and response
- Dual ESM + CJS — works in Node.js (ESM / CommonJS), Deno, Bun, and bundlers
- Zero runtime dependencies — only uses Node.js built-ins (
fetch,crypto) - Automatic retry — exponential back-off with full jitter for 5xx / network errors
- In-memory LRU cache — 256-entry, 60 s TTL on all GET requests
- Webhook verification — HMAC-SHA256 signature + replay-attack protection
- Cursor + offset pagination — async generator helpers for all list endpoints
Requirements
- Node.js ≥ 18.0.0 (native
fetch+crypto) - A FedPulse API key — generate one at app.fedpulse.dev/dashboard
Installation
npm install @fedpulse/sdkQuick start
import { FedPulse } from '@fedpulse/sdk';
const client = new FedPulse({ apiKey: process.env.FEDPULSE_API_KEY! });
// Search opportunities
const result = await client.opportunities.list({
q: 'cybersecurity',
naics: ['541519', '541512'],
postedAfter: '2025-01-01',
limit: 25,
});
console.log(result.data); // typed OpportunitySummary[]
console.log(result.pagination); // { total, page, limit, … }Authentication
Pass your API key to the constructor. It is sent as X-Api-Key: <key> on every request.
const client = new FedPulse({ apiKey: 'fp_live_…' });Never hard-code keys in source — use environment variables.
Resources
client.opportunities
// List / search opportunities
await client.opportunities.list({ q: 'cloud', naics: ['541512'] });
// Retrieve a single opportunity
await client.opportunities.get('abc123-notice-id');
// Aggregate statistics
await client.opportunities.stats({ postedAfter: '2025-01-01' });
// Exports are background jobs — createExport() returns immediately with a job ID.
// Poll getExport() until status === 'done', then call downloadExport().
const job = await client.opportunities.createExport({ format: 'csv', q: 'AI' });
let status = job.data;
while (status.status !== 'done' && status.status !== 'failed') {
await new Promise(r => setTimeout(r, 3000)); // wait 3s between polls
status = (await client.opportunities.getExport(status.exportId)).data;
}
if (status.status === 'done') {
const response = await client.opportunities.downloadExport(status.exportId);
const csv = await response.text(); // or response.body for streaming
}
// Async generator — walks all pages automatically
for await (const page of client.opportunities.paginate({ q: 'defense' })) {
console.log(page.data);
}client.exclusions
// List all current exclusions
await client.exclusions.list({ activeOnly: true });
// Retrieve one record
await client.exclusions.get('exclusion-id');
// Bulk check up to 100 entities
await client.exclusions.check({
entities: [
{ uei: 'ABC123DEF456' },
{ cage: '1A2B3' },
{ name: 'Acme Corp' },
],
});
// Stats
await client.exclusions.stats();
// Page through all exclusions
for await (const page of client.exclusions.paginate()) {
console.log(page.data);
}client.entities
// List registered entities
await client.entities.list({ naics: ['336411'], state: 'VA' });
// Entity by UEI
await client.entities.get('ABCDEF123456');
// Contracts awarded to an entity
await client.entities.opportunities('ABCDEF123456', { limit: 50 });
// Exclusion check (never cached)
await client.entities.exclusionCheck('ABCDEF123456');
// Stats
await client.entities.stats();
// All pages
for await (const page of client.entities.paginate({ state: 'TX' })) {
console.log(page.data);
}client.intelligence
// 360° entity intelligence profile
await client.intelligence.entityProfile('ABCDEF123456');
// Market analysis for a NAICS code
await client.intelligence.marketAnalysis('541512');
// Single-entity compliance check
await client.intelligence.complianceCheck({ uei: 'ABCDEF123456' });client.assistance
Federal grants and assistance listings (CFDA programs).
// Search listings
await client.assistance.list({ q: 'broadband', agency: 'USDA' });
// Retrieve one by CFDA program number (format: "XX.XXX")
await client.assistance.get('10.001');
// Aggregated stats
await client.assistance.stats();
// Auto-paginate all listings
for await (const page of client.assistance.paginate({ agency: 'HHS' })) {
console.log(page.data);
}client.analytics
Per-user API usage analytics — scoped to the authenticated key.
// KPI totals (requests today, this month, error rates)
await client.analytics.summary({ range: '30d' });
// Per-day breakdown for charting (up to 90 days)
await client.analytics.chart({ range: '30d' });
// Top endpoints by request volume and P95 latency
await client.analytics.endpoints({ range: '7d' });
// Paginated raw request log
await client.analytics.logs({ limit: 20 });client.webhooks
// List configured webhooks
await client.webhooks.list();
// Get a single webhook
await client.webhooks.get('00000000-0000-0000-0000-000000000001');
// Create
await client.webhooks.create({ url: 'https://example.com/hook', events: ['opportunity.new'] });
// Update / delete
await client.webhooks.update('00000000-0000-0000-0000-000000000001', { is_active: false });
await client.webhooks.delete('00000000-0000-0000-0000-000000000001');
// Send a test event to the endpoint immediately
await client.webhooks.test('00000000-0000-0000-0000-000000000001');
// Resume a paused webhook (auto-paused after 5 consecutive failures)
await client.webhooks.resume('00000000-0000-0000-0000-000000000001');
// Deliveries log
await client.webhooks.listDeliveries('00000000-0000-0000-0000-000000000001', { status: 'failed' });
await client.webhooks.getDelivery('00000000-0000-0000-0000-000000000001', '00000000-0000-0000-0000-000000000002');
// Auto-paginate all delivery history
for await (const page of client.webhooks.deliveryPages('00000000-0000-0000-0000-000000000001', { status: 'failed' })) {
console.log(page.data);
}Webhook signature verification
import { FedPulse } from '@fedpulse/sdk';
// In your Express / Fastify handler:
app.post('/webhooks', (req, res) => {
const { signatureHeader, timestampHeader } = FedPulse.extractWebhookHeaders(req.headers);
let event;
try {
event = FedPulse.verifyWebhook({
rawBody: req.body, // Buffer or string — the raw unparsed body
signatureHeader,
timestampHeader,
secret: process.env.FEDPULSE_WEBHOOK_SECRET!,
options: { maxAgeSeconds: 300 },
});
} catch (err) {
return res.status(400).send('Invalid signature');
}
console.log(event.event, event.data);
res.sendStatus(200);
});Pagination helpers
Every list resource exposes a paginate() async generator that walks all pages for you:
const allOpps: OpportunitySummary[] = [];
for await (const page of client.opportunities.paginate({ q: 'cloud' })) {
allOpps.push(...page.data);
}Error handling
All errors extend FedPulseError. Import specific classes for precise handling:
import {
FedPulse,
AuthenticationError,
PermissionError,
NotFoundError,
ValidationError,
RateLimitError,
ServerError,
NetworkError,
TimeoutError,
RetryExhaustedError,
} from '@fedpulse/sdk';
try {
await client.opportunities.get('bad-id');
} catch (err) {
if (err instanceof NotFoundError) {
console.log('Not found');
} else if (err instanceof RateLimitError) {
console.log(`Rate limited. Retry after: ${err.retryAfter}s`);
} else if (err instanceof RetryExhaustedError) {
console.log(`Failed after ${err.attempts} attempts`, err.lastError);
} else if (err instanceof NetworkError) {
console.log('Network issue — check internet connection');
}
}| Class | Status | When thrown |
|---|---|---|
| AuthenticationError | 401 | Invalid or missing API key |
| PermissionError | 403 | Key lacks required permission |
| NotFoundError | 404 | Resource does not exist |
| ValidationError | 400/422 | Bad request parameters |
| RateLimitError | 429 | Too many requests (check retryAfter) |
| ServerError | 5xx | FedPulse server error |
| NetworkError | — | DNS failure, ECONNREFUSED, etc. |
| TimeoutError | — | Request exceeded timeoutMs |
| RetryExhaustedError | — | All retry attempts failed |
Configuration
const client = new FedPulse({
apiKey: 'fp_live_…', // required
baseUrl: 'https://api.fedpulse.dev', // optional override
timeoutMs: 30_000, // per-attempt timeout (default: 30 s)
maxRetries: 3, // retries for 5xx / network errors (default: 3)
cacheSize: 256, // LRU cache entries, 0 to disable (default: 256)
cacheTtlMs: 60_000, // cache TTL in ms (default: 60 s)
});Rate limit info
const { data } = await client.opportunities.list({ q: 'cloud' });
console.log(client.rateLimit);
// { limit: 1000, remaining: 987, reset: 1740000000 }Clear cache
client.clearCache();CommonJS usage
const { FedPulse } = require('@fedpulse/sdk');
const client = new FedPulse({ apiKey: process.env.FEDPULSE_API_KEY });Community
- 💬 Discord — Ask questions, share what you build, get help in
#help - 🔧 Dashboard — Manage API keys, view logs and usage
- 📖 Docs — Full API reference
- 🐛 Bug reports — GitHub Issues for the API
License
MIT © FedPulse
