SC01Check

Developers

SC01Check API

Put Tesla VIN reports — Supercharging status, Full Self-Driving, battery, factory options, recalls — plus CARFAX and AutoCheck history into your own site, inventory system or listing feed. One REST endpoint set, JSON in and out, billed from the same credits you already buy.

Same credits

No API plan, no separate wallet, no minimum. A call costs exactly what the same action costs on the site.

Cache-aware

A VIN already in our system answers without a fresh upstream pull, and re-reading a report you own is always free.

Built for lots

Batch up to 50 VINs a call. If credits run out mid-batch it stops and tells you exactly which VINs are left.

Getting started

Create a key at API keys in your account. Keys look like sc01_live_… and are shown once — we store a hash, so a lost key must be revoked and replaced rather than recovered.

Send it as a bearer token on every request:

curl -H 'Authorization: Bearer sc01_live_YOUR_KEY' \
     https://sc01check.com/api/v1/ping
{
  "ok": true,
  "pong": true,
  "account": "you@dealership.com",
  "key": "Inventory sync",
  "credits_charged": 0,
  "credits_remaining": 480
}

Base URL https://sc01check.com/api/v1 · HTTPS only · JSON only. Every successful response carries credits_charged and credits_remaining, so you can reconcile spend without a second call.

Endpoint index

Everything, on one screen. Base https://sc01check.com/api/v1.

EndpointCostWhat it is for
GET /pingfree Check a key works.
GET /accountfree Credit balance and 30-day usage.
GET /vin/{VIN}/json1 credit Full payload to render in your own pages.
GET /vin/{VIN}/link1 credit A link to the report here, for a button on your listing.
GET /vin/{VIN}1 credit Same as /json. Add ?share=1 to also get a link.
GET /vin/{VIN}/historyfree What history exists, what you own, exact price each.
POST /history/{VIN}varies Buy CARFAX and/or AutoCheck.
GET /history/{VIN}/{kind}free Retrieve a report you own; ?format=html for the document.
POST /batch1 credit / VIN Up to 50 VINs at once.
POST /vin/{VIN}/listingfree Put a car on our public for-sale board.

Re-reading anything this account has already paid for is always free — retries after a timeout never double-charge.

What things cost

ActionCost
VIN report, VIN not in our system1 credit
VIN report, VIN already in our system1 credit (no upstream call)
Re-reading a VIN your account has already runFree
Forced fresh pull (?fresh=1)1 credit
CARFAX or AutoCheck, not on fileSee /vin/{VIN}/history
CARFAX or AutoCheck already on fileDiscounted — same endpoint quotes it
Re-reading a report you ownFree, forever

Quote before you buy: GET /vin/{VIN}/history returns the exact price for each report on that VIN, and how many records the provider holds — so you never spend on an empty document. Credits never expire.

Endpoints

GET /api/v1/ping

Checks a key. Costs nothing. Use it in your deploy smoke test.

GET /api/v1/account

Credit balance plus your last 30 days of API usage.

{
  "ok": true,
  "account": { "email": "you@dealership.com", "credits": 480 },
  "usage_30d": { "calls": 1204, "credits": 96 }
}

Pick your integration style

Two shapes of the same lookup, so you do not have to read a flag to get what you want. They cost the same, because the same work happens behind both.

EndpointGives youUse when
GET /vin/{VIN}/json The full payload — report, display, highlights You are rendering the car's details inside your own pages.
GET /vin/{VIN}/link A URL and the vehicle name You just want a “View EV report” button on your listing that opens the full report here — no rendering work at all.
curl -H 'Authorization: Bearer sc01_live_YOUR_KEY' \
     https://sc01check.com/api/v1/vin/5YJSA1E26HF000000/link
{
  "ok": true,
  "vin": "5YJSA1E26HF000000",
  "url": "https://sc01check.com/s/8fK2n4Qb7Lm9",
  "vehicle": "2017 Tesla Model S 100D",
  "charged": "paid",
  "credits_charged": 1,
  "credits_remaining": 479
}

The link is permanent, needs no account to open, and the same URL comes back on later calls for that VIN — safe to store next to your listing. Label it however you like: “View EV report”, “Supercharging status”, “Free VIN report”.

GET /api/v1/vin/{VIN}

The main call. Returns the full decoded report. Add ?fresh=1 to force a new upstream pull instead of serving what we hold.

curl -H 'Authorization: Bearer sc01_live_YOUR_KEY' \
     https://sc01check.com/api/v1/vin/5YJSA1E26HF000000
{
  "ok": true,
  "vin": "5YJSA1E26HF000000",
  "charged": "paid",
  "report": {
    "display_name": "2017 Model S 100D",
    "summary": { "model": "MODELS", "trim": "100D", "model_year": 2017,
                 "paint": "PPSW", "interior": "IBE00", "wheels": "WT19" },
    "supercharging": { "code": "SC01", "label": "Free Supercharging (transfers)",
                       "transferable": true },
    "fsd": { "owned": true, "verdict": "owned" },
    "autopilot": { "level": "fsd", "label": "Full Self-Driving" },
    "battery": { … },
    "headlights": { "matrix": true },
    "seating": { "count": 5 },
    "options": [ … ],
    "recalls": [ … ]
  },
  "credits_charged": 1,
  "credits_remaining": 479
}

You do not have to decode anything

Tesla's own data is codes and internal enums — whether Full Self-Driving is owned or a monthly subscription is not a field, it is a conclusion drawn from several. Working that out is our job, not yours, so every VIN response carries three layers and you pick one:

LayerUse it when
highlightsYou want badges on a listing page. An ordered array of things worth saying out loud, already filtered — loop and print.
displayYou want specific fields. Flat, documented, each one { value, label }.
reportYou want everything, including raw option codes and our internal structure.

The contract: value is a stable enum — filter, store and branch on it. label is a sentence for humans and may be reworded, so never key logic off it.

highlights — badges, ready to render

"highlights": [
  { "key": "supercharging", "label": "Free Supercharging — transfers", "tone": "good" },
  { "key": "fsd",           "label": "Full Self-Driving — owned",     "tone": "good" },
  { "key": "headlights",    "label": "Matrix headlights",             "tone": "good" },
  { "key": "seats",         "label": "5-seat",                        "tone": "neutral" },
  { "key": "recalls",       "label": "1 open recall",                 "tone": "caution" }
]

tone is good / neutral / caution — colour them however you like. A car with nothing notable returns an empty array rather than a row of "None" chips, so you can render it blind.

display — the field reference

Fieldvalue can beNotes
supercharging free_transferable · free_not_transferable · free_unknown_transfer · pay_per_use · unknown The question this site exists for. Only free_transferable means a buyer keeps it.
fsd owned · owned_not_transferable · subscription · removed · none · unknown The one people get wrong. subscription ends when the car sells — it is not an included feature. Also carries transfers and our raw verdict.
autopilotfsd · enhanced · basic · unknown Capability tier, independent of who paid for it.
headlightstrue · false · null Matrix headlights. null means no confirmed answer for that build — we do not guess.
paint, interior, wheelsoption code, or null value is the raw code for filtering, label is the human name.
seatsinteger, or null5, 6 or 7.
battery_healthnumber, or null Percent of original capacity where we can establish it.
original_msrpcents, or null List price of the configuration as it transfers. Tesla's own total includes anything the car currently has access to, so on a car with SUBSCRIBED Full Self-Driving that figure is thousands too high for a buyer — this one has it removed. When something was removed you also get list_total (Tesla's figure) and excludes.
open_recallsinteger Count; the full list is in report.recalls.
vehicle, model_year, model, trimstrings / integer vehicle is the printable name.
"display": {
  "vehicle":       { "value": "2017 Tesla Model S 100D", "label": "2017 Tesla Model S 100D" },
  "supercharging": { "value": "free_transferable",
                     "label": "Free Supercharging — transfers to the next owner",
                     "code": "SC01" },
  "fsd":           { "value": "owned",
                     "label": "Full Self-Driving — owned outright, transfers with the car",
                     "transfers": true, "verdict": "OWNED_OUTRIGHT", "confidence": "high" },
  "autopilot":     { "value": "fsd", "label": "Full Self-Driving" },
  "headlights":    { "value": true, "label": "Matrix headlights" },
  "paint":         { "value": "PPSW", "label": "Pearl White Multi-Coat" },
  "interior":      { "value": "IBE00", "label": "Black Premium Interior" },
  "wheels":        { "value": "WT19", "label": "19\" Silver Wheels" },
  "seats":         { "value": 5, "label": "5-seat" },
  "battery_health":{ "value": 92.4, "label": "92.4% of original capacity" },
  "original_msrp": { "value": 11350000, "label": "$113,500 as originally configured" },
  "open_recalls":  { "value": 1, "label": "1 open recall" }
}

Linking back: ?share=1

Add ?share=1 to a VIN call and the response gains share_url — a permanent, free-to-read link to the full rendered report on our site, no account needed for whoever opens it. Put it on your listing as "full VIN report" and your buyer gets the whole thing, including the Supercharging explanation, without you rebuilding any of it. The same URL comes back on later calls for that VIN, so it is stable to store.

GET /api/v1/vin/{VIN}/history

What history exists for a VIN, what you already own, and what each would cost. Costs nothing to ask.

{
  "ok": true,
  "vin": "5YJSA1E26HF000000",
  "reports": {
    "carfax":    { "owned": false, "on_file": true,  "record_count": 24,
                   "price": { "cents": 400, "credits": 16 } },
    "autocheck": { "owned": false, "on_file": false, "record_count": 11,
                   "price": { "cents": 800, "credits": 32 } }
  }
}

POST /api/v1/history/{VIN}

Orders history reports, paid from your credits.

curl -X POST -H 'Authorization: Bearer sc01_live_YOUR_KEY' \
     -H 'Content-Type: application/json' \
     -d '{"kinds":["carfax","autocheck"]}' \
     https://sc01check.com/api/v1/history/5YJSA1E26HF000000
{
  "ok": true,
  "vin": "5YJSA1E26HF000000",
  "ordered": ["carfax", "autocheck"],
  "already_owned": [],
  "retrieve": "https://sc01check.com/api/v1/history/5YJSA1E26HF000000/{kind}",
  "credits_charged": 48,
  "credits_remaining": 431
}

Body fields: kinds (array, defaults to both) and modeauto (default; buys what you lack at the best available price), access (only what is already on file), or fresh (force a new pull even if we hold a copy).

GET /api/v1/history/{VIN}/{kind}

Retrieves a report you own — carfax or autocheck. Free and unlimited once owned. Add ?format=html for the provider's full document, which you can render in an iframe or store; omit it for metadata only.

{
  "ok": true,
  "vin": "5YJSA1E26HF000000",
  "kind": "carfax",
  "provider": "CARFAX",
  "year_make_model": "2017 Tesla Model S",
  "record_count": 24,
  "fetched_at": "2026-08-04 19:22:10",
  "bytes": 318442
}

POST /api/v1/batch

Up to 50 VINs in one call — the dealer inventory path.

curl -X POST -H 'Authorization: Bearer sc01_live_YOUR_KEY' \
     -H 'Content-Type: application/json' \
     -d '{"vins":["5YJSA1E26HF000000","7SAYGDEE9PF000000"]}' \
     https://sc01check.com/api/v1/batch

Each VIN gets its own result object, so one bad VIN never fails the batch. If credits run out partway the response is a 402 carrying stopped_early, everything processed so far, and remaining_vins — top up and resend exactly that array.

A complete integration

What a dealer sync actually looks like end to end: decode the lot, keep a link on every listing page, and take cars off our board when they sell. Roughly thirty lines.

PHP

<?php
const SC01_KEY  = 'sc01_live_YOUR_KEY';
const SC01_BASE = 'https://sc01check.com/api/v1';

function sc01(string $method, string $path, ?array $body = null): array {
    $ch = curl_init(SC01_BASE . $path);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST  => $method,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . SC01_KEY,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS => $body === null ? null : json_encode($body),
        CURLOPT_TIMEOUT    => 30,
    ]);
    $raw    = curl_exec($ch);
    $status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    curl_close($ch);
    return [$status, json_decode((string) $raw, true) ?: []];
}

// 1. Decode the lot, 50 at a time.
foreach (array_chunk($yourVins, 50) as $chunk) {
    [$status, $r] = sc01('POST', '/batch', ['vins' => $chunk]);

    if ($status === 402) {          // out of credits: resume later, exactly here
        error_log('SC01Check credits exhausted; ' . count($r['remaining_vins']) . ' VINs left');
        break;
    }
    foreach ($r['results'] as $row) {
        if (empty($row['ok'])) { continue; }            // bad VIN, keep going

        // 2. Store the badges and the link against your listing.
        saveToYourDb($row['vin'], [
            'headline'   => $row['display']['vehicle']['label'],
            'badges'     => $row['highlights'],          // [{key,label,tone}, …]
            'sc_status'  => $row['display']['supercharging']['value'],
            'fsd_status' => $row['display']['fsd']['value'],
        ]);
    }
}

// 3. Give each listing a "View EV report" button.
[, $link] = sc01('GET', '/vin/' . $vin . '/link');
echo '<a href="' . htmlspecialchars($link['url']) . '">View EV report</a>';

// 4. Optionally put the car on the public board.
sc01('POST', '/vin/' . $vin . '/listing', [
    'listing_url' => 'https://yourdealership.com/inventory/' . $stockNumber,
    'price'       => 48995,
    'miles'       => 31200,
]);

// 5. When it sells, take it down.
sc01('POST', '/vin/' . $vin . '/listing', ['public' => false]);

Python

import requests

KEY  = "sc01_live_YOUR_KEY"
BASE = "https://sc01check.com/api/v1"
H    = {"Authorization": f"Bearer {KEY}"}

def chunks(seq, n):
    for i in range(0, len(seq), n):
        yield seq[i:i + n]

for chunk in chunks(your_vins, 50):
    r = requests.post(f"{BASE}/batch", json={"vins": chunk}, headers=H, timeout=30)
    data = r.json()

    if r.status_code == 402:                    # out of credits
        print("resume with:", data["remaining_vins"])
        break

    for row in data["results"]:
        if not row.get("ok"):
            continue
        save_to_your_db(
            vin        = row["vin"],
            headline   = row["display"]["vehicle"]["label"],
            badges     = row["highlights"],
            sc_status  = row["display"]["supercharging"]["value"],
            fsd_status = row["display"]["fsd"]["value"],
        )

# A link for the listing page
link = requests.get(f"{BASE}/vin/{vin}/link", headers=H).json()["url"]

# Put it on the public board, and pull it when sold
requests.post(f"{BASE}/vin/{vin}/listing", headers=H,
              json={"listing_url": listing_url, "price": 48995, "miles": 31200})
requests.post(f"{BASE}/vin/{vin}/listing", headers=H, json={"public": False})

Rendering the badges

highlights is built to be looped without inspection — it is already filtered to what is worth showing, and empty when a car has nothing notable.

<?php foreach ($car['badges'] as $b): ?>
  <span class="badge badge--<?= htmlspecialchars($b['tone']) ?>">
    <?= htmlspecialchars($b['label']) ?>
  </span>
<?php endforeach; ?>

How often to sync

  • Once per car, when it arrives. A VIN's factory build never changes — paint, wheels, seats, matrix headlights and original MSRP are fixed for life. Store them.
  • Re-check before a price drop or a long listing using ?fresh=1. Supercharging and FSD status CAN change while a car sits, because they follow the owner's account, not the metal.
  • Never poll. Re-reading a VIN you already ran is free, so a nightly full re-sync costs nothing — but it also tells you nothing new. Sync arrivals, not inventory.

Every failure uses this shape and one of the codes above — there is no endpoint that returns an HTML error page, and no code outside this list. Branch on error.code, show error.message to your own staff; the messages are written to be read by a human debugging an integration. A 402 and a 429 are the only two worth retrying automatically.

Versioning & stability

  • The version is in the path. /api/v1 keeps its promises: we add fields, we do not remove or rename them, and we do not change what an existing value means. Anything that would break you gets a /v2.
  • Parse leniently. New fields will appear in responses — treat unknown keys as harmless rather than erroring on them.
  • value enums are the contract. label strings are for humans and get reworded; never branch on them.
  • A field can be null when we do not have a confident answer — that is deliberate, not missing data. Render nothing rather than guessing, the same way we do.
  • Deprecations, if they ever happen, get an email to every account with an active key and at least 90 days.

Optional: put your cars on our board

If you are listing cars for sale anyway, you can push them onto our public for-sale board as part of the same sync. Buyers browsing there read the full report — plus any CARFAX or AutoCheck you have pulled — free, and the listing links back to wherever you sell.

POST /api/v1/vin/{VIN}/listing

curl -X POST -H 'Authorization: Bearer sc01_live_YOUR_KEY' \
     -H 'Content-Type: application/json' \
     -d '{"listing_url":"https://yourdealership.com/inventory/12345",
          "price":48995,"miles":31200}' \
     https://sc01check.com/api/v1/vin/5YJSA1E26HF000000/listing
{
  "ok": true,
  "vin": "5YJSA1E26HF000000",
  "public_status": "pending",
  "message": "Submitted. A person checks the link before it appears — usually the same day."
}
FieldRequiredNotes
priceYesAsking price in dollars.
milesYesOdometer.
listing_urlOne of these Where the car is for sale — your own site is fine.
keysavvy_urlOne of these If you use KeySavvy for the transaction.
publicNo Defaults true. Send false to update the details but keep the car off the board — or to pull one that has sold.
  • Run /vin/{VIN}/json or /link first — a board listing shows the car's report, so we need to have it. Posting a listing for a VIN we have not loaded returns 409 vin_not_loaded rather than quietly running a lookup you did not ask for.
  • Listing costs nothing. No credits, ever.
  • New listings are reviewed by a person before they appear — we open the link. Editing the link on a live listing sends it back for another look; price and mileage edits do not.
  • When a car sells, send {"public": false}. A board full of sold cars is worthless to everyone, and buyers can report stale listings.

Errors

Errors are always JSON, never an HTML page, and always shaped the same way:

{
  "ok": false,
  "error": { "code": "insufficient_credits",
              "message": "Not enough credits for that order." }
}
StatusCodeMeaning
400invalid_vinNot a valid 17-character VIN. Nothing charged.
400lookup_failedValid VIN we cannot decode — a non-Tesla on a Tesla endpoint, or an unsupported model. Nothing charged.
401unauthorizedMissing, malformed, revoked or unknown key.
402insufficient_creditsBuy more credits and retry. Partial batches tell you where to resume.
403not_ownedYou asked for a report this account has not bought.
404not_foundUnknown endpoint or version.
409vin_not_loadedListing posted for a VIN we have not loaded. Call /vin/{VIN}/json first.
400listing_incompleteA board listing needs price, mileage and at least one link.
400invalid_kindReport kind must be carfax or autocheck.
400invalid_listingA listing field would not parse — usually a link that is not a web address, or a price with stray characters. The message names the field.
400invalid_requestMalformed body. Batch wants {"vins": […]}.
400too_manyMore than 50 VINs in one batch. Split it.
400order_failedA history order could not be placed for a reason other than credits — the message says which.
400listing_failedThe listing saved but could not be submitted to the board. Retry; the details are kept.
405method_not_allowedRight path, wrong verb — GET to retrieve, POST to order or submit.
429rate_limited60 requests a minute per key. Honour Retry-After.
503not_readyReport bought but the provider has not delivered yet. Retry shortly; you are not charged again.

Things worth knowing

  • Rate limit: 60 requests a minute per key, and X-RateLimit-Remaining comes back on every response. Need more for a large import? Ask — it is a number in a config file, not a plan upgrade.
  • Idempotence: re-requesting a VIN or report your account already owns is free and returns the same data, so a retry after a timeout costs nothing. Safe to make your client retry.
  • Non-Tesla VINs are decoded too, via the government build record and recalls, and CARFAX/AutoCheck work on any VIN. Supercharging and FSD fields are Tesla-only and are simply absent otherwise — we do not invent them.
  • Caching: please cache what you pull. A VIN's factory build never changes, and re-reading your own reports is free anyway.
  • Display: if you show CARFAX or AutoCheck content to your users, your agreement is with those providers as much as with us — render the document as delivered rather than extracting pieces of it.
  • Keys are per account. Everything a key spends comes from that account's credits, and every call is logged against the key, so you can hand a key to a contractor and revoke it without touching the rest.
Create a key Buy credits

Building something and need an endpoint that is not here? Say so — most of what the site does can be exposed, and the list above grew from people asking.