SnapAPI/public/index.html
Hoid 56c7a87f3c
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled
feat: add developer blog with two posts
- Blog index page (public/blog.html) with dark theme
- Post 1: Why You Need a Screenshot API (~800 words)
- Post 2: Screenshot API Performance & Caching (~600 words)
- Express routes: /blog → /blog.html, /blog/:slug → /blog/:slug.html
- Blog link added to nav and footer on index.html
- Sitemap updated with blog URLs
- Full test coverage (19 new tests, 190 total passing)
2026-03-02 21:10:29 +01:00

838 lines
46 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>SnapAPI — Screenshot API for Developers | EU-Hosted, GDPR Compliant</title>
<meta name="description" content="Convert any URL to a pixel-perfect screenshot with a simple API call. PNG, JPEG, WebP. EU-hosted in Germany, fully GDPR compliant. Try it free in the playground.">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📸</text></svg>">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{
--bg:#0a0e17;--bg2:#0f1420;--card:#141a28;--card-hover:#1a2235;
--border:#1e2a3f;--border-light:#2a3752;
--text:#f0f2f7;--text-secondary:#94a3c0;--muted:#6b7a96;
--primary:#4f8fff;--primary-light:#6da3ff;--primary-dark:#3a6fd8;--primary-glow:rgba(79,143,255,0.15);
--accent:#10b981;--accent-glow:rgba(16,185,129,0.15);
--purple:#a78bfa;--orange:#f59e0b;--pink:#ec4899;
--gradient:linear-gradient(135deg,#4f8fff 0%,#a78bfa 50%,#ec4899 100%);
--radius:12px;--radius-lg:16px;--radius-xl:24px;
}
html{scroll-behavior:smooth}
body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.6;-webkit-font-smoothing:antialiased}
a{color:var(--primary-light);text-decoration:none;transition:color .2s}
a:hover{color:var(--primary)}
::selection{background:var(--primary);color:#fff}
.container{max-width:1180px;margin:0 auto;padding:0 24px}
.section{padding:100px 0}
.section-label{font-size:.75rem;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--primary);margin-bottom:12px}
.section-title{font-size:2.5rem;font-weight:800;line-height:1.2;margin-bottom:16px}
.section-subtitle{font-size:1.1rem;color:var(--text-secondary);max-width:600px;line-height:1.7}
.text-center{text-align:center}
.text-center .section-subtitle{margin:0 auto}
.btn{display:inline-flex;align-items:center;gap:8px;padding:12px 28px;border-radius:10px;font-weight:600;font-size:.95rem;border:none;cursor:pointer;transition:all .2s;font-family:inherit}
.btn-primary{background:var(--primary);color:#fff;box-shadow:0 4px 20px rgba(79,143,255,0.3)}
.btn-primary:hover{background:var(--primary-dark);transform:translateY(-1px);box-shadow:0 6px 28px rgba(79,143,255,0.4);color:#fff}
.btn-secondary{background:rgba(255,255,255,0.06);color:var(--text);border:1px solid var(--border)}
.btn-secondary:hover{background:rgba(255,255,255,0.1);border-color:var(--border-light);color:#fff}
.btn-lg{padding:16px 36px;font-size:1.05rem;border-radius:12px}
.btn-sm{padding:8px 18px;font-size:.85rem}
nav{position:sticky;top:0;z-index:100;background:rgba(10,14,23,0.85);backdrop-filter:blur(20px);border-bottom:1px solid rgba(30,42,63,0.5);padding:0 24px}
.nav-inner{max-width:1180px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;height:64px}
.nav-logo{font-size:1.15rem;font-weight:800;display:flex;align-items:center;gap:8px;color:var(--text)}
.nav-logo span{background:var(--gradient);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.nav-links{display:flex;gap:32px;align-items:center}
.nav-links a{color:var(--muted);font-size:.9rem;font-weight:500;transition:color .2s}
.nav-links a:hover{color:var(--text)}
.nav-cta{margin-left:8px}
.nav-mobile{display:none;background:none;border:none;color:var(--text);font-size:1.5rem;cursor:pointer}
.hero{padding:100px 0 80px;text-align:center;position:relative;overflow:hidden}
.hero::before{content:'';position:absolute;top:-200px;left:50%;transform:translateX(-50%);width:800px;height:800px;background:radial-gradient(circle,rgba(79,143,255,0.08) 0%,transparent 70%);pointer-events:none}
.hero-badge{display:inline-flex;align-items:center;gap:8px;padding:6px 16px 6px 8px;background:rgba(79,143,255,0.1);border:1px solid rgba(79,143,255,0.2);border-radius:100px;font-size:.82rem;font-weight:500;color:var(--primary-light);margin-bottom:28px}
.hero-badge .dot{width:8px;height:8px;background:var(--accent);border-radius:50%;animation:pulse 2s infinite}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
.hero h1{font-size:4rem;font-weight:900;line-height:1.1;margin-bottom:24px;letter-spacing:-.02em}
.hero h1 .gradient{background:var(--gradient);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.hero p{font-size:1.2rem;color:var(--text-secondary);max-width:580px;margin:0 auto 40px;line-height:1.7}
.hero-buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap;margin-bottom:48px}
.trust-badges{display:flex;gap:32px;justify-content:center;flex-wrap:wrap;margin-bottom:60px}
.trust-badge{display:flex;align-items:center;gap:8px;font-size:.85rem;color:var(--muted);font-weight:500}
.trust-badge .icon{font-size:1.1rem}
.code-preview{max-width:700px;margin:0 auto;position:relative}
.code-window{background:var(--card);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden;box-shadow:0 20px 60px rgba(0,0,0,0.3)}
.code-titlebar{display:flex;align-items:center;gap:8px;padding:14px 20px;background:rgba(0,0,0,0.2);border-bottom:1px solid var(--border)}
.code-dot{width:12px;height:12px;border-radius:50%;background:#333}
.code-dot:nth-child(1){background:#ff5f57}
.code-dot:nth-child(2){background:#ffbd2e}
.code-dot:nth-child(3){background:#28c840}
.code-titlebar span{flex:1;text-align:center;font-size:.8rem;color:var(--muted);font-weight:500}
.code-body{padding:24px;font-family:'JetBrains Mono',monospace;font-size:.85rem;line-height:1.9;overflow-x:auto;color:var(--text-secondary)}
.code-body .kw{color:var(--purple)}
.code-body .str{color:var(--accent)}
.code-body .flag{color:var(--orange)}
.code-body .cmt{color:#475569;font-style:italic}
.code-body .url{color:var(--primary-light)}
.code-body .fn{color:var(--primary-light)}
.code-body .prop{color:var(--orange)}
.code-body .num{color:var(--accent)}
.code-tabs{display:flex;gap:4px;flex:1;justify-content:center}
.code-tab{background:none;border:none;color:var(--muted);font-size:.8rem;font-family:inherit;font-weight:500;padding:4px 12px;border-radius:4px;cursor:pointer;transition:all .15s}
.code-tab:hover{color:var(--text-secondary)}
.code-tab.active{color:var(--text);background:rgba(255,255,255,0.08)}
.stats{display:grid;grid-template-columns:repeat(3,1fr);gap:0;max-width:700px;margin:0 auto;background:var(--card);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden}
.stat{padding:32px;text-align:center;border-right:1px solid var(--border)}
.stat:last-child{border-right:none}
.stat .number{font-size:2rem;font-weight:800;background:var(--gradient);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.stat .label{font-size:.82rem;color:var(--muted);margin-top:4px;font-weight:500}
.features-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:24px;margin-top:48px}
.feature-card{background:var(--card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:36px 28px;transition:all .3s}
.feature-card:hover{border-color:var(--border-light);background:var(--card-hover);transform:translateY(-2px)}
.feature-icon{width:48px;height:48px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:1.5rem;margin-bottom:20px}
.feature-icon.blue{background:rgba(79,143,255,0.12)}
.feature-icon.green{background:rgba(16,185,129,0.12)}
.feature-icon.purple{background:rgba(167,139,250,0.12)}
.feature-icon.orange{background:rgba(245,158,11,0.12)}
.feature-icon.pink{background:rgba(236,72,153,0.12)}
.feature-icon.cyan{background:rgba(34,211,238,0.12)}
.feature-card h3{font-size:1.05rem;font-weight:700;margin-bottom:10px}
.feature-card p{color:var(--text-secondary);font-size:.9rem;line-height:1.6}
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:40px;margin-top:48px}
.step{text-align:center;position:relative}
.step-number{width:56px;height:56px;border-radius:16px;background:var(--primary-glow);border:1px solid rgba(79,143,255,0.3);display:flex;align-items:center;justify-content:center;font-size:1.3rem;font-weight:800;color:var(--primary);margin:0 auto 20px}
.step h3{font-size:1.05rem;font-weight:700;margin-bottom:8px}
.step p{color:var(--text-secondary);font-size:.88rem;line-height:1.6}
.eu-section{background:var(--bg2);border-top:1px solid var(--border);border-bottom:1px solid var(--border)}
.eu-grid{display:grid;grid-template-columns:1fr 1fr;gap:60px;align-items:center;margin-top:48px}
.eu-features{display:flex;flex-direction:column;gap:20px}
.eu-feature{display:flex;gap:16px;align-items:flex-start}
.eu-feature .check{width:36px;height:36px;border-radius:10px;background:var(--accent-glow);display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:1.1rem}
.eu-feature h4{font-size:.95rem;font-weight:600;margin-bottom:4px}
.eu-feature p{color:var(--text-secondary);font-size:.85rem;line-height:1.5}
.eu-visual{background:var(--card);border:1px solid var(--border);border-radius:var(--radius-xl);padding:48px;text-align:center}
.eu-visual .flag{font-size:5rem;margin-bottom:16px}
.eu-visual h3{font-size:1.3rem;font-weight:700;margin-bottom:8px}
.eu-visual p{color:var(--text-secondary);font-size:.9rem}
.playground{margin-top:48px;background:var(--card);border:1px solid var(--border);border-radius:var(--radius-xl);overflow:hidden}
.playground-header{padding:24px 32px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between}
.playground-header h3{font-size:1rem;font-weight:700}
.playground-body{display:grid;grid-template-columns:1fr 1fr;min-height:400px}
.playground-input{padding:32px;border-right:1px solid var(--border);display:flex;flex-direction:column;gap:20px}
.playground-input label{font-size:.82rem;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:1px}
.playground-input input,.playground-input select{width:100%;padding:12px 16px;background:var(--bg);border:1px solid var(--border);border-radius:8px;color:var(--text);font-size:.9rem;font-family:inherit;transition:border-color .2s}
.playground-input input:focus,.playground-input select:focus{outline:none;border-color:var(--primary)}
.playground-output{padding:32px;background:var(--bg2);display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:320px}
.playground-output img{max-width:100%;max-height:300px;border-radius:8px;box-shadow:0 8px 30px rgba(0,0,0,0.3)}
.playground-output .placeholder{color:var(--muted);font-size:.9rem;text-align:center}
.playground-output .placeholder .icon{font-size:3rem;margin-bottom:12px;opacity:.3}
#playground-loading{display:none;flex-direction:column;align-items:center;gap:12px;color:var(--muted)}
.spinner{width:32px;height:32px;border:3px solid var(--border);border-top-color:var(--primary);border-radius:50%;animation:spin .8s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-top:48px}
.price-card{background:var(--card);border:1px solid var(--border);border-radius:var(--radius-lg);padding:36px 28px;text-align:center;transition:all .3s;position:relative}
.price-card:hover{border-color:var(--border-light);transform:translateY(-2px)}
.price-card.featured{border-color:var(--primary);background:linear-gradient(180deg,rgba(79,143,255,0.05) 0%,var(--card) 100%);box-shadow:0 0 40px rgba(79,143,255,0.1)}
.price-card.featured::before{content:'Most Popular';position:absolute;top:-12px;left:50%;transform:translateX(-50%);background:var(--primary);color:#fff;padding:4px 16px;border-radius:100px;font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:1px}
.price-tier{font-size:.8rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:1.5px;margin-bottom:12px}
.price-amount{font-size:2.8rem;font-weight:900;margin-bottom:4px}
.price-amount .currency{font-size:1.2rem;vertical-align:super;font-weight:600;color:var(--text-secondary)}
.price-amount .period{font-size:.9rem;font-weight:400;color:var(--muted)}
.price-limit{color:var(--text-secondary);font-size:.88rem;margin-bottom:24px;padding-bottom:24px;border-bottom:1px solid var(--border)}
.price-features{list-style:none;text-align:left;margin-bottom:28px}
.price-features li{padding:7px 0;font-size:.85rem;color:var(--text-secondary);display:flex;align-items:center;gap:10px}
.price-features li::before{content:'✓';color:var(--accent);font-weight:700;font-size:.9rem}
.price-card .btn{width:100%;justify-content:center}
.docs-preview{margin-top:48px}
.endpoint-card{background:var(--card);border:1px solid var(--border);border-radius:var(--radius-lg);overflow:hidden;margin-bottom:20px}
.endpoint-header{display:flex;align-items:center;gap:12px;padding:20px 28px;cursor:pointer;transition:background .2s}
.endpoint-header:hover{background:rgba(255,255,255,0.02)}
.endpoint-method{padding:4px 12px;border-radius:6px;font-size:.75rem;font-weight:700;font-family:'JetBrains Mono',monospace;text-transform:uppercase}
.method-post{background:rgba(79,143,255,0.15);color:var(--primary)}
.method-get{background:rgba(16,185,129,0.15);color:var(--accent)}
.endpoint-path{font-family:'JetBrains Mono',monospace;font-size:.9rem;font-weight:500}
.endpoint-desc{color:var(--muted);font-size:.85rem;margin-left:auto}
.faq-grid{max-width:800px;margin:48px auto 0;display:flex;flex-direction:column;gap:16px}
.faq-item{background:var(--card);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden}
.faq-q{padding:20px 24px;font-weight:600;font-size:.95rem;cursor:pointer;display:flex;justify-content:space-between;align-items:center;transition:background .2s}
.faq-q:hover{background:rgba(255,255,255,0.02)}
.faq-q .arrow{transition:transform .3s;color:var(--muted)}
.faq-a{padding:0 24px;max-height:0;overflow:hidden;transition:all .3s;color:var(--text-secondary);font-size:.9rem;line-height:1.7}
.faq-item.open .faq-a{padding:0 24px 20px;max-height:200px}
.faq-item.open .arrow{transform:rotate(180deg)}
.cta-section{text-align:center;padding:80px 0 100px}
.cta-box{background:linear-gradient(135deg,rgba(79,143,255,0.1) 0%,rgba(167,139,250,0.1) 100%);border:1px solid rgba(79,143,255,0.2);border-radius:var(--radius-xl);padding:64px 48px}
.cta-box h2{font-size:2.2rem;font-weight:800;margin-bottom:16px}
.cta-box p{color:var(--text-secondary);font-size:1.1rem;margin-bottom:32px;max-width:500px;margin-left:auto;margin-right:auto}
footer{border-top:1px solid var(--border);padding:48px 24px 32px;background:var(--bg2)}
.footer-grid{max-width:1180px;margin:0 auto;display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:40px;margin-bottom:40px}
.footer-brand h4{font-size:1.1rem;font-weight:800;margin-bottom:12px;display:flex;align-items:center;gap:8px}
.footer-brand p{color:var(--muted);font-size:.85rem;line-height:1.6;max-width:280px}
.footer-col h5{font-size:.8rem;font-weight:700;text-transform:uppercase;letter-spacing:1.5px;color:var(--muted);margin-bottom:16px}
.footer-col a{display:block;color:var(--text-secondary);font-size:.88rem;padding:4px 0;transition:color .2s}
.footer-col a:hover{color:var(--text)}
.footer-bottom{max-width:1180px;margin:0 auto;padding-top:24px;border-top:1px solid var(--border);display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:12px;font-size:.8rem;color:var(--muted)}
@media(max-width:900px){
.hero h1{font-size:2.6rem}
.features-grid{grid-template-columns:repeat(2,1fr)}
.pricing-grid{grid-template-columns:1fr}
.eu-grid{grid-template-columns:1fr}
.steps{grid-template-columns:1fr;gap:24px}
.footer-grid{grid-template-columns:1fr 1fr}
.stats{grid-template-columns:repeat(3,1fr)}
.playground-body{grid-template-columns:1fr}
.playground-input{border-right:none;border-bottom:1px solid var(--border)}
}
@media(max-width:640px){
.hero{padding:60px 0 40px}
.hero h1{font-size:2rem}
.hero p{font-size:1rem}
.section{padding:60px 0}
.section-title{font-size:1.8rem}
.features-grid{grid-template-columns:1fr}
.pricing-grid{grid-template-columns:1fr}
.stats{grid-template-columns:1fr}
.stat{border-right:none;border-bottom:1px solid var(--border)}
.stat:last-child{border-bottom:none}
.nav-links{display:none;flex-direction:column;position:absolute;top:64px;left:0;right:0;background:rgba(10,14,23,0.97);backdrop-filter:blur(20px);border-bottom:1px solid var(--border);padding:16px 24px;gap:16px;z-index:99}
.nav-links.show{display:flex}
.nav-mobile{display:block}
.footer-grid{grid-template-columns:1fr}
.trust-badges{flex-direction:column;align-items:center;gap:12px}
}
</style>
<link rel="canonical" href="https://snapapi.eu/">
<meta property="og:title" content="SnapAPI — Screenshot API for Developers">
<meta property="og:description" content="Convert any URL to a pixel-perfect screenshot with a simple API call. PNG, JPEG, WebP. EU-hosted, GDPR compliant.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://snapapi.eu">
<meta property="og:image" content="https://snapapi.eu/og-image.png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="SnapAPI — Screenshot API for Developers">
<meta name="twitter:description" content="Convert any URL to a pixel-perfect screenshot. EU-hosted, GDPR compliant.">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "SnapAPI",
"description": "Screenshot API for developers. Convert any URL to PNG, JPEG, or WebP.",
"url": "https://snapapi.eu",
"applicationCategory": "DeveloperApplication",
"operatingSystem": "Any",
"offers": [
{"@type": "Offer", "name": "Starter", "price": "9.00", "priceCurrency": "EUR", "description": "1,000 screenshots/month"},
{"@type": "Offer", "name": "Pro", "price": "29.00", "priceCurrency": "EUR", "description": "5,000 screenshots/month"},
{"@type": "Offer", "name": "Business", "price": "79.00", "priceCurrency": "EUR", "description": "25,000 screenshots/month"}
],
"provider": {
"@type": "Organization",
"name": "Cloonar Technologies GmbH",
"url": "https://snapapi.eu",
"address": {"@type": "PostalAddress", "addressLocality": "Wien", "addressCountry": "AT"}
}
}
</script>
</head>
<body>
<nav>
<div class="nav-inner">
<a href="#" class="nav-logo">📸 <span>SnapAPI</span></a>
<div class="nav-links">
<a href="#features">Features</a>
<a href="#playground">Try It Free</a>
<a href="/pricing">Pricing</a>
<a href="#docs">API Docs</a>
<a href="/docs" target="_blank">Swagger</a>
<a href="/usage">Usage</a>
<a href="/compare">Compare</a>
<a href="/guides/quick-start">Quick Start</a>
<a href="/blog">Blog</a>
<a href="#pricing" class="btn btn-primary btn-sm nav-cta">Get API Key</a>
</div>
<button class="nav-mobile" onclick="document.querySelector('.nav-links').classList.toggle('show')" aria-label="Menu"></button>
</div>
</nav>
<section class="hero">
<div class="container">
<div class="hero-badge"><span class="dot"></span> Now live — EU-hosted screenshot API</div>
<h1>URL → Screenshot<br><span class="gradient">in one API call</span></h1>
<p>Render pixel-perfect screenshots of any webpage. PNG, JPEG, or WebP. Try it instantly in our playground — no signup needed. Hosted in Germany, fully GDPR compliant.</p>
<div class="hero-buttons">
<a href="#playground" class="btn btn-primary btn-lg">Try It Free →</a>
<a href="#pricing" class="btn btn-secondary btn-lg">View Pricing</a>
</div>
<div class="trust-badges">
<div class="trust-badge"><span class="icon">🇪🇺</span> EU-Hosted (Germany)</div>
<div class="trust-badge"><span class="icon">🔒</span> GDPR Compliant</div>
<div class="trust-badge"><span class="icon"></span> Sub-second Response</div>
<div class="trust-badge"><span class="icon">🎯</span> No Signup to Try</div>
</div>
<div class="code-preview">
<div class="code-window">
<div class="code-titlebar">
<div class="code-dot"></div><div class="code-dot"></div><div class="code-dot"></div>
<div class="code-tabs">
<button class="code-tab active" onclick="switchCodeTab(this, 'code-curl')">cURL</button>
<button class="code-tab" onclick="switchCodeTab(this, 'code-get')">GET/Embed</button>
<button class="code-tab" onclick="switchCodeTab(this, 'code-node')">Node.js</button>
<button class="code-tab" onclick="switchCodeTab(this, 'code-python')">Python</button>
</div>
<div></div>
</div>
<div class="code-body" id="code-curl">
<span class="cmt"># Take a screenshot of any URL</span>
<span class="kw">curl</span> <span class="flag">-X POST</span> <span class="url">https://snapapi.eu/v1/screenshot</span> \
<span class="flag">-H</span> <span class="str">"Authorization: Bearer YOUR_API_KEY"</span> \
<span class="flag">-H</span> <span class="str">"Content-Type: application/json"</span> \
<span class="flag">-d</span> <span class="str">'{"url":"https://example.com","format":"png"}'</span> \
<span class="flag">-o</span> <span class="str">screenshot.png</span>
</div>
<div class="code-body" id="code-get" style="display:none">
<span class="cmt"># GET request with query parameters</span>
<span class="kw">curl</span> <span class="url">"https://snapapi.eu/v1/screenshot?url=https://example.com&key=YOUR_API_KEY&format=png&width=1920"</span>
<span class="cmt"># Direct image embedding in HTML</span>
&lt;<span class="kw">img</span> <span class="prop">src</span>=<span class="str">"https://snapapi.eu/v1/screenshot?url=https://example.com&key=YOUR_API_KEY"</span>
<span class="prop">alt</span>=<span class="str">"Screenshot of example.com"</span>&gt;
<span class="cmt"># Cached responses (5-min TTL)</span>
<span class="kw">curl</span> <span class="url">"https://snapapi.eu/v1/screenshot?url=https://example.com&key=YOUR_API_KEY"</span>
<span class="cmt"># First request: X-Cache: MISS</span>
<span class="cmt"># Next 5 minutes: X-Cache: HIT</span>
</div>
<div class="code-body" id="code-node" style="display:none">
<span class="cmt">// npm install snapapi</span>
<span class="kw">import</span> { <span class="fn">SnapAPI</span> } <span class="kw">from</span> <span class="str">'snapapi'</span>;
<span class="kw">const</span> snap = <span class="kw">new</span> <span class="fn">SnapAPI</span>(<span class="str">'YOUR_API_KEY'</span>);
<span class="kw">const</span> screenshot = <span class="kw">await</span> snap.<span class="fn">capture</span>(<span class="str">'https://example.com'</span>, {
<span class="prop">format</span>: <span class="str">'png'</span>,
<span class="prop">width</span>: <span class="num">1920</span>,
<span class="prop">fullPage</span>: <span class="kw">true</span>,
});
</div>
<div class="code-body" id="code-python" style="display:none">
<span class="cmt"># pip install snapapi</span>
<span class="kw">from</span> <span class="fn">snapapi</span> <span class="kw">import</span> <span class="fn">SnapAPI</span>
snap = <span class="fn">SnapAPI</span>(<span class="str">"YOUR_API_KEY"</span>)
screenshot = snap.<span class="fn">capture</span>(
<span class="str">"https://example.com"</span>,
<span class="prop">format</span>=<span class="str">"png"</span>,
<span class="prop">width</span>=<span class="num">1920</span>,
<span class="prop">full_page</span>=<span class="kw">True</span>,
)
</div>
</div>
</div>
</div>
</section>
<div class="container">
<div class="stats">
<div class="stat"><div class="number">100%</div><div class="label">EU-Hosted Infrastructure</div></div>
<div class="stat"><div class="number">99.9%</div><div class="label">Uptime Target</div></div>
<div class="stat"><div class="number">&lt;3s</div><div class="label">Average Response Time</div></div>
</div>
</div>
<!-- Playground FIRST - hero demo -->
<section class="section" id="playground">
<div class="container text-center">
<div class="section-label">Try it now</div>
<h2 class="section-title">See it in action — no signup needed</h2>
<p class="section-subtitle">Enter any URL and get a watermarked preview instantly. Like what you see? Get an API key for clean, unwatermarked screenshots.</p>
<div class="playground">
<div class="playground-header">
<h3>📸 API Playground</h3>
<span style="font-size:.8rem;color:var(--muted)">Free demo · 5 requests/hour · watermarked</span>
</div>
<div class="playground-body">
<div class="playground-input">
<div>
<label>URL to capture</label>
<input type="url" id="pg-url" value="https://example.com" placeholder="https://example.com">
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
<div>
<label>Format</label>
<select id="pg-format">
<option value="png">PNG</option>
<option value="jpeg">JPEG</option>
<option value="webp">WebP</option>
</select>
</div>
<div>
<label>Quality</label>
<input type="number" id="pg-quality" value="80" min="1" max="100" title="JPEG/WebP quality (1-100). Ignored for PNG.">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
<div>
<label>Width</label>
<input type="number" id="pg-width" value="1280" min="320" max="1920">
</div>
<div>
<label>Height</label>
<input type="number" id="pg-height" value="800" min="200" max="1080">
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
<div>
<label>Device Scale</label>
<select id="pg-scale">
<option value="1">1x</option>
<option value="2">2x (Retina)</option>
<option value="3">3x</option>
</select>
</div>
<div>
<label>Wait Until</label>
<select id="pg-waituntil">
<option value="domcontentloaded">DOM Ready</option>
<option value="load">Page Load</option>
<option value="networkidle0">Network Idle</option>
<option value="networkidle2">Network Idle 2</option>
</select>
</div>
</div>
<div>
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;text-transform:none;letter-spacing:0;font-size:.9rem">
<input type="checkbox" id="pg-fullpage" style="width:auto;padding:0;accent-color:var(--primary)"> Capture full page
</label>
</div>
<div>
<label>Wait for Selector <span style="font-weight:400;text-transform:none;letter-spacing:0">(optional)</span></label>
<input type="text" id="pg-selector" placeholder="#content, .loaded, img" title="CSS selector to wait for before capturing">
</div>
<button class="btn btn-primary" onclick="runPlayground()" id="pg-btn" style="margin-top:auto">
Take Screenshot →
</button>
<p style="font-size:.75rem;color:var(--muted);text-align:center">Playground screenshots are watermarked. <a href="#pricing">Get an API key</a> for clean output.</p>
</div>
<div class="playground-output">
<div class="placeholder" id="pg-placeholder">
<div class="icon">📷</div>
<p>Your screenshot will appear here</p>
</div>
<div id="playground-loading">
<div class="spinner"></div>
<span>Capturing screenshot...</span>
</div>
<img id="pg-result" style="display:none" alt="Screenshot result">
<p id="pg-error" style="display:none;color:#f87171;font-size:.9rem;text-align:center"></p>
</div>
</div>
</div>
</div>
</section>
<section class="section" id="features">
<div class="container text-center">
<div class="section-label">Features</div>
<h2 class="section-title">Everything you need to capture the web</h2>
<p class="section-subtitle">A powerful API with sensible defaults and full customization. No browser management, no infrastructure headaches.</p>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon blue">🖼️</div>
<h3>Multiple Formats</h3>
<p>PNG, JPEG, or WebP output. Control quality, resolution, and device pixel ratio up to 3x Retina.</p>
</div>
<div class="feature-card">
<div class="feature-icon green"></div>
<h3>Fast & Reliable</h3>
<p>Powered by headless Chromium with a managed browser pool. Auto-recycling ensures consistent performance.</p>
</div>
<div class="feature-card">
<div class="feature-icon purple">🔒</div>
<h3>Secure by Default</h3>
<p>SSRF protection blocks internal IPs and metadata endpoints. Strict input validation and secure defaults built in.</p>
</div>
<div class="feature-card">
<div class="feature-icon orange">📐</div>
<h3>Custom Viewports</h3>
<p>Set width, height, and device scale factor. Emulate any device — mobile, tablet, desktop, or 4K.</p>
</div>
<div class="feature-card">
<div class="feature-icon pink">📸</div>
<h3>Full-Page Capture</h3>
<p>Capture entire scrollable pages or just the visible viewport. Perfect for long-form content and landing pages.</p>
</div>
<div class="feature-card">
<div class="feature-icon cyan">🎯</div>
<h3>Wait for Elements</h3>
<p>Use CSS selectors to wait for specific elements before capturing. Ideal for SPAs and dynamic content.</p>
</div>
<div class="feature-card">
<div class="feature-icon purple"></div>
<h3>Response Caching</h3>
<p>Automatic 5-minute caching for repeat requests. Faster responses and reduced server load. Bypass with cache=false.</p>
</div>
<div class="feature-card">
<div class="feature-icon orange">🔗</div>
<h3>GET Request Support</h3>
<p>Direct image embedding with GET requests. Perfect for &lt;img&gt; tags and markdown. API key via query parameter.</p>
</div>
</div>
</div>
</section>
<section class="section" style="padding-top:0">
<div class="container text-center">
<div class="section-label">How it works</div>
<h2 class="section-title">Three steps to pixel-perfect screenshots</h2>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<h3>Try the Playground</h3>
<p>Test our API instantly — no signup needed. See watermarked results in seconds to evaluate quality.</p>
</div>
<div class="step">
<div class="step-number">2</div>
<h3>Get an API Key</h3>
<p>Choose a plan that fits your needs. Get your API key and start making authenticated requests.</p>
</div>
<div class="step">
<div class="step-number">3</div>
<h3>Clean Screenshots</h3>
<p>API keys deliver unwatermarked, full-resolution screenshots. PNG, JPEG, or WebP — your choice.</p>
</div>
</div>
</div>
</section>
<section class="section eu-section" id="eu">
<div class="container">
<div class="section-label">Privacy & Compliance</div>
<h2 class="section-title">Built for European businesses</h2>
<p class="section-subtitle">Your data never leaves the EU. Full GDPR compliance by design, not as an afterthought.</p>
<div class="eu-grid">
<div class="eu-features">
<div class="eu-feature">
<div class="check"></div>
<div>
<h4>Hosted in Germany</h4>
<p>All servers, storage, and processing happens in German data centers. Zero data transfer outside the EU.</p>
</div>
</div>
<div class="eu-feature">
<div class="check"></div>
<div>
<h4>GDPR Compliant</h4>
<p>Full compliance with EU data protection regulations. No US Cloud Act exposure. Austrian company (Cloonar Technologies GmbH).</p>
</div>
</div>
<div class="eu-feature">
<div class="check"></div>
<div>
<h4>No Third-Party Tracking</h4>
<p>No analytics cookies, no external trackers, no data sharing with third parties. Your API usage stays private.</p>
</div>
</div>
<div class="eu-feature">
<div class="check"></div>
<div>
<h4>Data Processing Agreement</h4>
<p>DPA available on request for business customers. Standard contractual clauses included.</p>
</div>
</div>
</div>
<div class="eu-visual">
<div class="flag">🇪🇺</div>
<h3>100% European</h3>
<p>From code to cloud — everything stays in the EU. Austrian company, German infrastructure, European values.</p>
</div>
</div>
</div>
</section>
<section class="section" id="use-cases">
<div class="container text-center">
<div class="section-label">Use Cases</div>
<h2 class="section-title">Built for real-world workflows</h2>
<p class="section-subtitle">See how developers use SnapAPI to automate screenshots across their stack.</p>
<div class="features-grid" style="grid-template-columns:repeat(3,1fr);margin-top:48px">
<a href="/use-cases/social-media-previews" class="feature-card" style="text-decoration:none;color:inherit">
<div class="feature-icon blue">🖼️</div>
<h3>OG Images & Social Previews</h3>
<p>Generate dynamic Open Graph images and Twitter cards from HTML templates on-the-fly.</p>
</a>
<a href="/use-cases/website-monitoring" class="feature-card" style="text-decoration:none;color:inherit">
<div class="feature-icon green">👁️</div>
<h3>Visual Website Monitoring</h3>
<p>Schedule screenshots to detect layout regressions, broken pages, and visual bugs automatically.</p>
</a>
<a href="/use-cases/pdf-reports" class="feature-card" style="text-decoration:none;color:inherit">
<div class="feature-icon purple">📄</div>
<h3>Reports & Thumbnails</h3>
<p>Create website thumbnails for link previews, directories, dashboards, and email digests.</p>
</a>
</div>
</div>
</section>
<section class="section" id="pricing">
<div class="container text-center">
<div class="section-label">Pricing</div>
<h2 class="section-title">Simple, transparent pricing</h2>
<p class="section-subtitle">Try free in the playground. Pay only when you need clean, unwatermarked screenshots via API.</p>
<div class="pricing-grid">
<div class="price-card">
<div class="price-tier">Starter</div>
<div class="price-amount"><span class="currency"></span>9<span class="period">/mo</span></div>
<div class="price-limit">1,000 screenshots/month</div>
<ul class="price-features">
<li>All output formats (PNG, JPEG, WebP)</li>
<li>Custom viewports & device scale</li>
<li>Full-page capture</li>
<li>No watermark</li>
<li>Email support</li>
</ul>
<button class="btn btn-primary" onclick="checkout('starter')">Get Started</button>
</div>
<div class="price-card featured">
<div class="price-tier">Pro</div>
<div class="price-amount"><span class="currency"></span>29<span class="period">/mo</span></div>
<div class="price-limit">5,000 screenshots/month</div>
<ul class="price-features">
<li>Everything in Starter</li>
<li>Priority rendering</li>
<li>Webhook callbacks</li>
<li>Batch API</li>
<li>Priority support</li>
</ul>
<button class="btn btn-primary" onclick="checkout('pro')">Get Started</button>
</div>
<div class="price-card">
<div class="price-tier">Business</div>
<div class="price-amount"><span class="currency"></span>79<span class="period">/mo</span></div>
<div class="price-limit">25,000 screenshots/month</div>
<ul class="price-features">
<li>Everything in Pro</li>
<li>SLA guarantee</li>
<li>Dedicated support</li>
<li>Custom integrations</li>
<li>DPA included</li>
</ul>
<button class="btn btn-primary" onclick="checkout('business')">Get Started</button>
</div>
</div>
<p style="margin-top:32px;color:var(--text-secondary);font-size:.95rem">
Want to test first? <a href="#playground" style="font-weight:600">Try the playground</a> — free, instant, no signup.
<br>
Lost your API key? <a href="/recovery.html" style="font-weight:600;color:var(--primary)">Recover it here</a>
</p>
</div>
</section>
<section class="section" id="docs" style="padding-top:0">
<div class="container text-center">
<div class="section-label">API Documentation</div>
<h2 class="section-title">Developer-friendly API</h2>
<p class="section-subtitle">RESTful API with JSON request/response. Interactive Swagger docs available at <a href="/docs">/docs</a>.</p>
<div class="docs-preview" style="text-align:left">
<div class="endpoint-card">
<div class="endpoint-header" onclick="this.parentElement.classList.toggle('open')">
<span class="endpoint-method method-post">POST</span>
<span class="endpoint-path">/v1/screenshot</span>
<span class="endpoint-desc">Take a screenshot (requires API key, no watermark)</span>
</div>
</div>
<div class="endpoint-card">
<div class="endpoint-header" onclick="this.parentElement.classList.toggle('open')">
<span class="endpoint-method method-post">POST</span>
<span class="endpoint-path">/v1/playground</span>
<span class="endpoint-desc">Free demo (no auth, watermarked, 5 req/hr)</span>
</div>
</div>
<div class="endpoint-card">
<div class="endpoint-header" onclick="this.parentElement.classList.toggle('open')">
<span class="endpoint-method method-get">GET</span>
<span class="endpoint-path">/health</span>
<span class="endpoint-desc">Health check & browser pool status</span>
</div>
</div>
</div>
<p style="margin-top:24px;font-size:.9rem;color:var(--text-secondary)">
Full interactive documentation with "Try it" available at <a href="/docs" class="btn btn-secondary btn-sm" style="margin-left:8px">Open Swagger Docs →</a>
</p>
</div>
</section>
<section class="section" style="padding-top:0">
<div class="container text-center">
<div class="section-label">FAQ</div>
<h2 class="section-title">Frequently asked questions</h2>
<div class="faq-grid">
<div class="faq-item">
<div class="faq-q" onclick="this.parentElement.classList.toggle('open')">
Where are your servers located?
<span class="arrow"></span>
</div>
<div class="faq-a">All our infrastructure is hosted in Germany (EU). Your data never leaves the European Union. We are operated by Cloonar Technologies GmbH, an Austrian company.</div>
</div>
<div class="faq-item">
<div class="faq-q" onclick="this.parentElement.classList.toggle('open')">
Can I try before buying?
<span class="arrow"></span>
</div>
<div class="faq-a">Yes! Our playground lets you test the API instantly — no signup, no credit card. Screenshots are watermarked in the playground, but you can evaluate quality, speed, and rendering before committing to a paid plan.</div>
</div>
<div class="faq-item">
<div class="faq-q" onclick="this.parentElement.classList.toggle('open')">
What's the difference between playground and paid API?
<span class="arrow"></span>
</div>
<div class="faq-a">The playground is free with 5 requests/hour and adds a visible watermark. Paid API keys give you clean, unwatermarked screenshots with higher limits (1,00025,000/month), full-page capture, custom viewports up to 4K, and priority support.</div>
</div>
<div class="faq-item">
<div class="faq-q" onclick="this.parentElement.classList.toggle('open')">
What formats do you support?
<span class="arrow"></span>
</div>
<div class="faq-a">We support PNG, JPEG, and WebP output formats. You can control quality (for JPEG/WebP), resolution, viewport size, and device pixel ratio up to 3x for Retina displays.</div>
</div>
<div class="faq-item">
<div class="faq-q" onclick="this.parentElement.classList.toggle('open')">
Is SnapAPI GDPR compliant?
<span class="arrow"></span>
</div>
<div class="faq-a">Yes. We are fully GDPR compliant. All data processing happens within the EU. No data is shared with third parties. A Data Processing Agreement (DPA) is available for business customers.</div>
</div>
</div>
</div>
</section>
<section class="cta-section">
<div class="container">
<div class="cta-box">
<h2>Ready to capture the web?</h2>
<p>Try our playground for free — no signup needed. See the quality for yourself, then upgrade when you're ready.</p>
<div style="display:flex;gap:16px;justify-content:center;flex-wrap:wrap">
<a href="#playground" class="btn btn-primary btn-lg">Try Playground Free →</a>
<a href="#pricing" class="btn btn-secondary btn-lg">View Pricing</a>
</div>
</div>
</div>
</section>
<footer>
<div class="footer-grid">
<div class="footer-brand">
<h4>📸 SnapAPI</h4>
<p>The EU-hosted screenshot API for developers. Convert any URL to a pixel-perfect image with a simple API call.</p>
</div>
<div class="footer-col">
<h5>Product</h5>
<a href="#features">Features</a>
<a href="#pricing">Pricing</a>
<a href="#playground">Playground</a>
<a href="/docs">API Docs</a>
<a href="/usage">Check Usage</a>
</div>
<div class="footer-col">
<h5>Developers</h5>
<a href="/docs">Swagger / OpenAPI</a>
<a href="#docs">Quick Start</a>
<a href="/health">Status</a>
<a href="/usage">Usage Dashboard</a>
<a href="/changelog">Changelog</a>
<a href="/blog">Blog</a>
</div>
<div class="footer-col">
<h5>Legal</h5>
<a href="/impressum.html">Impressum</a>
<a href="/privacy.html">Privacy Policy</a>
<a href="/terms.html">Terms of Service</a>
</div>
</div>
<div class="footer-bottom">
<span>© 2026 Cloonar Technologies GmbH · FN 631089y · ATU81280034</span>
<span>Linzer Straße 192/1/2, 1140 Wien, Austria 🇦🇹</span>
<span>EU-hosted 🇪🇺 · All data stays in Europe</span>
</div>
</footer>
<script>
// Code tab switcher
function switchCodeTab(btn, id){
btn.parentElement.querySelectorAll('.code-tab').forEach(function(t){t.classList.remove('active')});
btn.classList.add('active');
btn.closest('.code-window').querySelectorAll('.code-body').forEach(function(b){b.style.display='none'});
document.getElementById(id).style.display='';
}
// Playground - calls /v1/playground (no auth needed)
async function runPlayground(){
var url=document.getElementById('pg-url').value;
var format=document.getElementById('pg-format').value;
var quality=parseInt(document.getElementById('pg-quality').value)||80;
var width=parseInt(document.getElementById('pg-width').value)||1280;
var height=parseInt(document.getElementById('pg-height').value)||800;
var fullPage=document.getElementById('pg-fullpage').checked;
var deviceScale=parseInt(document.getElementById('pg-scale').value)||1;
var waitUntil=document.getElementById('pg-waituntil').value;
var waitForSelector=document.getElementById('pg-selector').value.trim()||undefined;
if(!url){alert('Please enter a URL');return}
var btn=document.getElementById('pg-btn');
var loading=document.getElementById('playground-loading');
var placeholder=document.getElementById('pg-placeholder');
var result=document.getElementById('pg-result');
var error=document.getElementById('pg-error');
btn.disabled=true;btn.textContent='Capturing...';
placeholder.style.display='none';result.style.display='none';error.style.display='none';
loading.style.display='flex';
var body={url:url,format:format,width:width,height:height,fullPage:fullPage,quality:quality,deviceScale:deviceScale,waitUntil:waitUntil};
if(waitForSelector)body.waitForSelector=waitForSelector;
try{
var r=await fetch('/v1/playground',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify(body)
});
if(!r.ok){var d=await r.json().catch(function(){return{}});throw new Error(d.error||'HTTP '+r.status)}
var blob=await r.blob();
result.src=URL.createObjectURL(blob);
result.style.display='block';
}catch(ex){
error.textContent='Error: '+ex.message;error.style.display='block';
}finally{
loading.style.display='none';
btn.disabled=false;btn.textContent='Take Screenshot →';
}
}
// FAQ toggles — handled by inline onclick, no duplicate listeners needed
// Smooth scroll
document.querySelectorAll('a[href^="#"]').forEach(function(a){
a.addEventListener('click',function(e){
var id=a.getAttribute('href');
if(id==='#')return;
var el=document.querySelector(id);
if(el){e.preventDefault();el.scrollIntoView({behavior:'smooth',block:'start'})}
});
});
</script>
<script>
async function checkout(plan) {
try {
const res = await fetch("/v1/billing/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ plan })
});
const data = await res.json();
if (data.url) window.location.href = data.url;
else alert(data.error || "Failed to start checkout");
} catch (e) { alert("Network error"); }
}
</script>
</body>
</html>