---
title: "Python"
description: "Official CarsXE SDK for Python."
canonical_url: "https://docs.carsxe.com/docs/sdks/python"
markdown_url: "https://docs.carsxe.com/docs/sdks/python.md"
last_updated: "1980-01-01"
---

# Python
URL: /docs/sdks/python
LLM index: /llms.txt
Description: Official CarsXE SDK for Python.

The `carsxe` package is the official CarsXE client for Python. It is async-first, built on `httpx` and `pydantic` v2. Requires Python 3.9 or later.

## Installation

```bash
pip install carsxe
# uv add carsxe
# poetry add carsxe
```

## Quick start

The client is an async context manager. Use `async with` so the underlying HTTP connection is closed when you're done.

```python
import asyncio
import os
from carsxe import CarsXE

async def main():
    async with CarsXE(api_key=os.environ['CARSXE_API_KEY']) as client:
        result = await client.decode_vin('WBAFR7C57CC811956')
        print(result.data.make, result.data.model, result.data.year)

asyncio.run(main())
```

If you need to reuse the client across many calls (e.g. in a long-running server), instantiate it directly and call `await client.close()` when done instead.

```python
client = CarsXE(api_key=os.environ['CARSXE_API_KEY'])

try:
    result = await client.decode_vin('WBAFR7C57CC811956')
finally:
    await client.close()
```

## Methods

### Core

| Method | Description |
|---|---|
| `decode_vin(vin)` | Decode a 17-character VIN into full vehicle specifications |
| `get_market_value(vin, *, mileage, state, condition)` | Retail, trade-in, and auction value estimates |
| `get_recalls(*, vin, make, model, year)` | Open safety recalls — pass VIN or make/model/year |
| `decode_plate(plate, state)` | VIN lookup from a license plate (50+ countries) |
| `get_vehicle_history(vin)` | Title events, salvage, junk, and insurance records |

### Bulk recall batch

| Method | Description |
|---|---|
| `submit_bulk_recall_batch(vins, *, csv, csv_url, webhook_url)` | Submit a batch of VINs |
| `get_bulk_recall_batch_status(batch_id)` | Poll job status |
| `get_bulk_recall_batch_results(batch_id)` | Retrieve completed results |
| `get_bulk_recall_batch_download_url(batch_id)` | Get a CSV download URL |

## Error handling

By default the client raises `CarsXEError` on non-2xx responses. The exception carries `status_code`, `code`, and `message`.

```python
from carsxe import CarsXE, CarsXEError
import os

async with CarsXE(api_key=os.environ['CARSXE_API_KEY']) as client:
    try:
        result = await client.decode_vin('WBAFR7C57CC811956')
    except CarsXEError as e:
        print(e.status_code, e.code, e.message)
```

Pass `throw_on_error=False` to suppress exceptions and inspect `result.error` instead:

```python
client = CarsXE(api_key=os.environ['CARSXE_API_KEY'], throw_on_error=False)
result = await client.decode_vin('INVALID')
if result.error:
    print(result.error.message)
```

## Best practices

**Always use `async with` or call `close()`.** The SDK uses an `httpx.AsyncClient` internally — leaving it open leaks connections.

**Store your key in an environment variable.**

```bash
# .env
CARSXE_API_KEY=your_key_here
```

```python
import os
from dotenv import load_dotenv

load_dotenv()
api_key = os.environ['CARSXE_API_KEY']
```

**Reuse the client.** In a web server (FastAPI, Django async views), create the client once at startup and close it on shutdown — don't instantiate per-request.

```python
# FastAPI example
from contextlib import asynccontextmanager
from fastapi import FastAPI
from carsxe import CarsXE

carsxe: CarsXE

@asynccontextmanager
async def lifespan(app: FastAPI):
    global carsxe
    carsxe = CarsXE(api_key=os.environ['CARSXE_API_KEY'])
    yield
    await carsxe.close()

app = FastAPI(lifespan=lifespan)
```

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
