cardog
The Python SDK 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.
pip install cardog
import os
from cardog import Cardog
client = Cardog(api_key=os.environ["CARDOG_API_KEY"])
# Free text enters exactly once. Everything after this takes refs.
result = client.v2.entities.resolve("2021 Civic")
ref = result.best.ref # "model-year:honda/civic/2021"
identity = client.v2.vin.get("2HGFC2F53MH500001")
recalls = client.v2.vin.recalls("2HGFC2F53MH500001")
quote = client.v2.quotes.get(ref)
Canadian VIN decode coverage is 99.77%. Types and methods are generated from the same OpenAPI contract the API validates against — this SDK and the TypeScript one are siblings, not translations.
Errors are instructions. Every non-2xx v2 response raises a typed CardogAPIError carrying code, hint, docs_url, and nearest-ref suggestions, so a wrong ref is corrected on the next call instead of silently matched to the wrong vehicle.
Whole platform in one fetch: https://cardog.app/docs.md
Quick start — resolve → refs → query
v2 is ref-native: free text enters the API in exactly one place (entities.resolve), everything else speaks canonical refs like make:tesla or model-year:toyota/rav4/2021.
from cardog import Cardog
client = Cardog(api_key="your-api-key")
# 1. Free text → refs with confidence (the front door)
resolved = client.v2.entities.resolve("tesla model y")
best = resolved.best # None when nothing clears the confidence floor —
# the API never guesses for you
print(best.ref) # "model:tesla/model-y"
# 2. Dereference a ref: node + parents/children + counts + links
detail = client.v2.entities.get(best.ref)
print(detail.name, detail.counts)
# 3. Query with refs — never fuzzy, never guessed
results = client.v2.listings.search(
filters={
"makes": ["make:tesla"],
"price": {"max": 60000},
"year": {"min": 2022},
},
sort="price",
order="asc",
)
for listing in results.listings:
print(f"{listing.year} {listing.make} {listing.model} — ${listing.price:,.0f}")
VIN → identity, recalls, market
# VIN → graph identity card (refs, grains, links)
identity = client.v2.vin.get("5YJSA1E26MF420053")
print(identity.make, identity.model, identity.refs.model_year)
# Batch decode (metered per VIN, max 1000) — one bad VIN fails its own row, never the batch
batch = client.v2.vin.batch(["5YJSA1E26MF420053", "1HGCV1F34LA045661"])
# Recalls affecting a VIN (Transport Canada + NHTSA fused)
recalls = client.v2.recalls.vin("5YJSA1E26MF420053")
# VIN → market instrument bridge, quotes, tape
instrument = client.v2.vin.instrument("5YJSA1E26MF420053")
quote = client.v2.quotes.get("model-year:toyota/rav4/2021")
bars = client.v2.tape.history("model-year:toyota/rav4/2021", window="3m")
Errors are instructions
Every non-2xx v2 response raises CardogAPIError carrying the full error envelope — code is machine-dispatchable, hint says what to do next, and suggestions carries nearest-ref candidates so a typo'd ref self-corrects in one turn:
from cardog import Cardog, CardogAPIError
client = Cardog(api_key="your-api-key")
try:
client.v2.listings.search(filters={"makes": ["make:teslla"]})
except CardogAPIError as e:
print(e.status_code) # 400
print(e.code) # "unknown_entity_refs"
print(e.hint) # "Resolve free text to refs at GET /v2/entities/resolve?q=teslla"
print(e.docs_url) # "https://cardog.app/docs/errors#unknown_entity_refs"
print(e.refs) # ["make:teslla"]
for s in e.suggestions or []:
print(s["invalid"], "→", [n["ref"] for n in s["nearest"]])
# "make:teslla" → ["make:tesla"] (advisory — never auto-applied)
Async
AsyncCardog mirrors the whole surface:
import asyncio
from cardog import AsyncCardog
async def main():
client = AsyncCardog(api_key="your-api-key")
identity = await client.v2.vin.get("5YJSA1E26MF420053")
results = await client.v2.listings.search(filters={"makes": ["make:tesla"]})
await client.close()
asyncio.run(main())
The v2 surface
| Group | Methods |
|---|---|
client.v2.entities |
browse(domain, ...) · resolve(q, ...) · get(ref) |
client.v2.vin |
get(vin) · batch(vins) · recalls(vin) · listings(vin) · instrument(vin) |
client.v2.specs |
catalog() · sheet(ref, trim=...) |
client.v2.listings |
search(...) · count(...) · facets(...) · by_vin(vin) · by_id(id) |
client.v2.instruments |
search(q=..., limit=...) · get(ref, window=...) |
client.v2.quotes |
get(ref) · get_many(refs) |
client.v2.tape |
live(limit=...) · history(ref, window=...) |
client.v2.recalls |
vin(vin) · entity(ref) · feed() · stats() · get(ref) |
client.v2.safety |
ratings(ref) · complaints(ref, page=..., limit=...) |
| platform meta | client.v2.pricing() · client.v2.openapi() (both unauthenticated) |
The machine-readable rate card (pricing()) plus the X-Credits-* response headers let you budget mid-task; openapi() returns the same contract this SDK is generated from.
v1 (legacy)
The pre-1.0 resources keep working unchanged — client.vin.decode(...), client.listings.search(...), client.market, client.recalls, client.charging, client.fuel, and the rest ride under the same client with the same signatures. New integrations should target client.v2.*.
One behavioural note for 1.0: api_key is now optional (Cardog() works) so the unauthenticated platform-meta routes are reachable without a key. Everything else requires a key, exactly as before.
Configuration
client = Cardog(
api_key="your-api-key", # or omit for pricing()/openapi() only
base_url="https://api.cardog.app/v1", # default; a trailing /v1 is stripped for v2 calls
timeout=30.0,
max_retries=2, # retries 408/429/5xx with backoff, honours Retry-After
)
Regenerating the v2 surface (maintainers)
make generate # emit openapi.v2.json from @cardog/contracts, regenerate src/cardog/v2/
make check # generate twice + assert zero diff + run tests
src/cardog/v2/ (types + group resources) is generated by scripts/generate.py from packages/contracts/dist/openapi.v2.json — do not edit those files by hand. The method map (operationId → Python method) lives in the generator and is validated against the spec on every run.
License
MIT
mcp-name: app.cardog/mcp
Release files for cardog 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cardog-1.0.0.tar.gz | 75.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cardog-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 133.7 kB
Release files / cardog-1.0.0.tar.gz
| Download URL | cardog-1.0.0.tar.gz |
|---|---|
| Size | 75.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9ef704626c5d105a88235c0c19cf1e0b11b390cd7182e1396347ff826cd755dc
|
|
BLAKE2b-256 checksum How to use checksums |
9da306f4752f5160ed72836e5ae2508741e7c30627468a4f8a87634f97467bee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.8
|
Release files / cardog-1.0.0-py3-none-any.whl
| Download URL | cardog-1.0.0-py3-none-any.whl |
|---|---|
| Size | 58.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0153e324fb6101e664dbd7d1b9bdd4dbf83370522d47f8858732a59c2708713f
|
|
BLAKE2b-256 checksum How to use checksums |
89933b4fb1bde2621356a3fd22f955ac7a3d43ebceb815106c97fbdde55f4673
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.8
|