# ChatGPT API, stable product IDs and inline shopping placeholders > Source: https://scrape.do/documentation/changelog/2026-06-23-chatgpt-shopping-placeholders/ 2026-06-23 ยท feature Every entry in `tool_data.shopping[]` now carries a stable **`id`**, and a new opt-in mode lets you render your own product cards inline without guessing positions. ## What's new - **Stable product `id`.** Each `tool_data.shopping[]` entry now includes an `id` that is consistent for a given product within a response. - **`shopping_placeholders=1`.** When set, each inline product in `output.markdown` is emitted as a `{{shopping:}}` token instead of a link, where `` matches a `tool_data.shopping[].id`. ## Why it matters If you render product cards yourself, you can now scan `output.markdown` for `{{shopping:}}` tokens and swap each for your own card, looked up by `id` in `tool_data.shopping[]`. - **No positional drift.** The same product can appear at more than one inline position, and `tool_data.shopping[]` order does not always follow the text. Matching by `id` stays correct where counting positions would not. - **Every token resolves.** A token is only emitted when its product has a matching `tool_data.shopping[]` entry; otherwise the product is left as normal text. - **Backward compatible.** The mode is opt-in. Without it, responses are unchanged, and `output.html` and `data` are never affected by it. See the [ChatGPT API documentation](/documentation/chatgpt-api#render-your-own-product-cards) for the full rendering guide.