@cardog/api
The TypeScript client for the Cardog API — the system of record for the Canadian vehicle. Decode VINs, resolve names to permanent refs, search live Canadian listings, quote the market, and check Transport Canada + NHTSA recalls behind one key.
Installation
npm install @cardog/api
# or
pnpm add @cardog/api
# or
yarn add @cardog/api
For React hooks, also install:
npm install @tanstack/react-query
Quick start
import { CardogV2 } from "@cardog/api";
const cardog = new CardogV2({ apiKey: process.env.CARDOG_API_KEY });
// Free text enters exactly once. Everything after this takes refs.
const { best } = await cardog.entities.resolve("2021 Civic");
// best.ref === "model-year:honda/civic/2021"
const vehicle = await cardog.vin.getByVin("2HGFC2F53MH500001");
const recalls = await cardog.recalls.vin("2HGFC2F53MH500001");
const quote = await cardog.quotes.getByRef(best.ref);
Canadian VIN decode coverage is 99.77%. Types, methods, and errors are generated from the same OpenAPI contract the API validates against, so the client cannot drift from the server.
Errors are typed and actionable. Every non-2xx carries { code, message, hint, docs_url, suggestions }. An unknown ref returns a 400 that names it and suggests the
nearest real ones — the API never fuzzy-matches you into the wrong vehicle.
import { APIErrorV2 } from "@cardog/api";
try {
await cardog.listings.search({ filters: { makes: ["make:teslla"] } });
} catch (err) {
if (err instanceof APIErrorV2) {
err.code; // "unknown_entity_refs"
err.hint; // what to do instead
err.suggestions; // nearest real refs — advisory, never auto-applied
}
}
Whole platform in one fetch: https://cardog.app/docs.md**
Reference: https://cardog.app/docs · Python: pip install cardog · Refs offline: @cardog/entities
Feeds
A feed is an organization's own set of vehicle identity rows, rebuilt every night and
handed over as files. Read it with the organization's crdg_org_ key. Every feed read
costs 0 credits.
import { CardogClient } from "@cardog/api";
const client = new CardogClient({ apiKey: process.env.CARDOG_ORG_KEY });
const { feeds } = await client.v2.feeds.list();
const run = await client.v2.feeds.latest("vehicle-identity@1");
run.runId; // the SHA-256 of the uncompressed full file
run.deliveryId; // the complete delivery context, when present
run.columns; // the files' columns, in order, under the names the organization chose
run.manifest; // kind, baselineRunId, artifacts with record counts and hashes
// Rows, streamed. The file is never held in memory.
for await (const row of run.rows("delta")) {
// new and changed rows since the run before
}
// Keep your own copy current. Picks the full file or the change file for you,
// checks every file against its SHA-256 first, and returns where you now stand.
const result = await client.v2.feeds.sync("vehicle-identity@1", {
since: lastRunId, // what the previous sync returned; omit the first time
sinceDeliveryId: lastDeliveryId, // retain the delivery ID when the result has one
reset: () => store.clear(),
upsert: (row) => store.upsert(row),
remove: (key) => store.remove(key),
});
lastRunId = result.runId; // result.mode is "full", "delta" or "unchanged"
lastDeliveryId = result.deliveryId; // save both IDs only after a successful sync
// Answer a VIN.
const index = await run.index();
const row = index.lookup("2T3W1RFV3SC776931"); // the row, or null when the pattern is not in the feed
run.rows(role), run.toJSON(role) and run.artifact(role) take "full" (the default),
"delta" or "deletes". run.artifact(role) is null when the run has no such file.
CSV, JSON Lines and JSON runs, plain or gzipped, read the same way in the browser and in
Node. Guide: cardog.app/docs/feeds.
The full checksum identifies content. The delivery ID also identifies the contract,
baseline and review context. Equal full checksums can have different delivery IDs.
For an exact delivery, sync returns unchanged only when both saved IDs match. A
delta must match both baseline IDs. If the context is absent or different, sync
requires a full reset. An exact record from an older producer can omit the public
baseline delivery ID; sync then uses the full file. Legacy feeds retain their
existing behavior. currentDeliveryId() returns the current exact ID, or null
for a legacy run or no current run.
The sync implementation checks both IDs. The delivery sync tests cover resets, deltas, missed deliveries, and legacy records. Use the MTO stack guide to review the complete producer path.
React Hooks
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { CardogClient } from "@cardog/api";
import { createHooks } from "@cardog/api/react";
const queryClient = new QueryClient();
const client = new CardogClient({ apiKey: "your-api-key" });
const { useVinDecode, useMarketOverview, useListingsSearch } = createHooks(client);
function App() {
return (
<QueryClientProvider client={queryClient}>
<VehicleLookup />
</QueryClientProvider>
);
}
function VehicleLookup() {
const { data, isLoading, error } = useVinDecode("1HGCM82633A123456");
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
{data?.make} {data?.model} ({data?.year})
</div>
);
}
API Reference
The sections below document the legacy v1 surface (CardogClient). New integrations
should use the v2 groups shown in the quick start — CardogV2 (or new CardogClient().v2):
entities, vin, specs, listings, instruments, quotes, tape, recalls,
safety, feeds, platform — documented in full at cardog.app/docs.
VIN Decoding
// Decode a VIN
const vehicle = await client.vin.decode("1HGCM82633A123456");
// Decode from image (base64)
const result = await client.vin.image(base64Image);
Market Analysis
// Market overview for a specific vehicle
const overview = await client.market.overview("Toyota", "Camry", 2022);
// Price distribution
const pricing = await client.market.pricing("Honda", "Civic", 2021);
// Geographic breakdown
const geography = await client.market.geography("Ford", "F-150", 2023);
// Market trends over time
const trends = await client.market.trends("Tesla", "Model 3", 2022, "month");
// Listing market position
const position = await client.market.position("listing-id");
// Overall market pulse
const pulse = await client.market.pulse({
priceRangeMin: 20000,
priceRangeMax: 50000,
});
Vehicle Listings
// Search listings with filters
const results = await client.listings.search({
makes: ["Toyota", "Honda"],
models: { Toyota: ["Camry", "Corolla"] },
year: { min: 2020, max: 2024 },
price: { min: 15000, max: 40000 },
odometer: { max: 50000 },
bodyStyles: ["Sedan", "SUV"],
fuelTypes: ["Gasoline", "Hybrid"],
});
// Get listing count
const count = await client.listings.count({ makes: ["BMW"] });
// Get facets for filtering UI
const facets = await client.listings.facets({ makes: ["Mercedes-Benz"] });
// Get specific listing
const listing = await client.listings.getById("listing-id");
Safety Recalls
// Search recalls
const recalls = await client.recalls.search({
country: "us", // or "ca" for Canada
makes: ["Toyota"],
models: ["RAV4"],
year: { min: 2019, max: 2023 },
});
Research & Specs
// Get vehicle lineup for a make
const lineup = await client.research.lineup("Toyota");
// Get model year details
const modelYear = await client.research.modelYear("Toyota", "Camry", 2023);
// Get vehicle images
const images = await client.research.images("Toyota", "Camry", 2023);
// Get available colors
const colors = await client.research.colors("Honda", "Civic", 2024);
Fuel & Charging
// Find gas stations
const gasStations = await client.fuel.search({
lat: 43.6532,
lng: -79.3832,
radius: 10,
fuelType: "REGULAR",
});
// Find EV charging stations
const chargers = await client.charging.search({
lat: 43.6532,
lng: -79.3832,
radius: 25,
minPower: 50, // kW
});
Locations
// Search dealer/seller locations
const dealers = await client.locations.search({
lat: 43.6532,
lng: -79.3832,
radius: 50,
});
// Get seller details
const seller = await client.locations.getById("seller-id");
EPA Efficiency
// Search EPA fuel economy data
const efficiency = await client.efficiency.search({
make: "Toyota",
model: "Prius",
year: 2023,
});
NHTSA Complaints
// Search vehicle complaints
const complaints = await client.complaints.search({
make: "Ford",
model: "Explorer",
year: { min: 2020, max: 2023 },
});
Error Handling
Every v2 failure throws APIErrorV2 carrying the full error envelope — code, hint,
docsUrl, the offending refs, nearest-ref suggestions, and field-level details.
See the quick start above for the idiomatic catch. Legacy v1 routes throw APIError
(status / code / message / data).
Query Keys
For manual React Query integration:
import { queryKeys } from "@cardog/api";
// Use in custom queries
const { data } = useQuery({
queryKey: queryKeys.vin.decode("1HGCM82633A123456"),
queryFn: () => client.vin.decode("1HGCM82633A123456"),
});
// Available key factories
queryKeys.vin.decode(vin)
queryKeys.market.overview(make, model, year)
queryKeys.listings.search(params)
queryKeys.recalls.search(params)
// ... and more
Configuration
const client = new CardogClient({
apiKey: "your-api-key",
baseUrl: "https://api.cardog.app", // Default
});
// Update config at runtime
client.setConfig({
apiKey: "new-api-key",
});
Pricing
Usage is credit-metered per call family, and the price of your next call is in the
headers of your last one (X-Credits-Rate, X-Credits-Remaining). The client keeps
the last set parsed on client.lastCredits, errors included (a 402 states the budget
too), so a budget-aware task never reads raw headers:
await client.v2.vin.getByVin("5YJSA1E28MF420053");
const { rate, remaining, allowance, reset, source } = client.lastCredits!;
// rate: what that call cost · remaining: allowance headroom + pack balance
// (-1 = custom plan) · reset: when the allowance rolls over · source: which
// pool paid ("allowance" | "pack" | "overage"), null when nothing was debited
The machine-readable rate card is a free endpoint — await client.v2.pricing() — and
the human one is cardog.app/pricing.
Links
Related Packages
@cardog/entities- The ref grammar: validate, build, and derive refs offline (Apache-2.0, no key)@cardog/corgi- Offline VIN decoder (no API key needed)cardogon PyPI - The Python sibling of this SDK
License
MIT