Car Image API

고객 사례 · 딜러, 수입 및 VIN 도구

딜러 소프트웨어에서 VIN 또는 재고 시트로 모든 차량의 사진을 만드는 방법

딜러 및 수입 도구가 VIN이나 재고 시트의 한 줄에서 모든 레코드의 스튜디오 이미지까지 도달하는 방법: 무료 VIN 디코딩, resolve, 안정적인 id, 서명된 URL.

문제: 레코드에는 사진보다 몇 달 먼저 VIN이 등록됩니다

CRM, ERP 또는 딜러 관리 시스템(DMS)의 재고 레코드는 VIN, 재고 번호, 가격만으로 시작합니다. 사진은 매장 촬영 담당자가 와야 생기고, 어떤 재고에는 끝내 생기지 않습니다. 운송 중인 차량, 아직 재정비 중인 보상 판매 차량, 모델명이 DMS 이미지 피드의 어떤 항목과도 일치하지 않는 수입차, 아무도 보도용 사진 라이선스를 받지 않은 1990년대 차량이 그렇습니다. 영업 사원의 목록, 매입 담당자의 감정 화면, 고객 견적서 등 해당 레코드를 보여주는 모든 화면에는 사진 대신 회색 자리 표시자가 표시됩니다.

API를 사용하는 딜러 및 수입차 도구는 레코드에 이미 있는 정보로 그 플레이스홀더를 채웁니다. VIN이 있으면 무료 디코딩 한 번으로 이미지를 얻을 수 있습니다. 재고 목록에 한 줄만 있다면 자유 텍스트 resolve로 찾을 수 있습니다. 어느 쪽이든 레코드에는 고정된 차량 id가 저장되고, 모든 화면에서 같은 차량이 같은 방식으로 렌더링됩니다.

딜러 시스템 연동은 이렇게 이루어집니다

  • 입고 시 디코딩. VIN은 레코드가 생성될 때 한 번 디코딩되며, 카탈로그 차량 id가 그 옆에 저장됩니다. 이후 단계에서는 어디서도 제조사나 모델명을 직접 쓰지 않습니다.
  • 목록용 뷰 하나, 상세용 뷰 하나. 재고 목록에는 넓은 영역에 정측면 side를, 상세 페이지와 견적서에는 front-3-4를 사용합니다.
  • 폭넓은 연식. 수입 및 중고 재고는 1990년대까지 거슬러 올라가며, 신차와 똑같이 렌더링됩니다. 카탈로그는 1990년부터 현재 모델 연도까지를 포괄하며, 일본 내수용 차명도 포함됩니다.
  • 앱 안의 서명된 URL. CRM은 브라우저에서 동작하므로 서버가 발급한 키 없는 서명된 URL을 불러옵니다. 한 번 호출에 최대 50개까지 발급되며, 일주일보다 오래 유지되는 레코드는 URL이 갱신됩니다.

1단계: VIN을 무료로 디코딩하기

GET /api/v1/vin/{vin}는 무료이며 API 키만 있으면 됩니다. 디코딩된 연식, 제조사, 모델, 트림, 차체 분류, 엔진 등 NHTSA vPIC 데이터에 있는 모든 속성을 반환하며, 카탈로그에 해당 차량이 있으면 고정 id와 바로 쓸 수 있는 image_path가 담긴 vehicle 객체도 함께 반환합니다. 부분 VIN도 사용할 수 있습니다. 최소 5자 이상 입력하고, 알 수 없는 자리마다 *를 넣으며, 선택적으로 year 힌트를 추가할 수 있습니다.

디코딩
curl -s -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/vin/1HGCM82633A004352"
200 OK (일부 생략)
{
  "data": {
    "vin": "1HGCM82633A004352",
    "valid": true,
    "year": 2003,
    "make": "Honda",
    "model": "Accord",
    "trim": "EX-V6",
    "body_class": "Coupe",
    "vehicle": {
      "id": "veh_3qfyk22gfhsx3",
      "make": "Honda",
      "model": "Accord",
      "year": 2003,
      "image_path": "/api/v1/images/car?vehicle=veh_3qfyk22gfhsx3"
    }
  },
  "request_id": "req_01j9x…"
}

1b단계: 재고 목록의 한 줄만 있을 때

수입 재고는 흔히 스프레드시트로 들어옵니다. 연식, 제조사, 경매장마다 제각각인 표기의 모델명, 등급 또는 트림 코드가 담겨 있습니다. POST /api/v1/images/resolve는 무료이며 결정적으로 동작합니다. 문구에서 연식, 색상, 뷰 관련 단어를 추출하고 남은 내용으로 카탈로그를 검색한 뒤, 판단에 활용할 수 있는 신뢰도와 함께 정확한 파라미터를 반환합니다.

재고 시트의 한 행 조회하기
curl -s -X POST https://carimage.dev/api/v1/images/resolve \
  -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "2019 toyota voxy zs side"}'
200 OK (일부 생략)
{
  "data": {
    "params": {
      "vehicle_id": "veh_9w0nvw7jphp6r",
      "make": "toyota",
      "model": "voxy",
      "year": 2019,
      "view": "side",
      "color": "silver"
    },
    "display": {
      "make_name": "TOYOTA",
      "model_name": "VOXY"
    },
    "confidence": "low",
    "image_path": "/api/v1/images/car?make=toyota&model=voxy&year=2019&view=side&color=silver"
  },
  "request_id": "req_01j9x…"
}

차량은 정확하지만(2019 Toyota Voxy) 신뢰도는 low입니다. "ZS"는 카탈로그에 없는 그레이드여서 이를 우회해 매칭해야 했기 때문입니다. 일괄 가져오기에서는 high를 완료로 처리하고, medium과 low는 candidates 목록과 함께 사람이 검토하도록 하세요. 시트에 연식과 제조사가 별도 열로 이미 있다면 year와 make 필드로 보내고 query에는 모델만 남기세요. 매칭이 훨씬 깔끔해집니다. 차량 관련 단어가 없는 문구는 400이 반환되고, 일치하는 모델이 없는 제조사는 해당 제조사의 연식을 나열한 404가 반환되므로 모델을 물어볼 수 있습니다.

2단계: 레코드에 차량 ID 저장하기

어떤 경로를 거쳤든 vehicle.id(또는 params.vehicle_id)를 재고 레코드에 일반 문자열 열로 저장하십시오. 이 값은 결정적이고 영구적입니다. 같은 차량은 모든 배포와 모든 카탈로그 릴리스 이후에도 같은 ID를 가지므로 외래 키로 사용해도 안전합니다. 이후 모든 이미지 요청은 vehicle=veh_… 형태가 되어 철자가 틀릴 일이 없고, 제조사가 브랜드명을 바꾸거나 데이터 출처가 분류를 변경하더라도 저장된 URL이 깨지지 않습니다.

3단계: 목록용 뷰 하나, 상세 페이지용 뷰 하나

재고 목록은 가로로 넓고 세로로 짧으므로 2:1 상자에 담긴 정측면 뷰가 일반적인 선택입니다. 모든 차량이 같은 방향을 향하고 같은 기준선 위에 놓입니다. trim=1&padding=6은 맞추기 전에 자동차에 맞춰 잘라 내므로 Hilux와 Yaris가 행을 똑같이 채웁니다. fit=contain은 투명 여백과 함께 캔버스를 정확히 640×320으로 유지합니다. 상세 페이지는 1024 크기의 front-3-4를 사용하며, 두 이미지는 캐시를 공유합니다. 도장 색상을 지정하지 않으면 둘 다 실버입니다.

레코드 하나의 목록 이미지
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/images/car?vehicle=veh_3qfyk22gfhsx3&view=side&w=640&h=320&fit=contain&trim=1&padding=6&format=webp" \
  -o stock-4471.webp

4단계: 서명된 URL로 CRM에 표시하기

CRM은 브라우저에서 실행되며, 브라우저에는 절대 API 키를 두어서는 안 됩니다. 화면에 재고 한 페이지가 표시될 때 서버가 POST /api/v1/image-urls로 레코드마다 서명된 URL을 하나씩 발급하고(호출당 최대 50개, 각 URL의 이미지는 해당 월의 차량 이미지로 집계), 페이지는 일반 이미지처럼 이를 불러옵니다. 몇 달간 재고로 남는 레코드에는 두 가지 설정이 중요합니다.

  • ttl_seconds: 604800와 renew: true: URL은 7일 TTL이 지난 후에도 계속 작동하며, 갱신 시 해당 월에 이미지가 한 번 집계되고, 그 달에 이미 집계된 경우에는 추가로 집계되지 않습니다. 아무도 차량을 보지 않은 달에는 집계되지 않습니다.
  • 동기화 페이지마다 Idempotency-Key를 사용합니다. 재시도된 요청은 첫 응답을 그대로 다시 반환하며, 어떤 것도 두 번 발급하지 않습니다.

대시보드에서 발급 키를 CRM의 오리진으로 제한하면 다른 사이트로 유출된 URL은 403으로 응답합니다. 키를 폐기하면 해당 키로 발급한 모든 URL이 무효화됩니다. 플랜의 이번 달 차량 이미지를 모두 사용하면 갱신 요청은 402로 응답하며, 허용량이 초기화되거나 플랜을 업그레이드하면 같은 URL이 다시 작동합니다. 전체 수명 주기는 서명된 URL 문서에서 확인하세요.

TypeScript SDK로 구현하는 입고 처리와 재고 목록
import { CarImageClient } from "@meterapp/car-image-sdk";

const client = new CarImageClient({ apiKey: process.env.CAR_IMAGE_API_KEY });

// On intake: VIN in, vehicle id on the record. Free.
const { data: decoded } = await client.decodeVin(stock.vin);
if (decoded.vehicle) {
  stock.vehicleId = decoded.vehicle.id; // veh_…, permanent; the record never spells a car again
  stock.trim = decoded.trim;            // shown next to the image, never claimed by it
}

// When the CRM draws the stock list: one signed URL per record, up to fifty per call.
// The browser loads them; the key never leaves the server.
const { data: urls } = await client.createImageUrls(
  page.map((row) => ({
    vehicle: row.vehicleId, view: "side",
    width: 640, height: 320, fit: "contain", trim: true, padding: 6, format: "auto",
  })),
  { ttlSeconds: 604_800, renew: true, renewDays: 365, idempotencyKey: `stock-list:${syncRunId}:${pageIndex}` }
);

CLI, SDK 및 MCP

npx @meterapp/car-image
# decode for free; the output names the vehicle id and a ready-to-run image command
npx @meterapp/car-image vin 1HGCM82633A004352
npx @meterapp/car-image vin 1HGCM82633A004352 --json | jq -r .data.vehicle.id

# a partial VIN from a faded door sticker: at least five characters, * for each unknown position
npx @meterapp/car-image vin "1HGCM826*3A" --year 2003

# the list image for one record
npx @meterapp/car-image get --vehicle veh_3qfyk22gfhsx3 --view side --width 640 --height 320 --fit contain --trim --padding 6 --format webp --out stock-4471.webp
  • SDK: 위와 같이 client.decodeVin(vin, { year? }), client.resolve(query), client.getImage({ vehicle, … }), client.createImageUrls(…)를 사용합니다.
  • MCP: decode_vin, resolve_vehicle, get_car_image, create_car_image_urls가 핵심 도구 세트에 포함되어 있어, 재고 시스템을 컨텍스트로 가진 어시스턴트가 레코드의 VIN으로 "재고 4471 사진" 요청에 답할 수 있습니다. 설정 방법은 MCP 페이지에, 디코더의 필드와 오류는 VIN 문서에 있습니다.

자주 묻는 질문

모든 재고 레코드에 사진을 넣는 데 드는 비용은 얼마인가요?
VIN 디코딩은 무료입니다. 각 차량의 도색과 뷰 조합 하나가 차량 이미지 하나이며, 표시 횟수나 크기와 관계없이 한 달에 한 번 집계됩니다. If-None-Match로 다시 확인하는 것은 무료입니다. 서명된 URL은 발급될 때 이미지가 집계되며, renew: true를 사용하면 이후 URL이 열리는 달마다 한 번씩 더 집계됩니다. 재고가 수천 대인 딜러 그룹은 한 달에 수천 개의 차량 이미지를 표시하므로, 전시 중인 재고에 맞춰 요금제를 선택하세요.
VIN은 디코딩되는데 vehicle이 null입니다. 어떻게 해야 하나요?
디코더는 제조사, 모델, 연식을 파악했지만 카탈로그에 해당 조합의 항목이 없는 경우입니다. 트레일러, 일부 상용 섀시, 아직 소스에 등록되지 않은 차명 등이 이에 해당합니다. 디코딩된 필드를 표시하고, 철자가 다를 수 있으니 GET /api/v1/vehicles?q=로 카탈로그를 검색한 뒤, 요청 게시판에서 해당 차량을 요청하세요. POST /api/v1/requests는 중복 요청을 만들지 않고 이미 열려 있는 요청에 추천을 추가합니다.
VIN 디코딩은 수입차와 미국에서 판매된 적 없는 차량도 지원하나요?
디코더는 NHTSA의 vPIC 데이터를 사용하므로 NHTSA에 등록하는 제조사의 VIN은 완전히 디코딩됩니다. 미국 인증을 받은 적이 없는 차량은 모델 없이 제조사와 연식만 디코딩되는 경우가 많고, 아예 디코딩되지 않는 경우도 있습니다. 이런 재고는 대신 등록 서류를 기준으로 조회하십시오. 카탈로그 자체는 1990년 이후 전 세계 차량을 포함하며, Toyota Voxy 같은 일본 내수용 모델도 들어 있습니다.
렌더에 레코드의 트림이 반영되나요?
아니요. 카탈로그는 제조사, 모델, 연식을 기준으로 하므로 EX-V6 쿠페와 LX 세단은 동일한 2003 Accord로 렌더링됩니다. 디코딩 결과의 trim과 body_class는 이미지 옆에 텍스트로 표시하고, 렌더링을 해당 실제 차량의 사진처럼 설명하지 마십시오.
딜러 웹사이트에서도 CRM과 동일한 URL을 사용할 수 있나요?
사이트가 허용 출처 목록에 포함된 키로 발급하면, 다른 사이트에서 브라우저가 해당 URL을 불러올 때 403 origin_not_allowed로 응답합니다. 이메일 클라이언트처럼 Referer 없이 불러오는 경우는 통과합니다. CRM과 웹사이트는 하나의 키와 URL 세트를 공유할 수도 있고, 각각 별도의 키를 사용해 하나만 따로 폐기할 수도 있습니다.