{
  "version": "1.0.0",
  "exported_at": "2026-05-31T00:00:00.000Z",
  "project": {
    "name": "eBay Product Scraper for US",
    "description": "Scrapes eBay US product detail pages from one or more product/listing URLs. Navigation uses a multi-URL loop: each URL in the Navigate block is opened, the page is loaded, product details are extracted, then Loop Continue advances to the next URL. Extracts product title, condition, inventory, sold count, price, seller name, positive feedback, return policy, product URL, seller link, eBay item number, item details, category, shipping/delivery text, and image URLs. Best-effort template: eBay may show CAPTCHA, bot checks, location-dependent content, hidden title modules, or layout variants; some fields may be blank when not publicly displayed.",
    "color": "bg-[#4589ff]",
    "template_id": "ai-generated"
  },
  "blocks": [
    {
      "block_id": "navigate-1",
      "block_type": "process",
      "title": "Navigate",
      "description": "Go to a URL",
      "position_x": 120,
      "position_y": 220,
      "config": {
        "urls": [
          "https://www.ebay.com/p/15065002804?iid=326090737825",
          "https://www.ebay.com/p/15025216336?iid=276432163206",
          "https://www.ebay.com/itm/334454341141?epid=28043687656&itmmeta=01HW2X91BFFM6YVXZNB66H4Z3W&hash=item4ddf083e15:g:r84AAOSw7PljcpWy&var=544249701162"
        ],
        "color": "bg-[#4589ff]",
        "tags": [
          "ebay",
          "product-detail",
          "multi-url"
        ]
      }
    },
    {
      "block_id": "wait-for-page-load-1",
      "block_type": "process",
      "title": "Wait for Page Load",
      "description": "Wait for page to finish loading",
      "position_x": 456,
      "position_y": 220,
      "config": {
        "timeout": 30
      }
    },
    {
      "block_id": "inject-javascript-1",
      "block_type": "process",
      "title": "Inject JavaScript",
      "description": "Execute custom JavaScript",
      "position_x": 792,
      "position_y": 220,
      "config": {
        "jsCode": "(() => { const selectors = ['#gdpr-banner-accept', 'button#gdpr-banner-accept', '[data-testid=cookie-banner-accept]', 'button[aria-label*=Accept]', 'button[aria-label*=accept]']; for (const sel of selectors) { const el = document.querySelector(sel); if (el) { el.click(); return 'clicked cookie/consent control'; } } return 'no cookie/consent control found'; })()",
        "waitForCompletion": true,
        "timeout": 10
      }
    },
    {
      "block_id": "sleep-1",
      "block_type": "process",
      "title": "Sleep",
      "description": "Wait for specified time",
      "position_x": 1128,
      "position_y": 220,
      "config": {
        "duration": 2
      }
    },
    {
      "block_id": "wait-for-element-1",
      "block_type": "process",
      "title": "Wait for Element",
      "description": "Wait until element appears",
      "position_x": 1464,
      "position_y": 220,
      "config": {
        "selector": "h1, [data-testid=x-item-title], .x-item-title__mainTitle, .x-price-primary, [data-testid=x-price-primary]",
        "timeout": 30,
        "visible": false
      }
    },
    {
      "block_id": "structured-export-1",
      "block_type": "process",
      "title": "Structured Export",
      "description": "Export data with custom columns",
      "position_x": 1800,
      "position_y": 220,
      "config": {
        "rowSelector": "body",
        "fileName": "ebay-product-scraper-for-us.csv",
        "saveLocation": "C:\\Users\\theskd\\Documents\\UScraper\\templates",
        "includeHeaders": true,
        "fileMode": "append",
        "columns": [
          {
            "name": "product_title",
            "selector": "(() => { const clean = v => (v || '').replace(/\\s+/g, ' ').trim(); const t = s => { const e = document.querySelector(s); return e ? clean(e.innerText || e.textContent) : ''; }; return t('[data-testid=x-item-title]') || t('.x-item-title__mainTitle') || t('h1') || document.title.replace(/\\|\\s*eBay.*$/i, '').trim(); })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "condition",
            "selector": "(() => { const clean = v => (v || '').replace(/\\s+/g, ' ').trim().replace(/^Condition:\\s*/i, ''); const t = s => { const e = document.querySelector(s); return e ? clean(e.innerText || e.textContent) : ''; }; const bySelector = t('[data-testid=x-item-condition]') || t('.x-item-condition-text') || t('[itemprop=itemCondition]'); if (bySelector) return bySelector; const txt = document.body.innerText || document.body.textContent || ''; const m = txt.match(/Condition\\s*:?\\s*([^\\n]+)/i); return m ? clean(m[1]) : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "inventory",
            "selector": "(() => { const txt = (document.body.innerText || document.body.textContent || '').replace(/\\r/g, ''); const m = txt.match(/(Limited quantity available|More than \\d+ available|Only \\d+ left|Last one|Out of stock|Available quantity[^\\n]*)/i); return m ? m[1].trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "sold",
            "selector": "(() => { const txt = document.body.innerText || document.body.textContent || ''; const m = txt.match(/([\\d,]+\\s+sold)/i); return m ? m[1].trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "price",
            "selector": "(() => { const clean = v => (v || '').replace(/\\s+/g, ' ').trim(); const t = s => { const e = document.querySelector(s); return e ? clean(e.innerText || e.textContent || e.getAttribute('content')) : ''; }; const bySelector = t('[data-testid=x-price-primary]') || t('.x-price-primary') || t('[itemprop=price]') || t('.notranslate'); if (bySelector) return bySelector; const txt = document.body.innerText || document.body.textContent || ''; const m = txt.match(/(?:US\\s*)?\\$[\\d,]+(?:\\.\\d{2})?(?:\\/ea)?/i); return m ? m[0].trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "seller_name",
            "selector": "(() => { const clean = v => (v || '').replace(/\\s+/g, ' ').trim(); const t = s => { const e = document.querySelector(s); return e ? clean(e.innerText || e.textContent) : ''; }; const bySelector = t('[data-testid=x-sellercard-atf] a') || t('.x-sellercard-atf__info__about-seller a') || t('.mbg-nw') || t('[data-testid=seller-section] a'); if (bySelector) return bySelector; const a = Array.from(document.links).find(a => (a.href.includes('/str/') || a.href.includes('/usr/')) && clean(a.innerText || a.textContent)); return a ? clean(a.innerText || a.textContent) : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "positive_feedback",
            "selector": "(() => { const txt = document.body.innerText || document.body.textContent || ''; const m = txt.match(/(\\d{1,3}(?:\\.\\d)?%)\\s*positive feedback/i) || txt.match(/(\\d{1,3}(?:\\.\\d)?%)\\s*Positive/i); return m ? m[1].trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "return_policy",
            "selector": "(() => { const clean = v => (v || '').replace(/\\s+/g, ' ').trim(); const t = s => { const e = document.querySelector(s); return e ? clean(e.innerText || e.textContent) : ''; }; const bySelector = t('[data-testid=x-returns-minview]') || t('.x-returns-minview') || t('[data-testid=return-policy]'); if (bySelector) return bySelector; const txt = (document.body.innerText || document.body.textContent || '').replace(/\\r/g, ''); const m = txt.match(/(\\d+\\s+days returns\\.[^\\n]*)/i) || txt.match(/(No returns[^\\n.]*(?:guarantee)?[^\\n.]*)/i); return m ? clean(m[1]) : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "shipping_delivery",
            "selector": "(() => { const clean = v => (v || '').replace(/\\s+/g, ' ').trim(); const preferred = Array.from(document.querySelectorAll('[data-testid=x-shipping-minview], .x-shipping-minview, [data-testid=ux-labels-values-shipping]')).map(e => clean(e.innerText || e.textContent)).filter(Boolean); if (preferred.length) return preferred[0]; const txt = document.body.innerText || document.body.textContent || ''; const m = txt.match(/(?:Shipping|Delivery)\\s*:?\\s*([^\\n]{1,220})/i); return m ? clean(m[0]) : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "product_url",
            "selector": "(() => location.href)()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "seller_link",
            "selector": "(() => { const a = Array.from(document.links).find(a => a.href.includes('/str/') || a.href.includes('/usr/')); return a ? a.href : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "ebay_item_number",
            "selector": "(() => { try { const u = new URL(location.href); const iid = u.searchParams.get('iid'); if (iid) return iid; } catch(e) {} const urlMatch = location.href.match(/\\/itm\\/(\\d+)/); if (urlMatch) return urlMatch[1]; const txt = document.body.innerText || document.body.textContent || ''; const m = txt.match(/eBay item number\\s*:?\\s*(\\d+)/i) || txt.match(/Item number\\s*:?\\s*(\\d+)/i); return m ? m[1] : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "rating",
            "selector": "(() => { const txt = document.body.innerText || document.body.textContent || ''; const m = txt.match(/([\\d.]+)\\s+out of\\s+5\\s+stars/i) || txt.match(/Rating\\s*:?\\s*([\\d.]+)/i); return m ? m[1].trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "feedback_count",
            "selector": "(() => { const txt = document.body.innerText || document.body.textContent || ''; const m = txt.match(/([\\d,]+)\\s+product ratings/i) || txt.match(/([\\d,]+)\\s+ratings/i) || txt.match(/\\((\\d[\\d,]*)\\)\\s*feedback/i); return m ? m[1].trim() : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "category",
            "selector": "(() => { const clean = v => (v || '').replace(/\\s+/g, ' ').trim(); const crumbs = Array.from(document.querySelectorAll('nav[aria-label*=Breadcrumb] a, .breadcrumbs a, [data-testid=ux-breadcrumbs] a')).map(a => clean(a.innerText || a.textContent)).filter(Boolean); return crumbs.join(' > '); })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "item_details",
            "selector": "(() => { const clean = v => (v || '').replace(/\\s+/g, ' ').trim(); const root = document.querySelector('[data-testid=x-about-this-item], .x-about-this-item, .ux-layout-section, [data-testid=ux-layout-section]'); return root ? clean(root.innerText || root.textContent) : ''; })()",
            "attribute": "text",
            "isJs": true
          },
          {
            "name": "image_urls",
            "selector": "(() => { const urls = Array.from(document.images).map(img => img.currentSrc || img.src).filter(src => src && src.includes('i.ebayimg.com')).map(src => src.replace(/\\?.*$/, '')); return Array.from(new Set(urls)).join(' | '); })()",
            "attribute": "text",
            "isJs": true
          }
        ]
      }
    },
    {
      "block_id": "loop-continue-1",
      "block_type": "process",
      "title": "Loop Continue",
      "description": "Continue multi-input loop",
      "position_x": 2136,
      "position_y": 220,
      "config": {}
    }
  ],
  "connections": [
    {
      "from_block_id": "navigate-1",
      "from_connector_id": "right",
      "to_block_id": "wait-for-page-load-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-page-load-1",
      "from_connector_id": "right",
      "to_block_id": "inject-javascript-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "inject-javascript-1",
      "from_connector_id": "right",
      "to_block_id": "sleep-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "sleep-1",
      "from_connector_id": "right",
      "to_block_id": "wait-for-element-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "wait-for-element-1",
      "from_connector_id": "right",
      "to_block_id": "structured-export-1",
      "to_connector_id": "left"
    },
    {
      "from_block_id": "structured-export-1",
      "from_connector_id": "right",
      "to_block_id": "loop-continue-1",
      "to_connector_id": "left"
    }
  ],
  "canvas_elements": [
    {
      "id": "group-load",
      "element_type": "group",
      "title": "Page Load",
      "color": "#08bdba",
      "position_x": 48,
      "position_y": 116,
      "width": 1664,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "navigate-1",
          "wait-for-page-load-1",
          "sleep-1",
          "wait-for-element-1"
        ]
      }
    },
    {
      "id": "group-interaction",
      "element_type": "group",
      "title": "Interaction",
      "color": "#a56eff",
      "position_x": 720,
      "position_y": 116,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "inject-javascript-1"
        ]
      }
    },
    {
      "id": "group-extract",
      "element_type": "group",
      "title": "Data Extraction",
      "color": "#42be65",
      "position_x": 1728,
      "position_y": 116,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "structured-export-1"
        ]
      }
    },
    {
      "id": "group-pagination",
      "element_type": "group",
      "title": "Pagination Loop",
      "color": "#ff832b",
      "position_x": 2064,
      "position_y": 116,
      "width": 380,
      "height": 296,
      "z_index": 20,
      "data": {
        "memberBlockIds": [
          "loop-continue-1"
        ]
      }
    },
    {
      "id": "note-overview",
      "element_type": "note",
      "title": "Overview",
      "content": "Scrapes eBay US product detail pages from one or more product/listing URLs. Navigation uses a multi-URL loop: each URL in the Navigate block is opened, the page is loaded, product details are extracted, then Loop Continue advances to the next URL. Extracts product title, condition, inventory, sold count, price, seller name, positive feedback, return policy, product URL, seller link, eBay item number, item details, category, shipping/delivery text, and image URLs. Best-effort template: eBay may show CAPTCHA, bot checks, location-dependent content, hidden title modules, or layout variants; some fields may be blank when not publicly displayed.",
      "color": "#f1c21b",
      "position_x": 80,
      "position_y": 20,
      "width": 480,
      "height": 160,
      "z_index": 22,
      "data": {}
    },
    {
      "id": "note-block-navigate-1",
      "element_type": "note",
      "title": "Note: Navigate",
      "content": "Multi-URL loop over 3 pages. Pair with loop-continue at the end of each iteration.",
      "color": "#ee5396",
      "position_x": 320,
      "position_y": 200,
      "width": 328,
      "height": 107,
      "z_index": 22,
      "data": {
        "block_id": "navigate-1"
      }
    },
    {
      "id": "note-block-inject-javascript-1",
      "element_type": "note",
      "title": "Note: Inject JavaScript",
      "content": "Runs custom JavaScript in the page: `(() => { const selectors = ['#gdpr-banner-accept', 'button#gdpr-banner-accept', '[data-testid=cookie...` Verify in browser if results are empty.",
      "color": "#ee5396",
      "position_x": 992,
      "position_y": 200,
      "width": 340,
      "height": 140,
      "z_index": 22,
      "data": {
        "block_id": "inject-javascript-1"
      }
    },
    {
      "id": "note-block-structured-export-1",
      "element_type": "note",
      "title": "Note: Structured Export",
      "content": "Structured export with JS columns (product_title, condition, inventory, sold, price). These selectors are fragile — update if the site layout changes.",
      "color": "#ee5396",
      "position_x": 2000,
      "position_y": 200,
      "width": 340,
      "height": 130,
      "z_index": 22,
      "data": {
        "block_id": "structured-export-1"
      }
    },
    {
      "id": "note-block-loop-continue-1",
      "element_type": "note",
      "title": "Note: Loop Continue",
      "content": "Loop Continue advances a multi-URL or multi-text loop. Place at the end of the loop body with a clear back-edge to the loop start.",
      "color": "#ee5396",
      "position_x": 2336,
      "position_y": 200,
      "width": 340,
      "height": 123,
      "z_index": 22,
      "data": {
        "block_id": "loop-continue-1"
      }
    }
  ]
}