Miniwall Docs
Web SDK

loadPaywall

Fetch a screen's JSON without showing it, and learn how sources, caching, timeouts and the offline copy work.

loadPaywall resolves a screen source to its JSON and returns it. It draws nothing and sends no analytics events. Use it to read what a screen needs before you show it, for example to list its package IDs and fetch prices for them.

function loadPaywall(source: PaywallSource, init?: RequestInit): Promise<PaywallJson>;
import { configure, loadPaywall, getPackageIds } from '@ui-kit/web';

configure({ apiKey: 'pk_...' });

const paywall = await loadPaywall({ placement: 'upgrade_button' });
const ids = getPackageIds(paywall); // for example ['monthly', 'annual']
const products = await fetchPricesFor(ids); // your own code
ParameterTypeWhat it does
sourcePaywallSourceWhere to get the screen. See below.
initRequestInitExtra options for fetch, used only when source is a URL string.

Sources

type PaywallSource = { placement: string } | { paywallId: string } | PaywallJson | string;
SourceLoadsAnalytics
{ placement: 'key' }The screen the placement shows now. A schedule in force is applied by the server. For an A/B test, the visitor's side is picked. Audiences, the frequency limit and the holdout are not applied by loadPaywall. Recommended. Needs configure({ apiKey }).Yes (when shown with presentPaywall or mountPaywall)
{ paywallId: 'id' }That screen's published version. Needs configure({ apiKey }).Yes
'https://.../paywall.json'JSON from any URL, such as a file you downloaded from the dashboard and host yourself.None
a JSON objectThat JSON as it is.None

A JSON object or URL must have the screen format: default_locale, components_config.base and components_localizations. If components_config.base is missing the call fails with Not a paywall JSON: expected components_config.base. A wrapper object with a paywallData field is unwrapped for you. A missing components_localizations becomes an empty object.

Download a screen's JSON from the dashboard with Download JSON on the Screens page. Because a downloaded file is a copy, it does not change when you publish. A placement does.

Caching and the offline copy

For a placement or a paywall ID the SDK keeps its own copy of the last answer:

  1. It sends the request with the last ETag it saw, so an unchanged screen costs a short 304 answer.
  2. The copy is kept in memory and in localStorage under uikit:v1: plus the request URL.
  3. The browser's own HTTP cache is skipped, so a new publish shows on the next load. The Miniwall CDN caches an answer for about 30 seconds, so a publish reaches your site within about a minute.
  4. If the network fails, the request times out, or the server answers with a 5xx error, the last copy is used. A published screen keeps working offline.
  5. A 4xx answer (a wrong key, an unknown placement, a domain that is not allowed) is an error, even if there is a copy.

timeoutMs (default 8000 ms, set in configure) is how long to wait. After that the saved copy is used. With no copy the call rejects with UIKit: the paywall took too long to load.

A URL source is fetched with fetch(url, { headers: { accept: 'application/json' }, ...init }). It is not cached by the SDK and has no timeout of its own. It fails with Could not load paywall (STATUS) from URL on a non-2xx answer.

The delivery response

For reference, the SDK calls GET {apiUrl}/v1/sdk/placements/KEY?key=pk_...&v=2 (or /v1/sdk/paywalls/ID). The answer contains the published screen and everything the browser needs to decide what to show:

FieldWhat it is
placement, paywallId, versionWhich placement, screen and published version.
paywallThe screen JSON.
packagesThe package IDs your site must price.
experimentA running A/B test: its id, shareB and side B's full delivery. null when none.
rulesThe audiences in order: id, conditions and the screen to show, or null for nothing.
frequencyThe frequency limit, or null.
holdoutThe holdout id and share, or null.
display, triggerThe placement's format and trigger.

Audiences, the frequency limit and the holdout ship as rules and are decided in the browser, so your visitors' attributes never leave the page. See Targeting attributes.

On this page