Car Image API

กรณีการใช้งาน · การเช่ารถ

ภาพรถยนต์สำหรับหน้าแสดงรถเช่า

แสดงรถเช่าทุกคลาสในรูปแบบเดียวกัน: รุ่นตัวแทนหนึ่งรุ่นต่อคลาส การจัดเฟรมที่เหมือนกันด้วย trim, padding และ fit สีที่มีชื่อเรียกหนึ่งสี และ URL ที่คุณแคชได้

คู่มือการสร้างที่เขียนจากพฤติกรรมของ API ตามเอกสาร โดยไม่ได้ระบุชื่อลูกค้ารายใด เผยแพร่เมื่อ

ปัญหา: การเช่ารถคือการเช่าตามประเภทรถ ไม่ใช่รถคันใดคันหนึ่ง

ลูกค้าเช่ารถจะจองตามประเภทรถ เช่น รถคอมแพกต์ SUV ขนาดกลาง หรือรถมินิแวน แล้วขับออกไปด้วยรถคันใดก็ได้ในประเภทนั้นที่มีอยู่ในลานจอด หน้าการจองยังคงต้องมีภาพสำหรับทุกประเภท และธรรมเนียมคือใช้รุ่นตัวอย่างหนึ่งรุ่นพร้อมคำว่า "หรือเทียบเท่า"

รูปภาพเหล่านั้นมักมาจากที่ใดก็ตามที่หาได้: ภาพประชาสัมพันธ์จากที่หนึ่ง ภาพจากดีลเลอร์จากอีกที่หนึ่ง มุมกล้องต่างกัน ฉากหลังต่างกัน และรถที่กินพื้นที่การ์ดไม่เท่ากัน กริดที่ประกอบขึ้นแบบนั้นจะดูปะติดปะต่อ สิ่งที่หน้าแสดงรถในฟลีตต้องการคือสิ่งตรงกันข้าม: ทุกคลาสถูกนำเสนอในแบบเดียวกัน เพื่อให้สิ่งเดียวที่เปลี่ยนไปในแต่ละการ์ดคือตัวรถ

การผสานรวมมีลักษณะอย่างไร

  • รุ่นตัวแทนหนึ่งรุ่นต่อกลุ่มรถ เก็บไว้เป็น vehicle id ในแค็ตตาล็อก โดยใส่ "หรือเทียบเท่า" ไว้ในป้ายกำกับ และไม่ใส่ไว้ในภาพเด็ดขาด
  • รูปแบบคำขอเดียวสำหรับทุกการ์ด: ใช้มุมมอง กรอบ การปรับพอดี การครอป ระยะขอบ สี ฉากหลัง และรูปแบบไฟล์เดียวกัน
  • ไม่เกินสองภาพต่อประเภทรถ: การ์ดในกริดและภาพหลักบนหน้าประเภทรถ
  • แคชไว้ เพราะกริดเหมือนกันสำหรับผู้เข้าชมทุกคน: ไฟล์บน CDN ของคุณ หรือ signed URL ที่สร้างครั้งเดียวแล้วนำกลับมาใช้ซ้ำ

ขั้นตอนที่ 1: เลือกรุ่นตัวแทนของแต่ละคลาสด้วย id

เลือกรุ่นที่ใช้เป็นตัวแทนในการให้เช่าของรถแต่ละกลุ่ม แล้วค้นหารุ่นปีปัจจุบันของรุ่นนั้น GET /api/v1/vehicles?make=&model= ใช้งานได้ฟรีและเป็นสาธารณะ โดยจะแสดงทุกปีของรุ่นนั้น พร้อม vehicle id ที่คงที่ซึ่งคุณจะจัดเก็บไว้ ส่วน POST /api/v1/images/resolve ทำงานเดียวกันจากวลี เช่น "2026 toyota rav4"

id ของรถหนึ่งรุ่นปี
# one model's years, each with its vehicle id (free, public)
curl -s "https://carimage.dev/api/v1/vehicles?make=toyota&model=rav4" | jq -c '.data.vehicles[] | select(.year == 2026)'
# {"year":2026,"id":"veh_e5w75c93f6cvk"}
fleet.ts
// One representative model per class: its current model year, by catalog id.
// "or similar" lives in the label; the picture never promises the car on the lot.
export const CLASS_IMAGES = {
  compact:        { vehicle: "veh_fy7r2yvwt4p1v", label: "Toyota Corolla or similar" },    // 2026 Toyota Corolla
  "midsize-suv":  { vehicle: "veh_e5w75c93f6cvk", label: "Toyota RAV4 or similar" },       // 2026 Toyota RAV4
  "fullsize-suv": { vehicle: "veh_9mzh9ejqjye43", label: "Chevrolet Tahoe or similar" },   // 2026 Chevrolet Tahoe
  minivan:        { vehicle: "veh_9jck7rrybf35p", label: "Chrysler Pacifica or similar" }, // 2026 Chrysler Pacifica
};

id ไม่มีวันเปลี่ยน การแมปนี้จึงเป็นจุดเดียวที่ได้รับผลกระทบเมื่อเปลี่ยนรถในฟลีต: เมื่อคลาสขนาดกลางเปลี่ยนไปใช้รุ่นอื่นหรือปีรุ่นถัดไป ก็แค่แทนที่ id เดียว แล้วการ์ด ภาพหลัก และอีเมลทุกรายการที่อ่านจากการแมปนี้จะเปลี่ยนตาม

ขั้นตอนที่ 2: รูปแบบคำขอเดียวสำหรับทุกคลาส

มุมกล้องทั้ง 9 มุมจัดเฟรมเหมือนกันสำหรับรถทุกคันในแคตตาล็อก ซึ่งทำให้กริดเรียงได้ตรงกัน ส่วนที่เหลือของการ์ดคือพารามิเตอร์เพียงไม่กี่ตัวที่กำหนดเพียงครั้งเดียว:

  • view=side สำหรับการ์ด: รถทุกคันหันไปทางเดียวกันบนเส้นฐานเดียวกัน มุมด้านซ้ายแต่ละมุมมีมุมคู่แบบ -right (side-right) เพื่อให้การ์ดหันเข้าหาเลย์เอาต์ได้ ส่วนหน้าของแต่ละประเภทรถใช้ front-3-4 ซึ่งเป็นมุมภาพหลัก
  • w=640&h=320&fit=contain: ขนาด 640×320 พอดีสำหรับทุกประเภทรถ และมองเห็นรถได้ทั้งคัน ส่วนหัว X-Image-Width และ X-Image-Height ในการตอบกลับจะยืนยันขนาดแคนวาส
  • trim=1&padding=6: ครอปให้เหลือเฉพาะตัวรถก่อนปรับขนาด โดยเว้นระยะขอบ 6% ของด้านที่ยาวกว่า เพื่อให้ Corolla และ Tahoe เต็มการ์ดในลักษณะเดียวกัน แทนที่จะลอยอยู่ในเฟรมต้นฉบับทรงสี่เหลี่ยมจัตุรัส
  • background=f4f4f4 จะทำให้การ์ดกลืนไปกับสีเทาของกริด หากไม่ใส่ไว้ พื้นหลังจะยังคงโปร่งใสและสีของการ์ดจะแสดงผ่านออกมา
  • format=webp สำหรับไฟล์ที่จัดเก็บไว้ และ format=auto บน signed URL ซึ่งจะส่ง WebP หรือ PNG ให้แต่ละเบราว์เซอร์ตามที่รองรับ
การ์ดและภาพแบนเนอร์หลักสำหรับรถหนึ่งกลุ่ม
# the class card: the same box, view, crop, paint and backdrop for every class
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/images/car?vehicle=veh_e5w75c93f6cvk&view=side&color=white&w=640&h=320&fit=contain&trim=1&padding=6&background=f4f4f4&format=webp" \
  -o midsize-suv-card.webp

# the class page's hero: the three-quarter view, the full width the API delivers
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
  "https://carimage.dev/api/v1/images/car?vehicle=veh_e5w75c93f6cvk&view=front-3-4&color=white&w=1024&format=webp" \
  -o midsize-suv-hero.webp

ขั้นตอนที่ 3: สีที่มีชื่อเรียกเพียงสีเดียวสำหรับทั้งกริด

ค่าสีที่กำหนดไว้ล่วงหน้า 15 ค่าเป็นชื่อสีรถ: white, black, gray, silver, blue, red, green, brown, beige, tan, orange, yellow, gold, burgundy, purple ค่าเริ่มต้นคือ silver และ grey จะถูกอ่านเป็น gray เลือกสีเดียวสำหรับรถทุกประเภทเพื่อให้การ์ดดูเป็นชุดเดียวกัน ตัวอย่างด้านบนใช้ white ค่าสีที่กำหนดไว้ล่วงหน้าแต่ละค่าใช้แถบสีรถของตนเอง ไม่ใช่สี CSS ที่มีชื่อเดียวกัน (red คือ #b3151b) และทุกสี รวมถึงค่า hex คือการเปลี่ยนสีจากมุมมองที่เตรียมไว้หนึ่งมุมมอง ดังนั้นแต่ละสีจึงเท่ากับภาพรถหนึ่งภาพ

ขั้นตอนที่ 4: แคชไฟล์หรือ URL

ตารางประเภทรถเหมือนกันสำหรับผู้เข้าชมทุกคน จึงควรมีค่าใช้จ่ายต่อการเข้าชมแทบเป็นศูนย์ ซึ่งทำได้สองวิธี:

  • จัดเก็บไฟล์ไว้ ดึงภาพการ์ดและภาพหลัก (hero) แต่ละภาพเพียงครั้งเดียวด้วย GET /api/v1/images/car แล้วให้บริการจาก CDN ของคุณเหมือนไฟล์อื่น ๆ แพ็กเกจแบบชำระเงินอนุญาตให้เก็บสำเนาบนเซิร์ฟเวอร์ของคุณเองเพื่อใช้ในผลิตภัณฑ์ตลอดที่แพ็กเกจยังใช้งานอยู่ (ลบภายใน 30 วันหลังแพ็กเกจสิ้นสุด หรือซื้อ Image Pack เพื่อเก็บชุดภาพไว้อย่างถาวร) เก็บ ETag ของแต่ละไฟล์ไว้ด้วย การตรวจสอบซ้ำทุกคืนด้วย If-None-Match จะได้รับ 304 และไม่มีค่าใช้จ่าย สำหรับกลุ่มรถข้างต้น จะใช้ภาพรถยนต์ 8 ภาพของเดือน ซึ่งนำกลับมาใช้ซ้ำได้บ่อยเท่าที่ต้องการ
  • หรือสร้าง signed URL เพียงครั้งเดียวแล้วนำกลับมาใช้ซ้ำ POST /api/v1/image-urls รับได้สูงสุด 50 ภาพต่อการเรียกหนึ่งครั้ง และส่งคืน URL ที่ไม่มีคีย์ซึ่งมีอายุได้นานสูงสุด 7 วัน URL ที่ใช้ได้ไม่จำกัดครั้งสามารถแคชแบบสาธารณะได้ตลอดอายุของมัน ดังนั้นเมื่อผู้เข้าชมทุกคนโหลด URL เดียวกัน การโหลดซ้ำจะมาจาก CDN โดยไม่มีค่าใช้จ่าย เพิ่ม renew: true แล้ว URL ที่จัดเก็บไว้จะยังใช้งานได้ต่อหลังหมดอายุ การต่ออายุจะนับภาพหนึ่งครั้งในเดือนที่เกิดการต่ออายุ และไม่นับเพิ่มหากภาพนั้นถูกนับไปแล้วในเดือนนั้น ดังนั้นฟลีตรถข้างต้นจะอยู่ที่ไม่เกิน ภาพรถ 8 ภาพ ต่อเดือน

จำกัดคีย์ที่ใช้สร้าง URL ให้ใช้ได้เฉพาะโดเมนการจองของคุณในแดชบอร์ด แล้ว URL ที่ถูกคัดลอกไปไว้บนเว็บไซต์อื่นจะตอบกลับด้วย 403

URL ของกริดที่สร้างเพียงครั้งเดียวด้วย TypeScript SDK
import { CarImageClient, type ImageParams } from "@meterapp/car-image-sdk";
import { CLASS_IMAGES } from "./fleet";

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

// Framed once, for every class.
const card = (vehicle: string): ImageParams => ({
  vehicle, view: "side", color: "white", width: 640, height: 320,
  fit: "contain", trim: true, padding: 6, background: "#f4f4f4", format: "auto",
});
const hero = (vehicle: string): ImageParams => ({ vehicle, view: "front-3-4", color: "white", width: 1024, format: "auto" });

// Every visitor sees the same grid: mint the URLs once and serve the stored ones.
// renew keeps each URL working past its 7-day lifetime, one credit per image
// for each further week in which it is loaded.
const classes = Object.entries(CLASS_IMAGES);
const { data } = await client.createImageUrls(
  classes.flatMap(([, entry]) => [card(entry.vehicle), hero(entry.vehicle)]),
  { ttlSeconds: 604800, renew: true, idempotencyKey: "class-grid-2026-10" }
);
await saveClassImages(
  classes.map(([key], index) => ({ key, card: data[2 * index].url, hero: data[2 * index + 1].url, renewsUntil: data[2 * index].renews_until }))
);

ขั้นตอนที่ 5: การยืนยันการจอง

อีเมลยืนยันจะแสดงคลาสที่จองไว้ และผู้เช่าจะเปิดอีเมลเมื่อใดก็ได้ที่สะดวก ให้สร้างภาพนั้นเป็น URL แบบต่ออายุได้: ttl_seconds ที่ค่าสูงสุด 7 วัน, renew: true และ renew_days ที่ครอบคลุมเลยวันคืนรถ สัปดาห์ที่ไม่มีใครเปิดอีเมลจะไม่มีค่าใช้จ่าย และหลังสิ้นสุดการเช่า URL จะหยุดต่ออายุ การใช้ Idempotency-Key หนึ่งค่าต่อการจองจะทำให้การส่งซ้ำได้ URL เดิมกลับมาแทนการสร้าง URL ใหม่ และการเก็บ URL ไว้กับข้อมูลการจองช่วยให้การส่งซ้ำทุกครั้งในภายหลังใช้ URL เดิมได้ อีเมลตามวงจรชีวิตลูกค้าของเราเองก็ทำงานแบบนี้ เรื่องราวเกี่ยวกับอีเมลมีรายละเอียดของกลไกและ HTML

URL ที่ต่ออายุได้สำหรับการยืนยันการจองหนึ่งรายการ
// The booking confirmation: opened today, again at the counter, maybe never.
const { data: [classImage] } = await client.createImageUrls(card(booking.classVehicle), {
  ttlSeconds: 604800,
  renew: true,
  // A week past the return date, and no renewals after it (365 days at most).
  renewDays: Math.min(daysUntil(booking.returnDate) + 7, 365),
  // A retried send replays the first URL instead of minting a second one.
  idempotencyKey: `booking:${booking.id}:class`,
});
booking.classImageUrl = classImage.url; // later resends reuse it

CLI, SDK และ MCP

  • CLI: npx @meterapp/car-image get --vehicle veh_… --view side --width 640 --height 320 --fit contain --trim --padding 6 --out card.webp เขียนการ์ดลงไฟล์ ส่วน car-image url --batch classes.json --renew สร้าง URL เป็นชุด
  • SDK: client.vehicles({ make, model }) สำหรับ id, client.getImage(…) สำหรับไฟล์ และ client.createImageUrls(…) สำหรับ URL
  • MCP: resolve_vehicle และ create_car_image_urls ช่วยให้ผู้ช่วย AI ร่างการ์ดประเภทรถจากคำว่า "SUV ขนาดกลาง, RAV4 หรือรุ่นใกล้เคียง" ได้ ดูวิธีตั้งค่าได้ที่หน้า MCP

คำถามที่พบบ่อย

การ์ดสามารถแสดงรถคันที่ผู้เช่าจะได้รับจริงได้หรือไม่
ไม่ได้ และไม่ควรพยายามด้วย รถแต่ละคลาสให้เช่าแบบ “หรือรุ่นใกล้เคียง” และภาพเรนเดอร์แสดงยี่ห้อ รุ่น และปีจากแค็ตตาล็อก ไม่ใช่รถที่อยู่ในลานจอดของคุณ ให้แสดงรุ่นตัวแทนหนึ่งรุ่นพร้อมคำว่า “หรือรุ่นใกล้เคียง” ในป้ายกำกับ เมื่อมีการกำหนดรถคันที่แน่นอนที่เคาน์เตอร์แล้ว คุณสามารถถอดรหัส VIN ของรถคันนั้นได้ฟรีเพื่อหารถที่ตรงกันในแค็ตตาล็อก หากต้องการแสดงรุ่นนั้นแทน
ทำไมรถในกริดจึงไม่ได้แสดงตามสัดส่วนขนาดจริง
เพราะ trim จะครอปภาพเรนเดอร์แต่ละภาพให้พอดีกับตัวรถ และ fit=contain จะขยายรถให้เต็มการ์ด ดังนั้นรถคอมแพกต์และ SUV ขนาดใหญ่จึงเต็มการ์ดในลักษณะเดียวกัน นี่คือสิ่งที่ทำให้กริดดูเป็นระเบียบ ให้บอกขนาดเป็นข้อความแทน: ชื่อคลาส จำนวนที่นั่ง และจำนวนกระเป๋า
กริดแสดงคลาสรถมีค่าใช้จ่ายเท่าใด
ฟลีตรถ 4 ประเภท ที่แต่ละประเภทมีภาพการ์ดและภาพฮีโร่ จะใช้ ภาพรถ 8 ภาพ ต่อเดือน โดยแต่ละภาพส่งมอบได้บ่อยเท่าที่ต้องการในทุกขนาด หากจัดเก็บไฟล์ไว้บน CDN ของคุณขณะที่แพ็กเกจแบบชำระเงินยังใช้งานอยู่ ภาพจะถูกนับเฉพาะในเดือนที่คุณดึงภาพ พร้อมการตรวจสอบซ้ำด้วย ETag ฟรี หากใช้ Signed URL แบบต่ออายุแทน ภาพเดียวกันจะถูกนับหนึ่งครั้งในแต่ละเดือนที่มีการเปิดหน้าเว็บ
ตารางรถควรใช้สีใด
ใช้สีพรีเซ็ตเดียวกันสำหรับทุกคลาส เพื่อให้การ์ดดูเป็นชุดเดียวกัน สีพรีเซ็ตทั้ง 15 สีได้แก่ white, black, gray, silver, blue, red, green, brown, beige, tan, orange, yellow, gold, burgundy, purple และค่าเริ่มต้นคือ silver คุณใช้รหัส hex ใดก็ได้เช่นกันในราคาเดียวกัน ผู้เช่าจะได้รถสีที่มีอยู่ในลานจอด ซึ่งการ์ดไม่เคยรับประกันไว้
หากรุ่นรถของประเภทรถเช่าไม่มีในแค็ตตาล็อกจะทำอย่างไร
หากไม่มีรถคันนั้นในแคตตาล็อก ระบบจะตอบกลับเป็น 404 พร้อมคำแนะนำ ได้แก่ รถในแคตตาล็อกที่ใกล้เคียงที่สุดพร้อม id ของแต่ละคัน ให้ใช้หนึ่งในนั้น หรือขอเพิ่มรถบนบอร์ดคำขอสาธารณะ แคตตาล็อกนี้เป็นโอเพนซอร์ส

อ่านต่อ

เรื่องราวลูกค้า · เครื่องมือสำหรับนักพัฒนาวิธีฝังภาพรถยนต์ในอีเมลด้วย signed URL ที่ไม่มีวันเสียอีเมลตามวงจรชีวิตลูกค้าของเราเองฝังภาพเรนเดอร์แรกของลูกค้าแต่ละรายเป็น signed URL ที่ใช้งานได้จริงและต่ออายุตัวเองได้นานหนึ่งปี นี่คือวิธีการทำงานและวิธีนำไปใช้เอง