Le problème : une annonce a besoin d’une image avant que le vendeur n’en publie une
Un site de petites annonces ou un catalogue d’achat automobile vit ou meurt par sa grille. Chaque carte a besoin d’une image dès que l’annonce existe, et la photo du vendeur arrive plus tard, voire jamais, sous un angle quelconque et dans une allée quelconque. La photo d’illustration a le problème inverse : une photo de presse sous licence existe pour les voitures populaires des années populaires, sous l’angle et sur le fond choisis par le constructeur, et pour rien d’autre. Un catalogue qui couvre de 1990 au millésime actuel présente des dizaines de milliers de manques.
Ce que veulent les équipes qui créent ces sites est ennuyeux, au meilleur sens du terme : une image studio par millésime, le même angle et la même teinte pour toutes, transparente pour s'intégrer à n'importe quel fond de carte, et assez légère pour être servie dix mille fois par jour.
À quoi ressemble une intégration de catalogue
Les catalogues construits aujourd'hui sur l'API ont tous la même structure, qu'il s'agisse d'un site de petites annonces qui alimente ses annonces, d'une application d'achat automobile qui affiche ses fiches comparatives ou d'une application mobile qui montre une image principale par véhicule :
- Un remplissage initial, puis un flux régulier. Des milliers de rendus en quelques heures pour couvrir le catalogue, puis une poignée par jour au fil des nouveaux millésimes et des demandes.
- Un angle, une peinture, ou une courte palette. La plupart choisissent
front-3-4ousideen argent ou en blanc. Un catalogue d’achat qui laisse l’utilisateur choisir une couleur génère quelques teintes prédéfinies par véhicule ; chacune représente une image de véhicule de plus, pas un nouveau pipeline. - Du WebP dans un cadre fixe. De 512 à 1024 pixels de large, livré exactement dans le cadre qu’utilise la carte : le front-end n’a jamais à recadrer l’image ni à ajouter de bandes.
- Stockés par identifiant de véhicule. Les rendus sont déposés sur le stockage objet ou le CDN de l'équipe, ou les pages chargent des URL signées ; dans les deux cas, la clé est l'identifiant
veh_…stable du catalogue, et non une chaîne marque-modèle-année.
La suite de cette page présente ce pipeline, étape par étape, avec le code. Elle suppose une clé API dotée du scope images:read et un forfait dimensionné pour le catalogue (voir la FAQ).
Étape 1 : parcourir le catalogue par id, jamais par libellé
L'échec le plus courant dans la construction d'un catalogue ne vient pas du rendu, mais de l'orthographe. Un build qui génère les chaînes de marque et de modèle à partir d'une autre base de données peut consacrer la plupart de ses requêtes à des 404 vehicle_not_found : gratuites, mais chacune représente un aller-retour inutile, et la grille se retrouve trouée. La solution : laisser le catalogue vous dire ce qui existe. GET /api/v1/vehicles est gratuit, ne nécessite aucune clé, et chaque modèle renvoyé porte un identifiant de véhicule stable (veh_ suivi de 13 caractères) qui ne change jamais d'un déploiement ou d'une version du catalogue à l'autre. Stockez cet identifiant sur l'annonce et utilisez-le pour le rendu.
# every make with a 2024 model year (free, no key)
curl -s "https://carimage.dev/api/v1/vehicles?year=2024"
# every 2024 model of one make, each with the id you will store
curl -s "https://carimage.dev/api/v1/vehicles?year=2024&makeId=584"{
"data": {
"year": 2024,
"make_id": 584,
"models": [
{
"name": "718",
"slug": "718",
"vehicle_type": "Passenger Car",
"id": "veh_b4bf090fyb53k"
},
{
"name": "911",
"slug": "911",
"vehicle_type": "Passenger Car",
"id": "veh_78qtwrgh37bkr"
}
]
}
}?make=porsche&model=911 liste toutes les années d'un modèle avec un id par année, et ?q= effectue une recherche en texte libre lorsqu'un vendeur saisit un nom. Le même catalogue est publié sous forme de package ouvert @meterapp/vehicle-db, ce qui permet aux listes déroulantes et à la validation de fonctionner hors ligne sur une liste identique. Dans les deux cas, à la fin de cette étape, vous disposez d'une liste d'id, et plus rien en aval n'a jamais à épeler le nom d'une voiture.
Les annonces qui nomment déjà leurs voitures selon l'orthographe d'une autre base de données sont mises en correspondance en un seul appel : POST /api/v1/vehicles/check en lit jusqu'à 2 000 comme le ferait un rendu et répond pour chacune avec son identifiant de véhicule, les véhicules réels les plus proches lorsque le catalogue ne contient pas cette voiture, ou un échec à ignorer dans toutes les vues et couleurs. car-image check listings.csv --out checked.csv réécrit les identifiants dans le fichier. C'est gratuit, et cela vous évite de découvrir les lacunes une 404 après l'autre.
Étape 2 : une seule forme de requête pour toutes les annonces
Définissez la vignette une fois pour toutes et encodez-la dans la chaîne de requête. Tout ce qui suit vehicle= ci-dessous est un choix de cadrage que vous faites une seule fois pour tout le catalogue :
curl --fail-with-body -H "Authorization: Bearer $CAR_IMAGE_API_KEY" \
"https://carimage.dev/api/v1/images/car?vehicle=veh_78qtwrgh37bkr&view=front-3-4&color=silver&w=800&h=600&fit=contain&trim=1&padding=8&format=webp" \
-o veh_78qtwrgh37bkr.webpw=800&h=600&fit=containproduit exactement du 800×600 avec la voiture entièrement visible et une marge transparente autour, pour que chaque carte ait le même canevas.fit=coverremplit plutôt le cadre et recadre au centre.trim=1&padding=8recadre sur le contour de la voiture avant l'ajustement, avec une marge de 8 % de son plus grand côté, si bien qu'un Suburban et une Miata remplissent le cadre de la même façon au lieu de flotter dans l'image source.color=silverest la valeur par défaut et la teinte neutre la plus sûre ; chacun des 15 préréglages ou n'importe quel code hexadécimal compte de la même façon, pour une image de véhicule.format=webpest l'encodage avec canal alpha sans perte le plus compact. Lorsqu'un navigateur charge directement l'URL (URL signées, étape 4),format=autonégocie WebP ou PNG selon le client.
La réponse contient ETag, X-Image-Source (cache ou generated) et X-Vehicle-Id, le véhicule du catalogue qui a été servi. Conservez l’ETag avec le fichier : une requête ultérieure avec If-None-Match renvoie 304 et ne coûte rien, ce qui permet à une resynchronisation nocturne de rester gratuite.
Étape 3 : étaler le rattrapage dans le respect des limites
Trois limites encadrent un remplissage initial, et chacune se signale dans les en-têtes plutôt que d’échouer en silence :
- Requêtes par minute. 120 par clé avec Free et Pro, 600 avec Business, 1 200 avec Enterprise, plus une limite par compte couvrant toutes les clés. Chaque réponse authentifiée comporte
RateLimit-Remaining; une réponse429comporteRetry-After. Quatre à huit requêtes simultanées suffisent largement. - Nouvelles vues de véhicule. La première requête pour une vue de véhicule que nous n’avons pas encore préparée est un peu plus longue, le temps que nous la préparions pour qu’elle puisse être affichée dans n’importe quelle couleur ; toutes les requêtes suivantes, dans n’importe quelle couleur, sont servies depuis le cache. La préparation de nouvelles vues puise dans la part du budget de rendu quotidien attribuée au compte (2 % en Free, 10 % en Pro, 25 % en Business, 50 % en Enterprise). Au-delà, un nouveau rendu répond
429aveccode: account_generation_capetreset_at(minuit UTC), rien n’est facturé et les images en cache continuent d’être servies. Un import massif de rattrapage se fait en quelques soirées, pas en une seule. - Images de véhicule par mois. Free en inclut 100, Pro 2 500, Business 10 000 ; Enterprise se négocie. Au-delà de ce quota, une requête pour une image pas encore comptée ce mois-ci renvoie
402(plan_image_limit) et n’est pas décomptée ; une image déjà comptée ce mois-ci est toujours servie, si bien qu’une grille en production ne casse jamais.
Les échecs de rendu (502) restituent automatiquement leur image de véhicule, les requêtes simultanées pour une même nouvelle variante partagent un seul rendu, et un simple GET peut être relancé sans risque. Le SDK relance automatiquement les erreurs 429 et 503 en respectant Retry-After, et ne relance jamais une erreur 402.
import { CarImageClient, CarImageError } from "@meterapp/car-image-sdk";
const client = new CarImageClient({ apiKey: process.env.CAR_IMAGE_API_KEY });
// One make, one model year: the ids are what you store and what you render by.
const { data } = await client.vehicles({ year: 2024, makeId: 584 });
const queue = data.models.flatMap((model) => (model.id ? [model.id] : []));
// Four in flight stays well inside 120 requests a minute, and a cold render
// that takes a few seconds never holds up the rest of the batch.
await Promise.all(
Array.from({ length: 4 }, async () => {
for (let id = queue.shift(); id; id = queue.shift()) {
try {
const image = await client.getImage({
vehicle: id, view: "front-3-4", color: "silver",
width: 800, height: 600, fit: "contain", trim: true, padding: 8, format: "webp",
});
await store(id, image.bytes, image.etag); // your object store or CDN, keyed by vehicle id
} catch (error) {
// 429 and 503 were already retried with Retry-After; a 404 is a vehicle to request, not a failure.
if (error instanceof CarImageError && error.status === 404) continue;
throw error;
}
}
})
);Étape 4 : servez-la depuis votre CDN ou via des URL signées
Il existe deux bonnes façons de présenter l'image à un acheteur, et les intégrations de catalogue utilisent les deux :
- Stockez les fichiers. Enregistrez chaque rendu dans votre propre stockage d’objets ou CDN sous son identifiant de véhicule et servez-le comme n’importe quelle autre ressource. C’est la bonne solution pour une grille affichée des millions de fois : aucun appel vers nos serveurs à chaque chargement, et la licence d’un forfait payant couvre les copies que vous conservez pour servir votre produit.
- Générez des URL signées.
POST /api/v1/image-urlsaccepte jusqu’à cinquante images par appel, compte l’image de chaque URL comme une image de véhicule du mois au moment de sa génération, et renvoie des URL sans clé que le navigateur charge directement. Définissezttl_secondsjusqu’à sept jours ; ajoutezrenew: truepour les pages d’annonces qui restent en ligne plus longtemps (un renouvellement compte l’image une fois dans le mois où il a lieu, et rien de plus si elle a déjà été comptée ce mois-là). Limitez la clé aux origines de votre site dans le tableau de bord, et une URL copiée ailleurs répondra403.
Envoyez un Idempotency-Key par page d’annonces lorsque vous générez des URL par lots : un appel relancé avec la même clé et le même corps rejoue la première réponse et ne décompte rien de plus.
{
"images": [
{
"vehicle": "veh_78qtwrgh37bkr",
"view": "front-3-4",
"color": "silver",
"width": 800,
"height": 600,
"fit": "contain",
"trim": true,
"padding": 8,
"format": "auto"
},
{
"vehicle": "veh_b4bf090fyb53k",
"view": "front-3-4",
"color": "silver",
"width": 800,
"height": 600,
"fit": "contain",
"trim": true,
"padding": 8,
"format": "auto"
}
]
}Authorization. Une clé qui se retrouve dans une page peut être lue par quiconque ouvre les outils de développement.Quand un véhicule n'est pas dans le catalogue
Cherchez d’abord : GET /api/v1/vehicles?q= trouve un modèle par nom, par préfixe ou par correspondance approximative, et POST /api/v1/images/resolve transforme une expression en texte libre en paramètres exacts, avec un indice de confiance exploitable. Ce qui manque encore ensuite a sa place sur le tableau des demandes : POST /api/v1/requests crée la demande ou, si une demande ouverte existe déjà pour la même marque, le même modèle et la même année, lui ajoute un vote au lieu de créer un doublon. Le catalogue lui-même est open source, et les ajouts profitent à tous les clients.
CLI, SDK et MCP
Tout le pipeline s’exécute depuis la CLI, sans la moindre ligne de code ; c’est ainsi que plusieurs catalogues de l’API ont été alimentés :
# One file per vehicle id. Re-running skips what exists, so a stopped backfill resumes.
while read -r id; do
[ -f "cdn/$id.webp" ] || npx @meterapp/car-image get --vehicle "$id" \
--view front-3-4 --color silver --width 800 --height 600 --fit contain \
--trim --padding 8 --format webp --out "cdn/$id.webp"
done < vehicle-ids.txt
# Or fifty signed URLs in one call, for a page of listings the browser loads itself.
npx @meterapp/car-image url --batch listings.json --ttl 604800 --renew --json- SDK :
client.vehicles({ year, makeId })pour énumérer,client.getImage({ vehicle, … })pour générer le rendu,client.createImageUrls(…)pour des lots d'URL signées. Consultez la documentation des images pour tous les paramètres. - MCP : un assistant connecté à
https://carimage.dev/api/mcpdispose du même catalogue (search_vehicles,resolve_vehicle) et des mêmes rendus (get_car_image,create_car_image_urls) : c’est le bon outil pour un responsable de catalogue qui ajoute un véhicule à la main, et le mauvais pour un import rétroactif de milliers de véhicules. Utilisez pour cela la CLI ou le SDK, et gardez l’assistant pour les cas restants. La configuration est décrite sur la page MCP.
Questions fréquentes
- Quel angle utiliser pour une annonce ?
- front-3-4 est l’angle par défaut et l’angle vedette classique, avant de la voiture vers la gauche. Chaque angle du côté gauche de la voiture a un jumeau -right (front-3-4-right, side-right, rear-3-4-right), pour qu’une carte puisse être orientée vers l’intérieur de la mise en page plutôt que vers l’extérieur. Choisissez-en un et tenez-vous-y : la cohérence entre les annonces fait l’essentiel de la valeur.
- Combien coûte un catalogue de 20 000 annonces ?
- 20 000 images de véhicules par mois si chaque annonce montre un véhicule dans une teinte et une vue, chacune livrée aussi souvent que nécessaire, dans n'importe quelle taille ; les annonces qui partagent le même véhicule, la même teinte et la même vue comptent pour une seule image, et la revalidation avec If-None-Match est gratuite. Pro inclut 2 500 images de véhicules par mois et Business 10 000, donc un catalogue de cette taille relève d'Enterprise, ou d'un Image Pack à paiement unique que vous conservez et hébergez vous-même définitivement. Un remplissage rétroactif au-delà du quota renvoie 402 et ne décompte rien.
- Pouvons-nous conserver les fichiers sur notre propre CDN ?
- Oui. Un forfait payant vous accorde une licence d’utilisation des rendus dans votre produit, copies sur vos propres serveurs ou CDN comprises, sans mention d’attribution, tant que le forfait est actif ; dans les 30 jours suivant sa fin, les copies stockées sont supprimées (les images d’un Image Pack peuvent être conservées définitivement). Ce que la licence n’autorise pas, c’est de les proposer sous forme de bibliothèque, de flux ou de jeu de données autonome. C’est un forfait actif qui permet de générer les rendus du millésime suivant.
- Que se passe-t-il quand le catalogue reçoit un nouveau millésime ?
- Le catalogue @meterapp/vehicle-db est mis à jour à mesure que les sources publient, et les identifiants existants ne changent jamais. Listez la nouvelle année, générez les rendus des identifiants que vous n'avez pas encore et laissez le reste tel quel. Les requêtes renvoient un ETag ; une requête répétée avec If-None-Match répond 304 et ne coûte rien.
- Pourquoi certaines de nos chaînes de marque et de modèle renvoient-elles une 404 ?
- Parce qu’ils ont été orthographiés par une autre base de données. Le catalogue accepte les noms, les slugs et de nombreuses variantes de badges et de types de carrosserie, mais une chaîne qu’il n’a jamais vue renvoie une 404 vehicle_not_found (gratuite, mais c’est un aller-retour inutile). Parcourez le catalogue et stockez ses identifiants au lieu de traduire vos propres chaînes, puis recherchez ou résolvez les cas restants.