@fundedapi/client
v0.3.0
Published
Zero-dependency TypeScript client for FundedAPI — recently funded startups with hiring signals, contacts, and webhooks.
Maintainers
Readme
@fundedapi/client
Zero-dependency TypeScript client for FundedAPI — the free REST API for recently funded startups, hiring signals, and founder contacts.
npm install @fundedapi/client
# or
pnpm add @fundedapi/client
# or
bun add @fundedapi/clientRuns on Node 18+, Bun, Deno, and Cloudflare Workers — uses the platform's built-in fetch. Zero runtime dependencies.
Get a free API key: fundedapi.com/dashboard (GitHub login, no credit card, 100 calls/day forever).
Quick start
import { FundedAPI } from "@fundedapi/client";
const api = new FundedAPI({ apiKey: process.env.FUNDED_API_KEY });
// List startups
const { startups } = await api.listStartups({
niche: "AI",
hiring: true,
round: "Seed",
limit: 10,
});
// Natural-language search
const ai = await api.aiSearch({
query: "Seed-stage AI infra startups hiring in Europe",
});
// Single startup
const { startup } = await api.getStartup("clx1a2b3...");
// CSV export (streams text)
const csv = await api.exportStartupsCsv({ hiring: true, since: "2026-04-01" });If FUNDED_API_KEY is set in the environment you can call new FundedAPI() with no args.
CLI
Every install ships a fundedapi binary. Use it from the terminal with zero code.
# Your key from the env (or pass --key)
export FUNDED_API_KEY=fapi_...
# Dataset counters
npx @fundedapi/client stats
# Filtered list (all /v1/startups query params supported)
npx @fundedapi/client startups --niche=AI --hiring --round=Seed --limit=10
# One startup + contacts
npx @fundedapi/client get clx1a2b3c4d5e6f7g8h9i0j1k
# Natural-language search
npx @fundedapi/client ai "Seed-stage AI infra startups hiring in Europe"
# VC directory
npx @fundedapi/client investors --min-deals=5 --hiring-only
# Help
npx @fundedapi/client --helpJSON is printed to stdout so everything composes with jq:
npx @fundedapi/client startups --niche=AI --hiring --limit=50 | \
jq '.startups[] | select(.contacts | length > 0) | .contacts[0].linkedinUrl'All methods
| Method | Endpoint | Notes |
|---|---|---|
| listStartups(query?) | GET /v1/startups | Filters: niche, round, hiring, country, investor, min_amount, since, sort, limit, offset |
| getStartup(id) | GET /v1/startups/:id | Full detail + contacts |
| exportStartupsCsv(query?) | GET /v1/startups/export | Returns raw CSV text; requires an API key |
| aiSearch({ query }) | POST /v1/search/ai | Perplexity-powered NL → structured |
| stats() | GET /v1/stats | Aggregate counts |
| listInvestors(query?) | GET /v1/investors | VC directory with min_deals, hiring_only, limit |
| listWebhooks() | GET /v1/webhooks | Your subscriptions |
| createWebhook({ url, filters? }) | POST /v1/webhooks | Returns .secret ONCE — store it |
| deleteWebhook(id) | DELETE /v1/webhooks/:id | |
| testWebhook(id) | POST /v1/webhooks/:id/test | 1/min cooldown |
| rotateWebhookSecret(id) | POST /v1/webhooks/:id/rotate-secret | 10s cooldown; old secret invalid instantly |
Webhook signature verification
Every webhook delivery arrives with X-FundedAPI-Signature: sha256=<hex>. Verify before trusting the payload:
import { verifyWebhookSignature } from "@fundedapi/client";
export async function POST(req: Request) {
const raw = await req.text(); // MUST be the raw body — do not re-serialize
const sig = req.headers.get("x-fundedapi-signature");
if (!sig || !(await verifyWebhookSignature(raw, sig, process.env.WH_SECRET!))) {
return new Response("invalid signature", { status: 401 });
}
const event = JSON.parse(raw);
// ... your logic
return new Response("ok");
}Works on any runtime that has WebCrypto or node:crypto. Comparison is timing-safe.
Configuration
new FundedAPI({
apiKey: "fapi_...", // or set FUNDED_API_KEY env var
baseUrl: "https://fundedapi.com", // override for self-hosted deploys
maxRetries: 3, // 429 auto-retry with X-RateLimit-Reset awareness
timeoutMs: 30_000, // per-request
fetch: customFetch, // inject e.g. `fetch-undici` or a mocked fetch
headers: { "X-Tenant": "acme" } // merged into every request
});Automatic rate-limit handling
The client reads X-RateLimit-Reset (unix epoch) and Retry-After headers on 429 responses and sleeps exactly the right amount. If retries are exhausted you get a typed RateLimitError with the remaining wait in .retryAfter milliseconds.
Errors
import { FundedAPI, AuthError, RateLimitError, FundedAPIError } from "@fundedapi/client";
try {
await api.listStartups();
} catch (err) {
if (err instanceof AuthError) {
// 401 — bad or missing key
} else if (err instanceof RateLimitError) {
// 429 — exceeded retries; err.retryAfter is ms to wait
} else if (err instanceof FundedAPIError) {
// Other non-2xx; err.status, err.body available
}
}Platform notes
- Node 18+ — works out of the box with global
fetch - Bun / Deno — native support
- Cloudflare Workers — pass
fetchfrom the runtime if you want custom routing; otherwise global works - Node 16 — install
node-fetch@3and pass it via thefetchoption; HMAC verify falls back tonode:crypto
License
MIT — do anything. Attribution appreciated.
- Source: github.com/thequantumchronicle/fundedapi
- Docs: fundedapi.com/docs
- Recipes: fundedapi.com/recipes
- Status: fundedapi.com/status
