How do I call Cartroute from TypeScript?
Use the built-in fetch (Node.js 18+, Deno, Bun, edge runtimes) with the key in an Authorization header, a timeout via AbortSignal.timeout, one Idempotency-Key per logical request and retries only for 429, 5xx and network errors. Types for the response come straight from the OpenAPI description.
What does a small typed client look like?
type Offer = {
offer_id: string;
retailer: { id: string; name: string; membership_required: boolean };
price_usd: number;
shipping_usd: number;
effective_price_usd: number;
availability: { in_stock: boolean; stock_level: string };
buy_url: string;
};
type Product = {
id: string;
title: string;
price_summary: { best_retailer: string | null; best_effective_price_usd: number | null };
price_insight: { verdict: string; reason: string };
offers: Offer[];
};
type SearchResult = { results: Product[]; next_cursor: string | null; credits: { charged: number; remaining: number } };
export class CartrouteError extends Error {
constructor(public status: number, public code: string, public problem: Record<string, unknown>) {
super(String(problem.detail ?? `HTTP ${status}`));
}
}
const BASE = "https://cart-route.com/api/v1";
async function get<T>(path: string, params: Record<string, string> = {}, retries = 3): Promise<T> {
const url = new URL(BASE + path);
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
const idempotencyKey = crypto.randomUUID();
for (let attempt = 0; ; attempt++) {
let res: Response;
try {
res = await fetch(url, {
headers: { Authorization: `Bearer ${process.env.CARTROUTE_API_KEY}`, "Idempotency-Key": idempotencyKey },
signal: AbortSignal.timeout(15_000),
});
} catch (err) {
if (attempt >= retries) throw err;
await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
continue;
}
if ((res.status === 429 || res.status >= 500) && attempt < retries) {
const wait = res.status === 429 ? Number(res.headers.get("Retry-After") ?? 1) : 2 ** attempt;
await new Promise((r) => setTimeout(r, wait * 1000));
continue;
}
const body = await res.json();
if (!res.ok) throw new CartrouteError(res.status, body.code, body);
return body as T;
}
}
export const cartroute = {
search: (q: string, filters: Record<string, string> = {}) => get<SearchResult>("/search", { q, ...filters }),
product: (id: string) => get<{ product: Product }>(`/products/${encodeURIComponent(id)}`),
account: () => get<{ credits_remaining: number }>("/account"),
};
How do I use it?
const { results } = await cartroute.search("espresso machine", { max_price: "700", in_stock: "true" });
const cheapest = results[0]?.offers[0];
if (cheapest) {
console.log(`${cheapest.retailer.name}: $${cheapest.effective_price_usd.toFixed(2)} → ${cheapest.buy_url}`);
}
Can I generate the types instead?
Yes. Point any OpenAPI 3.1 code generator at https://cart-route.com/openapi.json. The hand-written types above cover the fields most agents use.