{
  "id": "api-poller",
  "name": "API Poller",
  "category": "automation",
  "tags": ["api", "poll", "automation", "monitor", "diff", "watch", "endpoint"],
  "description": "Poll any HTTP endpoint at a chosen interval, render JSON response, and highlight diffs from the previous response. Configurable headers + auth.",
  "triggers": ["api poller", "poll api", "watch endpoint", "monitor api", "api watcher", "http monitor"],
  "defaultSize": { "w": 5, "h": 5 },
  "source": "function ApiPoller() {\n  const [url, setUrl] = React.useState('REPLACE_WITH_URL');\n  const [interval, setInterval] = React.useState(60);\n  const [headers, setHeaders] = React.useState('');\n  const [last, setLast] = React.useState(null);\n  const [prev, setPrev] = React.useState(null);\n  const [running, setRunning] = React.useState(false);\n  const [ts, setTs] = React.useState(null);\n  const [error, setError] = React.useState('');\n  React.useEffect(() => {\n    if (!running) return;\n    let alive = true;\n    const tick = async () => {\n      try {\n        const opts = {};\n        if (headers) { try { opts.headers = JSON.parse(headers); } catch {} }\n        const r = await fetch(url, opts);\n        const text = await r.text();\n        let body; try { body = JSON.parse(text); } catch { body = text; }\n        if (!alive) return;\n        setPrev(last); setLast(body); setTs(new Date()); setError('');\n      } catch (e) { setError(String(e?.message||e)); }\n    };\n    tick();\n    const id = window.setInterval(tick, Math.max(5, interval) * 1000);\n    return () => { alive = false; window.clearInterval(id); };\n  }, [running, url, interval, headers]);\n  const changed = JSON.stringify(prev) !== JSON.stringify(last);\n  return (\n    <div style={{padding:12,fontFamily:'system-ui',height:'100%',display:'flex',flexDirection:'column',gap:6}}>\n      <input value={url} onChange={e=>setUrl(e.target.value)} placeholder=\"https://...\" style={{padding:'6px 10px',border:'1px solid #ccc',borderRadius:6,fontSize:12,fontFamily:'monospace'}}/>\n      <div style={{display:'flex',gap:6}}>\n        <input type=\"number\" value={interval} onChange={e=>setInterval(Number(e.target.value)||60)} placeholder=\"sec\" style={{width:80,padding:'6px 8px',border:'1px solid #ccc',borderRadius:6,fontSize:12}}/>\n        <input value={headers} onChange={e=>setHeaders(e.target.value)} placeholder='{\"Authorization\":\"Bearer …\"}' style={{flex:1,padding:'6px 8px',border:'1px solid #ccc',borderRadius:6,fontSize:11,fontFamily:'monospace'}}/>\n        <button onClick={()=>setRunning(!running)} style={{padding:'6px 14px',border:'none',borderRadius:6,background:running?'#FF3B30':'#34C759',color:'#fff',cursor:'pointer'}}>{running?'Stop':'Start'}</button>\n      </div>\n      <div style={{fontSize:11,color:'#888'}}>Last poll: {ts ? ts.toLocaleTimeString() : '—'} {prev !== null && (changed ? <span style={{color:'#FF9500'}}>· changed!</span> : <span style={{color:'#34C759'}}>· no change</span>)}</div>\n      {error && <div style={{color:'#FF3B30',fontSize:12}}>{error}</div>}\n      <pre style={{flex:1,overflow:'auto',background:'#1e1e1e',color:'#d4d4d4',padding:10,borderRadius:8,fontSize:11,margin:0}}>{last !== null ? (typeof last === 'string' ? last : JSON.stringify(last, null, 2)) : 'No data yet.'}</pre>\n    </div>\n  );\n}\nrender(<ApiPoller/>);",
  "placeholders": [
    { "name": "REPLACE_WITH_URL", "description": "URL to poll", "default": "https://api.github.com/zen" }
  ]
}
