diff --git a/info/pricing.mdx b/info/pricing.mdx index 34b28f1..ef5254a 100644 --- a/info/pricing.mdx +++ b/info/pricing.mdx @@ -4,14 +4,24 @@ title: "Pricing & Limits" With Kernel, you only pay for what you use and nothing more. You don't pay for idle time thanks to [Standby Mode](/browsers/standby), and you're never charged for proxies. Our goal is to be cost-effective, scalable, and transparent. +## Pricing + +| Browser type | Memory | Price ($/sec) | +| --- | --- | --- | +| Headless | 1 GB | 0.0000166667 | +| Headful | 8 GB | 0.0001333336 | + +### Pricing calculator + +import { PricingCalculator } from '/snippets/calculator.jsx'; + + + ## Managed infrastructure | Feature | Developer (free + usage) | Hobbyist ($30 / mo + usage) | Start-Up ($200 / mo + usage) | Enterprise | | --- | --- | --- | --- | --- | -| Price per gb-second ($) | 0.0000166667 | 0.0000166667 | 0.0000166667 | Custom | -| Headless browsers resource | 1 gb | 1 gb | 1 gb | 1 gb | -| Headful browsers resource | 8 gb | 8 gb | 8 gb | 8 gb | -| Free usage credits / mo | $5 | $10 | $50 | Custom | +| Included credits / mo | $5 | $10 | $50 | Custom | | Extended browser timeouts | ✅ | ✅ | ✅ | ✅ | | Browser live view | ✅ | ✅ | ✅ | ✅ | | Configurable browser viewports | ✅ | ✅ | ✅ | ✅ | @@ -59,9 +69,3 @@ Auth sessions are fast (typically 5-30 seconds each). Kernel monitors session he | Managed auth profiles | N/A | 3 | 20 | Custom | > Note: Limits are org-wide by default unless stated otherwise. `Managed auth profiles` refer to the number of active auth connections that Kernel maintains using your stored [Credentials](/profiles/credentials) or [1Password connection](/integrations/1password). - -## Pricing calculator - -import { PricingCalculator } from '/snippets/calculator.jsx'; - - diff --git a/snippets/calculator.jsx b/snippets/calculator.jsx index 14e15a3..3ce7bd1 100644 --- a/snippets/calculator.jsx +++ b/snippets/calculator.jsx @@ -9,10 +9,30 @@ export const PricingCalculator = () => { } const usagePrices = 0.0000166667 - const [plan, setPlan] = useState('free'); - const [headless, setHeadless] = useState(true); - const [avgSessionLength, setAvgSessionLength] = useState(30); - const [numSessions, setNumSessions] = useState(100); + // Initialize state from URL params + const getInitialState = () => { + if (typeof window === 'undefined') { + return { + plan: 'free', + headless: true, + avgSessionLength: 30, + numSessions: 100 + }; + } + const params = new URLSearchParams(window.location.search); + return { + plan: params.get('plan') || 'free', + headless: params.get('headless') === 'false' ? false : true, + avgSessionLength: parseInt(params.get('length')) || 30, + numSessions: parseInt(params.get('sessions')) || 100 + }; + }; + + const initialState = getInitialState(); + const [plan, setPlan] = useState(initialState.plan); + const [headless, setHeadless] = useState(initialState.headless); + const [avgSessionLength, setAvgSessionLength] = useState(initialState.avgSessionLength); + const [numSessions, setNumSessions] = useState(initialState.numSessions); const [flash, setFlash] = useState(false); const prevPriceRef = useRef(null); @@ -42,6 +62,20 @@ export const PricingCalculator = () => { } prevPriceRef.current = { usageCost, includedUsageCredits, price }; }, [usageCost, includedUsageCredits, price]); + + // Update URL params when state changes + useEffect(() => { + if (typeof window === 'undefined') return; + + const params = new URLSearchParams(); + params.set('plan', plan); + params.set('headless', headless.toString()); + params.set('length', avgSessionLength.toString()); + params.set('sessions', numSessions.toString()); + + const newUrl = `${window.location.pathname}?${params.toString()}`; + window.history.replaceState({}, '', newUrl); + }, [plan, headless, avgSessionLength, numSessions]); const labelStyle = { fontWeight: 600, fontSize: '0.875rem', minWidth: '10rem', flexShrink: 0, maxWidth: '10rem' }; const rowStyle = { display: 'flex', alignItems: 'center', gap: '0.5rem', minHeight: '2.25rem' }; const inputStyle = { minWidth: 0, flex: 1, maxWidth: '100%', boxSizing: 'border-box', background: 'transparent' };