How to Scrape Vehicle Data with an API
Learn how to collect vehicle data safely with APIs, decode VINs in batches, handle limits, and know when vPIC is not enough.
Use a documented vehicle-data API whenever one covers the fields you need. For basic U.S.-market VIN decoding and manufacturer-reported attributes, NHTSA’s Product Information Catalog and Vehicle Listing (vPIC) is the official starting point. It is free, requires no registration, supports JSON, CSV, and XML, and can decode individual or batched VINs.
vPIC is not a universal vehicle-history, pricing, mileage, inventory, or maintenance API. Define your required fields first, verify coverage, then select the documented source that actually provides them.
1. Define the vehicle data you need
“Vehicle data” can mean very different things. Write down the exact fields before choosing an endpoint or provider.
| Need | Typical source decision |
|---|---|
| VIN decoding | vPIC or another documented VIN decoder |
| Make, model, manufacturer, vehicle type | vPIC reference lookups |
| Safety recalls or complaints | Use NHTSA’s separately documented recall and complaint APIs |
| Current listings, asking prices, mileage, title or damage history | Evaluate a source that explicitly licenses and documents those fields |
| Maintenance records or auction history | Use a provider with documented coverage and permitted commercial use |
Also record the vehicle population, model-year range, geography, expected request volume, refresh interval, and whether results power an internal workflow or a public product.
2. Check vPIC coverage before writing code
NHTSA describes vPIC as intended for model years 1981 and newer, based on manufacturer submissions and focused primarily on vehicles intended for sale, use, or importation in the United States. A non-U.S.-market vehicle or unsupported model can return limited or missing values. Include the model year when you know it; NHTSA recommends it for VIN decoding and supports partial VIN decoding.
The vPIC API documentation and coverage information should be checked before production release because endpoint details, fields, and data releases can change: vPIC API documentation and vPIC information portal.
3. Decode one VIN with cURL
The following request asks vPIC for flat decoded values in JSON. Replace the VIN and model year with your input, and confirm the current path and parameters in the official documentation before deployment.
curl --fail-with-body \
'https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVinValues/1HGCM82633A004352?format=json&modelyear=2003'
The response contains a result object with named variables. Treat empty values as unknown, preserve the original VIN for diagnostics, and inspect any decode-error fields instead of assuming every returned field is authoritative.
4. Decode a VIN in Python
import json
import re
import requests
VIN_RE = re.compile(r"^[A-HJ-NPR-Z0-9]{11,17}$", re.IGNORECASE)
def decode_vin(vin: str, model_year: int | None = None) -> dict:
original = vin
vin = vin.strip().upper()
if not VIN_RE.fullmatch(vin):
raise ValueError("VIN must contain 11 to 17 letters or digits and omit I, O, and Q")
params = {"format": "json"}
if model_year is not None:
params["modelyear"] = str(model_year)
url = f"https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVinValues/{vin}"
response = requests.get(url, params=params, timeout=30)
response.raise_for_status()
payload = response.json()
results = payload.get("Results", [])
if not results:
raise RuntimeError(f"No decode result returned for {original}")
result = results[0]
return {
"input_vin": original,
"make": result.get("Make"),
"model": result.get("Model"),
"model_year": result.get("ModelYear"),
"manufacturer": result.get("Manufacturer"),
"vehicle_type": result.get("VehicleType"),
"error_code": result.get("ErrorCode"),
"error_text": result.get("ErrorText"),
"raw": result,
}
if __name__ == "__main__":
print(json.dumps(decode_vin("1HGCM82633A004352", 2003), indent=2))
Install the only dependency with python -m pip install requests. In an application, validate the model year range, log the source and request identifier, and keep the raw response beside normalized fields so a later data-quality review is possible.
5. Decode a VIN in Node.js
const vin = process.argv[2] || '1HGCM82633A004352';
const modelYear = process.argv[3] || '2003';
if (!/^[A-HJ-NPR-Z0-9]{11,17}$/i.test(vin)) {
throw new Error('VIN must contain 11 to 17 letters or digits and omit I, O, and Q');
}
const endpoint = new URL(
`https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVinValues/${encodeURIComponent(vin)}`
);
endpoint.searchParams.set('format', 'json');
if (modelYear) endpoint.searchParams.set('modelyear', modelYear);
const response = await fetch(endpoint, { signal: AbortSignal.timeout(30000) });
if (!response.ok) {
throw new Error(`vPIC returned HTTP ${response.status}`);
}
const payload = await response.json();
const result = payload.Results?.[0];
if (!result) throw new Error('No decode result returned');
console.log(JSON.stringify({
inputVin: vin,
make: result.Make,
model: result.Model,
modelYear: result.ModelYear,
manufacturer: result.Manufacturer,
vehicleType: result.VehicleType,
errorCode: result.ErrorCode,
errorText: result.ErrorText,
raw: result
}, null, 2));
Run it with node decode-vin.mjs 1HGCM82633A004352 2003 on a recent Node.js version that includes the built-in fetch API.
6. Batch-decode VINs
The documented flat-value batch endpoint accepts up to 50 VIN and model-year pairs per request. Its input is semicolon-separated; a year follows a VIN after a comma. Keep the original order and map each response back to its input.
curl --fail-with-body -X POST \
'https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVINValuesBatch/' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'format=json' \
--data-urlencode 'data=1HGCM82633A004352,2003;1N4AL11D75C109151,2005'
Send no more than the documented batch size, split larger jobs into modest requests, and retry only transient failures. Do not silently discard rows with blank fields or decode errors.
7. Use lookups when you do not have a VIN
vPIC also documents lookups for manufacturers, makes, models, variables, and vehicle types. These are useful for building selectors or validating user input, but they do not turn the service into a current inventory or pricing feed. Use the current vPIC documentation for exact endpoint paths, parameter casing, and response formats.
8. Parse and validate defensively
- Normalize whitespace and uppercase the VIN, but retain the original input.
- Reject characters that cannot occur in a VIN, especially I, O, and Q.
- Supply the model year whenever it is known, particularly for partial VINs.
- Select JSON, CSV, or XML explicitly and parse that format; do not scrape a human-facing display.
- Check HTTP status, empty responses, decode-error fields, and missing variables.
- Store the source, retrieval time, input VIN, model year, and raw response with normalized data.
- Make downstream code tolerate new variables and blank values.
NHTSA’s FAQ describes about 99% confidence for decoding model year 1995 forward for most major brands. Treat that as the agency’s stated confidence level, not a guarantee for every VIN or every returned field.
9. Rate limits, scheduling, and local operation
The public API is free and does not require registration. NHTSA applies automated traffic control and asks users with large batch workloads to schedule them at night Eastern Time or on weekends. Very large batches can bog down the shared service.
Use a bounded queue, exponential backoff with jitter for transient errors, and a low initial concurrency. Avoid inventing a numeric rate limit that the documentation does not publish. For VIN-only workloads where local operation is suitable, NHTSA offers standalone databases that avoid API quotas and rate limits. Those databases are limited to VIN decoding and do not replace API calls for other lookups.
10. Reliability, freshness, and data quality
A successful HTTP response means the service returned a response; it does not mean every field is complete or verified. Track field-level completeness, sample decoded records for manual review, and expose the source and retrieval date to users of your system.
Cache results only when the retention period and update needs fit the source’s current terms. If your product shows changing recalls, listings, or prices, define a refresh policy separately from VIN decoding. For high-volume pipelines, make jobs idempotent by keying them on normalized VIN, model year, and source version.
11. Cost and provider selection
vPIC itself is free for public use. Your real costs are storage, retries, queueing, monitoring, and any separate provider required for fields outside vPIC’s scope.
| Selection question | Why it matters |
|---|---|
| Which fields are included? | VIN decode is different from listings, value, mileage, recalls, complaints, or history. |
| Where and when is coverage available? | Check country, model year, vehicle type, and update cadence. |
| What is the provenance? | Manufacturer-reported values and user-submitted listings have different meanings. |
| How are batches and traffic controlled? | Limits affect queue design and completion time. |
| Can data be retained or redistributed? | Review current terms and commercial-use permissions before launch. |
| Which formats and SDKs exist? | JSON, CSV, XML, and ordinary HTTP clients change integration effort. |
12. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| HTTP 400 or a validation error | Malformed VIN, unsupported parameter, or incorrectly encoded batch body | Normalize the VIN, URL-encode query values, use the documented form encoding, and inspect the response body. |
| Many blank variables | The manufacturer did not submit that field, or the vehicle is outside expected coverage | Keep blanks as unknown, check model year and market, and choose a source that documents the missing field. |
| Partial VIN gives an unexpected result | Insufficient characters or missing model year | Provide the model year when known and treat the result as a candidate decode. |
| Requests slow down or fail intermittently | Automated traffic control or oversized batches | Reduce concurrency, split batches, back off with jitter, and schedule large jobs during NHTSA’s suggested off-peak windows. |
| Batch rows appear mismatched | Input order was not preserved by application code | Attach an internal index to every request row and verify the returned VIN before storing it. |
| Decode succeeds but the user expects price or history | vPIC does not provide that data class | Integrate a separately documented recalls, complaints, listing, valuation, or history source. |
| JSON parser fails | The request returned another format or an error document | Set the format explicitly, check the content type, and inspect HTTP status before parsing. |
13. Legal and operational boundaries
An API’s public availability does not decide whether collecting data from a particular listing site is permitted. Use a published API where possible, obtain permission when required, and review the target source’s current terms and applicable rules for your jurisdiction. This is practical guidance, not a universal legal conclusion.
14. Or skip the browser setup
If your workflow also needs screenshots of vehicle listing pages, you can call ScreenshotNeo directly instead of maintaining browser automation. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets AI agents take screenshots, inspect pages, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 shots.
Create a free ScreenshotNeo account to get started.
FAQ
Is vPIC a complete vehicle-history API?
No. It is primarily a VIN decoder and manufacturer-data service. Use separately documented sources for recalls, complaints, listings, pricing, mileage, title, damage, or maintenance history.
Can I decode vehicles sold outside the United States?
Coverage may be limited because vPIC is oriented toward vehicles reported for sale, use, or importation in the United States. Verify the specific market and model.
Does every VIN need a model year?
No, but NHTSA recommends supplying it when known, especially for partial VINs and older vehicles.
Can I send thousands of VINs in one request?
No. The documented flat batch endpoint accepts up to 50 VIN/year pairs per request. Queue additional batches and respect automated traffic controls.
Should I store decoded results forever?
Choose retention and refresh rules based on your product’s freshness needs and the source’s current terms. Keep provenance so users can understand where each field came from.


