Welly Diecast — Find a Diecast for a Vehicle

Site wellydiecast.comTask find-diecast-model-2atm9jVersion v1Updated Jul 13, 2026Category shopping

Search Welly Die Casting Factory's manufacturer catalog by vehicle keyword, brand, or scale and return matching diecast models with Item No., model name, scale, photo URL, and shareable canonical URL. Read-only manufacturer reference — no prices or stock. This skill was captured from a live agent session on wellydiecast.com and publishes here verbatim, exactly as an agent receives it.

NoteSelectors and URL schemes drift as sites change. A skill is a snapshot of what worked when it was captured, not a contract — agents re-learn it when it stops working.

Purpose

Given a user's vehicle (year, make, model — or any subset), search Welly Die Casting Factory's product catalog at wellydiecast.com and return matching diecast models with their item number, model name, scale, photo URL, and shareable canonical URL. Read-only; the site has no cart, no checkout, no auth — it is the manufacturer's reference catalog, not a store.

When to Use

  • A collector wants to know if Welly produces a diecast of their car (e.g. "do they make a 1969 Mustang?").
  • An agent assembling shopping lists across diecast manufacturers — Welly is the catalog of record for Item Numbers like 12516, 22485NS, 43696S3 that resellers (eBay, Amazon, hobby shops) list under.
  • Cross-referencing a user-supplied Item No. (printed on the box) to the canonical product record (name, scale, photo).
  • Bulk enumeration of all diecast models in a given scale (e.g. "all 1:18 Fords").

Workflow

wellydiecast.com is a server-rendered PHP catalog behind Cloudflare with no anti-bot, no auth, no cookies, no JS rendering required for the result list. A plain HTTP GET with a generic User-Agent returns the full result HTML (verified 2026-05-22: a direct HTTP fetch returns 200 OK with all <div class="col" pid="…"> blocks intact, no Cloudflare challenge). The site uses jQuery + AJAX only to inflate the product-detail panel inline; the underlying detail/spec endpoints are also plain POST forms. Prefer fetch over scripted browsing — the browser path costs ~100× more turns and adds no information.

1. Parse the vehicle into search axes

From the user's vehicle, pick the most-specific axis available, in this priority order:

User inputSearch axisURL shape
Specific model name (e.g. "Mustang", "Skyline GT-R", "F-150")keyword/product.php?keyword=<urlenc>&mode=search
Item No. from a box (e.g. "12516", "22485NS")keyword/product.php?keyword=<itemno>&mode=search
Brand only (e.g. "any Ford", "any Porsche")brand/product.php?brand=<BRAND>
Brand + scale (e.g. "1:18 Fords")brand + sid + cid=1/product.php?cid=1&sid=<N>&brand=<BRAND>
Year + make + model (most specific)keyword with model name only — year filtering happens client-side on <div class="name">/product.php?keyword=<model>&mode=search

Recommendation: lead with keyword. The keyword field does a substring match against both the product name and the item number, so keyword=Mustang returns every Mustang regardless of year or scale; keyword=12516 returns the single product with that item number. brand= works but the brand enum is messy (see Gotchas) and many models from the same make are not consistently tagged.

Run the whole search — navigate, parse tiles, paginate — inside one browserless_function call. The function executes in a browser page context, so page.goto(searchUrl) loads the server-rendered HTML directly (no separate origin warm-up needed — the search URL is the origin), then page.evaluate parses the tiles in-page and you return a compact JSON projection rather than raw HTML. Multi-page results (<a class="next"> present and not .nolink) loop over &page=N (1-indexed, ~20 per page) in the same call:

export default async function ({ page, context }) {
  const { vehicleQuery, maxPages = 5 } = context;
  const HOST = "https://www.wellydiecast.com/";
  const base =
    HOST +
    "product.php?keyword=" +
    encodeURIComponent(vehicleQuery) +
    "&mode=search";
  const matches = [];
  let pagesScanned = 0;

  for (let n = 1; n <= maxPages; n++) {
    await page.goto(base + "&page=" + n, { waitUntil: "load", timeout: 45000 });
    const res = await page.evaluate(() => {
      const tiles = [];
      document.querySelectorAll("div.col[pid]").forEach((col) => {
        const q = (sel) => {
          const el = col.querySelector(sel);
          return el ? el.textContent.trim() : null;
        };
        const img = col.querySelector("table.photo img");
        tiles.push({
          pid: Number(col.getAttribute("pid")),
          item_no: q(".item_no"),
          name: q(".name"),
          scale: q(".scale"),
          photo_rel: img ? img.getAttribute("src") : null,
        });
      });
      return {
        tiles,
        noRecord: /No record found\./.test(document.body.innerText),
        atEnd:
          !!document.querySelector("a.next.nolink") ||
          !document.querySelector("a.next"),
      };
    });
    pagesScanned = n;

    for (const t of res.tiles) {
      matches.push({
        pid: t.pid,
        item_no: t.item_no,
        name: t.name,
        scale: t.scale,
        photo_url: t.photo_rel ? HOST + t.photo_rel : null,
        share_url: HOST + "share.php?pid=" + t.pid,
        detail_link:
          HOST +
          "product.php?keyword=" +
          encodeURIComponent(t.item_no) +
          "&mode=search#pid=" +
          t.pid,
      });
    }
    if (n === 1 && res.noRecord) {
      return {
        data: {
          query: { vehicle: vehicleQuery, axis: "keyword", url: base },
          no_results: true,
          matches: [],
        },
        type: "application/json",
      };
    }
    if (res.atEnd) break;
  }

  return {
    data: {
      query: { vehicle: vehicleQuery, axis: "keyword", url: base },
      no_results: matches.length === 0,
      total_pages_scanned: pagesScanned,
      matches,
    },
    type: "application/json",
  };
}

Pass { "vehicleQuery": "Mustang" } (or an item number) as the call's context. For a brand=/sid= query, swap the base URL for the brand URL shape from step 1 — the tile-parsing and pagination logic are identical.

3. Parse result blocks with a regex per <div class="col" pid="…">

Each catalog tile is one <div class="col" pid="N">…</div> containing:

FieldSelector
pid (Welly's internal product ID)<div class="col" pid="(\d+)">
Photo URL<table class="photo"><tr><td><img src="(upload/product/middle/[^"]+)"> → prefix with https://www.wellydiecast.com/
Model name<div class="name">([^<]+)</div>
Item number (the SKU printed on the box)<div class="item_no">([^<]+)</div>
Scale (free-text)<div class="scale">([^<]+)</div>
Shareable canonical URLhttps://www.wellydiecast.com/share.php?pid={pid}

Empty-slot tiles <div class="col empty"></div> exist to pad rows of 4 — skip them.

Zero-results sentinel: when the search has no matches, the body of .product_list_container contains <div class="text">No record found.</div> and no pid="…" blocks. Return { "matches": [], "no_results": true }.

4. (Optional) Enrich each match with the detail-panel data

The result tile gives you everything most users need (name, item no., scale, photo). For high-resolution photo and a confirmed "Category" string, POST to the AJAX endpoints used by the inline detail panel. Because a bare fetch() in the page context has no network egress until the page is navigated, page.goto('https://www.wellydiecast.com/') first, then run the same-origin POSTs inside page.evaluate — one browserless_function call covers both endpoints:

export default async function ({ page, context }) {
  const { pid } = context;
  await page.goto("https://www.wellydiecast.com/", {
    waitUntil: "load",
    timeout: 45000,
  });
  const fragments = await page.evaluate(async (pid) => {
    const post = (path) =>
      fetch(path, {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: "pid=" + pid,
      }).then((r) => r.text());
    return {
      detail: await post("/product_detail.php"), // high-res photo + brand logo
      spec: await post("/product_spec.php"), // Item No., Category (scale group), Model — more reliably formatted
    };
  }, pid);
  return { data: fragments, type: "application/json" };
}

Both endpoints return HTML fragments (not JSON) — parse them with regexes like the result tile. Verified 2026-05-22 in iter-1: both return 200 with the documented HTML fragments.

Browser fallback

If the in-page fetch ever returns a Cloudflare challenge page (not observed in 2026-05-22 testing across 6 result pages and 2 AJAX endpoints), fall back to a full browserless_agent call on a residential-proxy stealth session. Keep the whole flow — navigate, clear the challenge, extract — in one call's commands array; there is no release step (nothing to release), and one call avoids extra round-trips:

{
  "proxy": { "proxy": "residential", "proxyCountry": "us" },
  "commands": [
    {
      "method": "goto",
      "params": {
        "url": "https://www.wellydiecast.com/product.php?keyword=Mustang&mode=search",
        "waitUntil": "load",
        "timeout": 45000
      }
    },
    { "method": "solve", "params": { "type": "cloudflare" } },
    { "method": "html", "params": { "selector": "body" } }
  ]
}

Then parse the returned body HTML with the same regexes/selectors as step 3. (For cleaner output you can replace the final html command with an evaluate that runs the step-2 in-page tile parser and returns the compact JSON projection directly.) Because Welly is behind Cloudflare (proxies: true in the frontmatter), repeat the same proxy argument on every fallback call — the session is keyed by it, so reusing it reconnects to the same warmed session while dropping or changing it lands you in a different one.

Do not rely on the snapshot command for parsing — the accessibility tree collapses the catalog tiles into long flat StaticText runs and drops the pid attribute. Use the html/evaluate output and the selector set above.

Site-Specific Gotchas

  • Welly is a manufacturer, not a store. Product pages have no price, no stock status, no buy button. The skill returns model identifiers (item_no, name, scale, pid) — the consumer of this skill then takes those into eBay/Amazon/hobby-shop searches to actually buy one. Don't fabricate prices or availability; they do not exist on this site.
  • The brand= enum is dirty. The same make appears under multiple variants because of historical data-entry drift. Observed duplicates in /product.php brand list (2026-05-22): PORSCHEPorche (typo), FIATFAIT+ (typo) ↔ +Fiat, CHEVROLET+CHEVROLET, KAWASAKIKawasak, MINIMINI+COOPERMini+Cooper1, Aston Martin ↔ (no UPPERCASE variant), Mazda (mixed-case only). When the user supplies a brand, the safer query is keyword=<BRAND> not brand=<BRAND>keyword substring-matches against the model-name string which contains "FORD MUSTANG", "NISSAN SILVIA" etc, capturing all variants in one shot.
  • brand= is case-sensitive. brand=NISSAN returns results; brand=nissan returns zero. Map user input to the catalog's UPPERCASE canonical form (or fall back to keyword, see above).
  • Scale is free-text in the result tile. Strings observed: 1:18 Scales, 1:24-27 SCALE (note: 27 not 1:27 — inconsistent with the menu's 1:24-1:27 Scales), 1:32 Scales, 1:34-1:39 Scales, 1:43-1:49 Scales, 1:60-1:64 Scales, 1:87 Scales, 1:10 Motos, 1:18 Motos. Match loosely with a regex like 1:(\d+)(?:-1?:?(\d+))? rather than equality.
  • Categories (cid) and scales (sid) are stable enums:
    • cid=1 = SCALE MODEL (the diecast-collector catalog)
    • cid=2 = TOYS (BELOW… playsets, novelty)
    • Under cid=1: sid=1→1:18, sid=2→1:24-1:27, sid=3→1:18 Motos, sid=4→1:32, sid=5→1:34-1:39, sid=6→1:43-1:49, sid=7→1:60-1:64, sid=8→1:87, sid=18→1:10 Motos.
  • Pagination is &page=N, 1-indexed, ~20 per page. Detect end-of-results by <a class="next nolink"></a> (the nolink class on the next-page anchor means there is no next page). The first page does not need &page=1 but accepts it.
  • No canonical "product detail page" exists. Welly's catalog UI expands the detail inline via AJAX (product.init() JS). The user-facing canonical shareable URL is https://www.wellydiecast.com/product.php?keyword=<itemno>&mode=search#pid=<pid> (which auto-opens the detail panel on load) or https://www.wellydiecast.com/share.php?pid=<pid> (their generated short URL — verified via the QR-code generator in product_spec.php). The hash-fragment URL is friendlier for humans; the share URL is what their own QR encodes.
  • #pid=N only works on a result page that includes that pid. Linking to product.php?keyword=Mustang&mode=search#pid=80 opens the detail panel because pid 80 is in the Mustang result set. Linking to product.php#pid=80 (no search) opens the catalog index without the panel. Always link a detail through its own search context.
  • Item numbers have alphanumeric suffixes that matter. 12519H, 12519C, 22485NS, 22485S, 43696S3 — the trailing letters denote body style or variant (H=hardtop, C=convertible, NS=non-stock, S3=series 3 etc.). Don't strip them; they distinguish otherwise-identical-named models.
  • Photo URLs need the host prefix. Result HTML carries src="upload/product/middle/2017-01-12/690237635467.jpg" (relative). Prefix https://www.wellydiecast.com/ to make them absolute. The middle/ segment is the medium-resolution catalog tile; large/ is the detail-panel high-res; tn/ is the new-items thumbnail.
  • Cloudflare passes a generic UA. No a residential proxy or stealth needed for a direct HTTP fetch. The site sets a PHPSESSID cookie but never validates it — discard it. The Cloudflare beacon (cdn-cgi/challenge-platform/scripts/jsd/main.js) is loaded but not interactively enforced on our requests.
  • product.php with no params lists all brand filter links in the left nav. That page is a useful one-shot to harvest the full brand enum if needed — but expect ~85 entries including the duplicates listed above.

Expected Output

Three distinct outcome shapes:

// Matches found
{
  "query": {
    "vehicle": "1969 Ford Mustang",
    "axis": "keyword",
    "url": "https://www.wellydiecast.com/product.php?keyword=Mustang&mode=search"
  },
  "no_results": false,
  "total_pages_scanned": 2,
  "matches": [
    {
      "pid": 80,
      "item_no": "12516",
      "name": "1969 FORD MUSTANG",
      "scale": "1:18 Scales",
      "photo_url": "https://www.wellydiecast.com/upload/product/middle/2017-01-12/224922330185.jpg",
      "share_url": "https://www.wellydiecast.com/share.php?pid=80",
      "detail_link": "https://www.wellydiecast.com/product.php?keyword=12516&mode=search#pid=80"
    },
    {
      "pid": 76,
      "item_no": "12519H",
      "name": "1964-1/2 FORD MUSTANG COUPE",
      "scale": "1:18 Scales",
      "photo_url": "https://www.wellydiecast.com/upload/product/middle/2017-01-12/671176041761.jpg",
      "share_url": "https://www.wellydiecast.com/share.php?pid=76",
      "detail_link": "https://www.wellydiecast.com/product.php?keyword=12519H&mode=search#pid=76"
    }
  ]
}
// No results
{
  "query": {
    "vehicle": "Toyota Prius",
    "axis": "keyword",
    "url": "https://www.wellydiecast.com/product.php?keyword=Prius&mode=search"
  },
  "no_results": true,
  "matches": [],
  "suggestion": "Welly's catalog does not include this model. Try a broader query (e.g. brand only) or check that the year/trim is correct."
}
// Ambiguous brand — multiple enum variants for the same make
{
  "query": {
    "vehicle": "any Porsche",
    "axis": "brand",
    "url": "https://www.wellydiecast.com/product.php?brand=PORSCHE"
  },
  "no_results": false,
  "brand_variants_detected": ["PORSCHE", "Porche"],
  "note": "Brand enum contains duplicate variants; combined results across all variants returned below.",
  "matches": [
    {
      "pid": 1234,
      "item_no": "24081",
      "name": "PORSCHE 911 CARRERA S",
      "scale": "1:24-27 SCALE",
      "photo_url": "https://www.wellydiecast.com/upload/product/middle/…",
      "share_url": "https://www.wellydiecast.com/share.php?pid=1234",
      "detail_link": "https://www.wellydiecast.com/product.php?keyword=24081&mode=search#pid=1234"
    }
  ]
}

Call it

GET https://production-sfo.browserless.io/skills?token=TOKEN-HERE&domain=wellydiecast.com&task=find-diecast-model-2atm9j