VinPartsAI API v1

VinPartsAI API v1

Resolve OEM part numbers from a VIN or license plate plus a free-text part name — original OE, factory supersessions, and verified analogs. A clean REST API, the machine twin of your dashboard.

Overview Authentication Create lookup Get lookup Clarify Webhooks List lookups Statuses Errors

Overview

Authentication

Every request carries a personal Bearer token in the Authorization header. Tokens are issued per client on approval — contact us to get yours.

# Every request
Authorization: Bearer <your-token>
Accept: application/json
Requests without a valid token return 401 Unauthorized. A token belongs to one account and only ever sees that account's lookups. Rate limit: 120 requests/minute per token.

Create a lookup

POST/api/v1/lookups

FieldTypeDescription
vehiclestringrequired17-char VIN, or a license plate. We auto-detect and resolve a plate to its VIN.
partstringrequiredFree-text part name, e.g. front bumper, right headlight.
want_replacementsbooleanoptionalAlso return factory supersessions. Default false.
want_analogsbooleanoptionalAlso return verified aftermarket analogs (implies replacements). Default false.
langstringoptionalReserved for output localization. Accepted, not yet applied.
callback_urlstring (URL)optionalWe POST the result here when the lookup finishes — see Webhooks. Omit to poll instead.
# Request
curl -X POST https://vinpartsai.com/api/v1/lookups \
  -H "Authorization: Bearer <your-token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"vehicle": "JTDBR32E720123456", "part": "front bumper"}'
# 201 Created
{
  "data": {
    "id": 42,                    // your reference number for this lookup
    "status": "pending",
    "status_label": "В очереди",
    "complete": false,
    "vehicle": { "vin": "JTDBR32E720123456", "plate": null, "make_model": null },
    "part": "front bumper",
    "scope": { "replacements": false, "analogs": false },
    "clarify_question": null,
    "result": null,
    "duration_sec": null,
    "created_at": "2026-07-10T19:07:02+00:00",
    "updated_at": "2026-07-10T19:07:02+00:00"
  }
}

Keep the returned id — you use it to poll the result.

Get a lookup (poll for the result)

GET/api/v1/lookups/{id}

Poll this endpoint until complete becomes true. Poll gently — once every few seconds is plenty. While the engine works, result stays null.

# 200 OK — finished lookup
{
  "data": {
    "id": 42,
    "status": "done",
    "status_label": "Готово",
    "complete": true,
    "vehicle": { "vin": "JTDBR32E720123456", "plate": null, "make_model": "Toyota Corolla 2018" },
    "part": "front bumper",
    "scope": { "replacements": false, "analogs": false },
    "clarify_question": null,
    "result": {
      "original": [
        { "number": "52119-02988", "name": "Bumper cover, front", "brand": "Toyota", "note": null }
      ],
      "replacements": [],
      "analogs": []
    },
    "duration_sec": 143,
    "created_at": "2026-07-10T19:07:02+00:00",
    "updated_at": "2026-07-10T19:09:25+00:00"
  }
}

Each entry in original / replacements / analogs is { number, name, brand, note }. number is the OEM part number.

Answer a clarification

POST/api/v1/lookups/{id}/clarify

Some parts have variants (side, model year, trim). When the engine can't disambiguate, the lookup finishes with status: "needs_clarify" and a human-readable clarify_question (e.g. “Left or right mirror?”). Post your answer back to the same lookup — it re-enters the queue and continues. No new lookup is created, and you are not charged twice.

FieldTypeDescription
answerstringrequiredYour reply to clarify_question, e.g. left.
# Request
curl -X POST https://vinpartsai.com/api/v1/lookups/42/clarify \
  -H "Authorization: Bearer <your-token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"answer": "left"}'
# 200 OK — lookup re-queued, keep polling GET /lookups/42
{
  "data": {
    "id": 42,
    "status": "pending",
    "complete": false,
    "clarify_question": null,
    "result": null
    /* … */
  }
}
You can only answer a lookup whose status is needs_clarify. Answering any other status returns 404 — so a duplicate reply is safe and simply ignored.

Webhooks (push instead of poll)

Pass a callback_url when you create a lookup and we'll POST the result to it the moment the lookup reaches a final state — no polling needed. Polling still works as a fallback, so a missed webhook is never a lost result.

The POST body mirrors the GET response, wrapped with an event type:

# POST to your callback_url
{
  "event": "lookup.completed",
  "data": { "id": 42, "status": "done", "result": { /* … */ } }
}
eventFires when
lookup.completedResult is ready (status: done).
lookup.needs_clarifyWe need one detail — answer via clarify.
lookup.failedCould not complete the lookup.
lookup.cancelledLookup was stopped.

Verifying the signature

Every webhook carries an HMAC-SHA256 signature of the raw request body in the X-VinPartsAI-Signature header (format sha256=<hex>), keyed with your webhook secret (issued together with your API token). Recompute it and compare — reject the request if it doesn't match.

# PHP
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $webhookSecret);
if (!hash_equals($expected, $_SERVER['HTTP_X_VINPARTSAI_SIGNATURE'])) abort(403);

# Node.js
const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
if (expected !== req.header('X-VinPartsAI-Signature')) return res.sendStatus(403);
Headers: X-VinPartsAI-Event (event type), X-VinPartsAI-Delivery (lookup id), X-VinPartsAI-Signature. Respond 2xx to acknowledge; we retry a couple of times on failure, then rely on your polling.

List your lookups

GET/api/v1/lookups

Paginated, newest first. Optional query param per_page (1–100, default 25).

# 200 OK
{
  "data": [ { "id": 42, "status": "done", /* … */ } ],
  "links": { "next": null, "prev": null },
  "meta": { "current_page": 1, "per_page": 25, "total": 1 }
}

Status values

statuscompleteMeaning
pendingfalseQueued, waiting for the engine.
in_progressfalseEngine is working on it.
donetrueFinished — result is populated.
needs_clarifytrueOne detail needed — see clarify_question.
wrongtrueCould not complete the lookup (e.g. VIN did not resolve).
cancelledtrueStopped.

Treat any complete: true as terminal — stop polling.

Errors

CodeMeaning
401Missing or invalid token.
403Account is pending approval — lookups not enabled yet.
404Lookup not found (or not yours).
422Validation error — see the errors object.
429Rate limit exceeded (120 req/min). Back off and retry.
# 422 Unprocessable Content
{
  "message": "The VIN or plate number field is required.",
  "errors": { "vehicle": [ "The VIN or plate number field is required." ] }
}