Typed all the way down.
Every method, argument and return type is derived from the same contract the API itself is built from. There is no codegen step to run and nothing to regenerate — a wrong chain slug is a compile error, not a 400 you discover in production.
Install
npm i foxd-sdkimport { FoxD } from "foxd-sdk";
const foxd = new FoxD({ apiKey: process.env.FOXD_API_KEY });
const { tokens } = await foxd.market.trending({ chain: "base", limit: 10 });What 'typed' actually buys you
The chain parameter is a union, not a string. Autocomplete lists the six real chains, and anything else fails to compile:
foxd.market.trending({ chain: "base" }); // fine
foxd.market.trending({ chain: "dogecoin" }); // Type '"dogecoin"' is not assignable
foxd.market.trending({ nonsense: 1 }); // Object literal may only specify known properties
foxd.market.doesNotExist({}); // Property 'doesNotExist' does not existResponses are shaped too — tokens[0].priceUsd is number | null, so the nullability that actually exists in market data is visible at the call site rather than discovered by a crash.
Two behaviours worth knowing
Mutating calls are never retried
Read calls retry transient failures with exponential backoff and jitter. Anything that moves money does not — a timeout does not mean the write failed, and replaying a trade to be helpful would be far worse than surfacing the ambiguity.
Errors are structured
import { FoxD, FoxDError } from "foxd-sdk";
try {
await foxd.trade.execute({ /* … */ });
} catch (e) {
if (e instanceof FoxDError) {
e.code; // "spend_cap_exceeded" — stable, matchable
e.status; // 403
e.retryable; // false
}
}A real safety check
const { tokens } = await foxd.market.trending({ chain: "base", limit: 5 });
const t = tokens[0];
const [provider, sim] = await Promise.all([
foxd.analytics.security({ chain: "base", address: t.address }),
foxd.analytics.simulateHoneypot({ chain: "base", address: t.address }),
]);
// simulated:false is NOT a pass — it means no conclusion was reached.
const sellable = sim.simulated && sim.canSell === true;
if (provider.isHoneypot || !sellable) throw new Error("not sellable");
const quote = await foxd.trade.quote({
chain: "base",
tokenIn: "0x0000000000000000000000000000000000000000",
tokenOut: t.address,
amountIn: "10000000000000000", // base units, as a string
side: "buy",
});
console.log(quote.venue, quote.estimatedUsd, quote.priceImpactBps);Amounts are base-unit strings — wei on EVM, lamports on Solana. An 18-decimal amount does not survive a float, so the SDK will not accept one.
Get an API key
Public market data needs no key. Everything account-, wallet- or trade-related does. Keys are free, scoped, and revocable in one click.