IntroductionQuick Start

Display Modes

Choosing a display modeBanner (Default)PopupCustom Display ModeAPI

Integrations

Display Modes

API

Fetch discount data directly from the Evendeals API — client-side or server-side.

API mode gives you full control. Call the Evendeals API directly from your frontend or backend — no script tag needed.

Best for: Server-rendered pricing pages, checkout flows, email personalization, mobile apps, or any scenario where you need discount data outside a browser.

When to use the API vs Custom (CSS)

You want to…Use
Only show discount %, coupon code, country, flag in static textCustom mode — add CSS classes; no API or extra JS.
Set checkout link to include ?coupon=... (or append to existing query string)API — fetch discount, then set element.href (or build the URL) in your code.
Show slashed original price and the discounted price on the cardAPI — parse discountAmount, compute new price, update the DOM.
No-code HTML widget (Swipepages, Webflow embed, etc.) with prices + coupon in buttonsClient API — one scoped block (HTML + CSS + fetch + DOM updates) avoids script-tag timing and builder CSS conflicts.

Rule of thumb: Static display → Custom. Dynamic (URLs, price math) → API. See Choosing a display mode for the full guide.

Two endpoints, one API

EndpointAuthUse case
Client-sideNone (IP auto-detected)Browser-based custom integrations
Server-sideAPI keyBackend, checkout, emails, mobile

Client-side

This is the same endpoint banner.js calls under the hood. You can call it directly for fully custom client-side integrations.

Endpoint

GET https://www.evendeals.com/api/discount?url={pageUrl}

Query parameters

ParameterTypeRequiredDescription
urlstringYesThe full URL of the page the visitor is on

The visitor's IP and country are detected automatically from request headers.

Example

const res = await fetch(
  `https://www.evendeals.com/api/discount?url=${encodeURIComponent(window.location.href)}`
);

if (res.status === 200) {
  const data = await res.json();
  if (data.discount) {
    console.log(`${data.discount.countryName}: ${data.discount.discountAmount} off`);
    console.log(`Use code: ${data.discount.couponCode}`);
  }
}

Response (200)

{
  discount: {
    countryCode: string;        // "IN"
    countryName: string;        // "India"
    discountAmount: string;     // "30%"
    couponCode: string;         // "PPP_IN_30"
  } | null;
  settings: {
    blockVpn: boolean;
    autoRefreshEnabled: boolean;
    showPoweredBy: boolean;
    affiliateLink: string | null;
  };
  modes: Array<{
    mode: "banner_top" | "banner_bottom" | "popup";
    enabled: boolean;
    config: Record<string, unknown>;  // per-mode shape (see note)
  }>;
  timer: {
    enableTimer: boolean;
    timerType: "fixed_deadline" | "evergreen_per_visitor";
    expiresAt: string | null;
    evergreenDurationSeconds: string | null;
    timerConfigVersion: number;
  } | null;
  isVpn: boolean;
  productId: string;
}

modes lists every enabled display mode (top banner, bottom banner, popup) with its own config. Most consumers ignore this — banner.js reads it to render the right surface. Per-mode config shapes live in schema/display-mode.ts (banner vs popup differ).

timer is the offer-level countdown deadline shared across all enabled modes. null when no timer is configured or the org isn't on a paid plan.

Other status codes

StatusMeaning
204No discount available (no matching product, country, or IP)
422Invalid parameters

CORS

Returns Access-Control-Allow-Origin: * — callable from any domain.

When to use this vs banner.js

If you're already loading banner.js (even with showBanner: false), use Custom mode instead — the script already fetches the data for you. Use the client-side API directly only when you don't want the script tag at all.

Tips (client-side)

  • No-code widgets: Put one <style>, one wrapper <div class="your-root">, and one <script> that fetches the API and updates only elements inside that wrapper. Scope all CSS under .your-root so the host page’s styles don’t break your layout.
  • Price math: discountAmount is a string like "30%". Use parseFloat(discountAmount) to compute discounted price and set both the slashed original and the new price in the DOM.
  • Checkout links: Build the URL as baseUrl + (baseUrl.includes('?') ? '&' : '?') + 'coupon=' + encodeURIComponent(couponCode) so it works on file:// and any base URL.

Server-side

Call from your backend with an API key. You provide the visitor's IP — no browser needed.

Endpoint

GET https://www.evendeals.com/api/discount?ip={visitorIp}&productId={productId}

Authentication

Include your API key in the x-api-key header. Generate keys in Dashboard → Settings → API Keys.

x-api-key: your-api-key-here

Query parameters

ParameterTypeRequiredDescription
ipstringYesVisitor's IP address
productIdstringYesYour Evendeals product ID

Examples

cURL

curl "https://www.evendeals.com/api/discount?ip=103.1.168.1&productId=prod_abc123" \
  -H "x-api-key: your-api-key-here"

Node.js / Next.js

const response = await fetch(
  `https://www.evendeals.com/api/discount?ip=${visitorIp}&productId=${productId}`,
  { headers: { "x-api-key": process.env.EVENDEALS_API_KEY! } }
);

if (response.ok) {
  const data = await response.json();
  if (data.discount) {
    // Auto-apply coupon at checkout, show in pricing page, etc.
    console.log(`${data.discount.discountAmount} off: ${data.discount.couponCode}`);
  }
}

Python

import requests

response = requests.get(
    "https://www.evendeals.com/api/discount",
    params={"ip": visitor_ip, "productId": product_id},
    headers={"x-api-key": api_key},
)

if response.ok:
    data = response.json()
    if data.get("discount"):
        print(f"{data['discount']['discountAmount']} off: {data['discount']['couponCode']}")

Response (200)

{
  discount: {
    countryCode: string;
    countryName: string;
    discountAmount: string;
    couponCode: string;
  } | null;
  settings: {
    blockVpn: boolean;
    autoRefreshEnabled: boolean;
    showPoweredBy: boolean;
    affiliateLink: string | null;
  } | null;
  modes: Array<{
    mode: "banner_top" | "banner_bottom" | "popup";
    enabled: boolean;
    config: Record<string, unknown>;  // per-mode shape (see note)
  }>;
  timer: {
    enableTimer: boolean;
    timerType: "fixed_deadline" | "evergreen_per_visitor";
    expiresAt: string | null;
    evergreenDurationSeconds: string | null;
    timerConfigVersion: number;
  } | null;
  isVpn: boolean;
  productId: string;
}

Same modes and timer semantics as the client-side response — see the note above. The server-side endpoint returns a redacted settings projection (the four fields above) for both branches.

Error codes

StatusReason
401Missing or invalid x-api-key header
404Product not found, or country couldn't be determined from IP
422Invalid query parameters

Use cases

  • Server-rendered pricing — show the discounted price before the page loads
  • Checkout flow — auto-apply the coupon code server-side
  • Email campaigns — personalize emails with country-specific discounts
  • Mobile apps — fetch discounts from your backend API

Custom Display Mode

Show discounts in your own UI using CSS classes and the window object.

Thinkific

Install Evendeals on Thinkific, create regional coupon codes, and show country-based offers without adding code manually.

On this page

When to use the API vs Custom (CSS)Two endpoints, one APIClient-sideEndpointQuery parametersExampleResponse (200)Other status codesCORSWhen to use this vs banner.jsTips (client-side)Server-sideEndpointAuthenticationQuery parametersExamplesResponse (200)Error codesUse cases