Listings embeddable

Embeddable Listing Visibility Widget

Add a business listing scan to a customer-facing page. The host page passes a publishable widget key, theme, and settings; the widget handles listing search, scan creation, lead capture, and report display.

Give these docs to your AI agent

Copy or download a clean markdown version of the Listing Visibility embed docs.

You are viewing the Listing Visibility Embeddable docs. For the main Ceyo platform API, see Main Ceyo Platform API. To try the public tool, see AI-Ready Business Listing.
Required setup. Pass a publicKey and set settings.product = "listing_visibility" when mounting the widget.

How it works

PartWhere it runsWhat it does
Loaderyour pageCreates and manages the iframe, then passes the widget key, theme, and settings to it.
Widget keybrowserPublishable key that identifies the workspace and allowed origins for this widget.
Widget appiframeRenders the Listing Visibility scan flow and report.
Host pageyour siteControls placement, height, theme, copy, and callbacks.

Quick start

  1. Create a widget key in Ceyo and add the domains where the widget can run.

  2. Add a container where the widget should appear.

  3. Include the loader once on the page.

  4. Call Ceyo.mount() with publicKey and settings.product = "listing_visibility".

HTML
<div id="ceyo-listing-visibility"></div>

<script src="https://cdn.ceyo.ai/embed/v1.js"></script>
<script>
  const embed = Ceyo.mount("#ceyo-listing-visibility", {
    publicKey: "ceyo_lpk_...",
    height: "760px",
    title: "AI Visibility Scan",
    settings: {
      product: "listing_visibility",
      title: "AI Visibility Scan",
      subtitle: "See how your business listing appears across search and answer engines."
    }
  });
</script>

Widget key

Create a Listing Visibility widget key in Ceyo. The key is publishable and is designed to be used in browser code.

ValueMeaning
publicKeyThe publishable key passed to Ceyo.mount().
allowed_originsOrigins where the widget is allowed to run.
enabledDisable the key to stop new sessions for that widget.

Allowed origins

Allowed origins must be exact origins: scheme, host, and optional port. Do not include paths, query strings, or fragments.

InputValid?Notes
https://example.comYesProduction domain.
https://app.example.comYesSubdomains are separate origins.
http://localhost:3000YesUseful while building your integration locally.
https://example.com/pageNoRemove the path.

Install the loader

Include the loader script once per page:

Production loader
<script src="https://cdn.ceyo.ai/embed/v1.js"></script>

The loader registers a single global, Ceyo, and only runs when you call Ceyo.mount().


Mount the widget

Ceyo.mount(target, options) renders the widget into a container element. The iframe fills the container width; you control the height.

JavaScript
const embed = Ceyo.mount("#ceyo-listing-visibility", {
  publicKey: "ceyo_lpk_...",
  height: "760px",
  minWidth: "320px",
  title: "AI Visibility Scan",
  settings: {
    product: "listing_visibility",
    title: "AI Visibility Scan",
    subtitle: "See how your business listing appears across search and answer engines."
  },
  theme: {
    colors: {
      primary: "#0F766E",
      primaryText: "#FFFFFF",
      background: "transparent",
      surface: "#FFFFFF",
      text: "#16181D",
      muted: "#6B7280",
      border: "#E5E7EB",
      warning: "#B45309",
      warningSurface: "#FFFBEB"
    },
    shape: {
      radius: "10px",
      cardRadius: "12px",
      controlRadius: "10px"
    },
    density: "compact"
  },
  onReady: () => console.log("listing widget ready"),
  onError: (message) => console.error("listing widget error:", message)
});

Mount options

NameTypeRequiredDescription
publicKeystringYesPublishable widget key from Ceyo.
settings.productstringYeslisting_visibility. This tells the shared loader to open the Listing Visibility scan widget.
heightstringNoIframe height. Defaults to 720px. Width is always 100%.
minWidthstringNoMinimum iframe width. Defaults to 360px. Use 320px for narrow mobile containers.
titlestringNoAccessible title for the iframe element. Defaults to Visibility.
themeobjectNoVisual overrides for colors, typography, shape, spacing, and density.
settingsobjectNoWidget copy and product settings.
onReadyfunctionNoCalled once after the widget has booted.
onErrorfunctionNoCalled with a message when the widget reports an error.

Widget handle

Ceyo.mount() returns a handle:

MemberTypeDescription
iframeHTMLIFrameElementThe managed iframe element.
unmount()functionRemove the widget and all event listeners.

Full example

Full HTML example
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Listing Visibility Scan</title>
  </head>
  <body>
    <main>
      <h1>Check your AI visibility</h1>
      <p>Find your business listing and run a visibility scan.</p>
      <div id="ceyo-listing-visibility"></div>
    </main>

    <script src="https://cdn.ceyo.ai/embed/v1.js"></script>
    <script>
      const embed = Ceyo.mount("#ceyo-listing-visibility", {
        publicKey: "ceyo_lpk_...",
        height: "760px",
        minWidth: "320px",
        title: "AI Visibility Scan",
        settings: {
          product: "listing_visibility",
          title: "AI Visibility Scan",
          subtitle: "See how your business listing appears across search and answer engines."
        },
        theme: {
          colors: {
            primary: "#0F766E",
            primaryText: "#FFFFFF"
          }
        },
        onReady: () => {
          console.log("Listing widget ready");
        },
        onError: (message) => {
          console.error("Listing widget error:", message);
        }
      });

      // Later, if needed:
      // embed.unmount();
    </script>
  </body>
</html>

Settings

Listing Visibility uses a small settings object. product is required; the rest controls visible copy.

NameTypeRequiredDescription
productstringYeslisting_visibility.
titlestringNoWidget heading. Defaults to AI Visibility.
subtitlestringNoShort intro text below the heading.

Theme

Theme overrides are plain tokens. Anything you do not set keeps its default.

Theme options
theme: {
  colors: {
    primary: "#0F766E",
    primaryText: "#FFFFFF",
    background: "transparent",
    surface: "#FFFFFF",
    text: "#16181D",
    muted: "#6B7280",
    border: "#E5E7EB",
    success: "#047857",
    successSurface: "#ECFDF5",
    warning: "#B45309",
    warningSurface: "#FFFBEB",
    danger: "#B91C1C",
    dangerSurface: "#FEF2F2"
  },
  typography: {
    fontFamily: "'Inter', sans-serif",
    baseSize: "14px",
    smallSize: "12.5px",
    titleSize: "22px",
    titleWeight: "700",
    bodyWeight: "400"
  },
  shape: {
    radius: "10px",
    cardRadius: "10px",
    controlRadius: "10px",
    pillRadius: "7px"
  },
  spacing: {
    pagePadding: "20px",
    panelPadding: "16px",
    cardPadding: "16px 18px",
    gap: "12px",
    controlHeight: "34px"
  },
  density: "compact"
}

colors

TokenDefaultDescription
primary#16181dButtons, active states, and accents.
primaryText#ffffffText on primary surfaces.
backgroundtransparentWidget page background.
surface#ffffffCards and panels.
text#16181dPrimary text.
muted#6b7280Secondary text.
border#e5e7ebBorders and dividers.
success / successSurface#047857 / #ecfdf5Positive indicators.
warning / warningSurface#b45309 / #fffbebWarning indicators, star/rating accents, and mid-range score states.
danger / dangerSurface#b91c1c / #fef2f2Negative indicators.

typography

TokenDefaultDescription
fontFamilysystem stackSet this to match your product font.
baseSize14pxBody text size.
smallSize12.5pxSecondary text size.
titleSize22pxWidget title size.
titleWeight / bodyWeight700 / 400Font weights.

shape & density

TokenDefaultDescription
shape.radius10pxBase border radius.
shape.cardRadius10pxCard radius.
shape.controlRadius10pxInputs and buttons.
shape.pillRadius7pxBadges and pills.
densitycompactcompact or comfortable. Adjusts paddings and control heights as a set.

spacing

TokenDefaultDescription
pagePadding20pxOuter page padding.
panelPadding16pxPanel padding.
cardPadding16px 18pxCard padding.
gap12pxDefault layout gap.
controlHeight34pxInput and button height.

Events

CallbackWhen it runs
onReady()The iframe has booted and received its initial configuration.
onError(message)The widget reports a setup or runtime error.

Limits

LimitWhat happens
Daily per-IP scan limitThe widget can stop additional scans from the same IP for the day and show a retry message.
Monthly workspace scan limitIf the workspace reaches its monthly scan allowance, the widget shows a scan limit reached state.
Allowed originsIf the page origin is not allowed for the widget key, setup fails and onError is called.

Troubleshooting

SymptomCause & fix
Widget does not loadCheck that the loader script is present, the mount target exists, and publicKey is set.
Origin is not allowedAdd the exact page origin to the widget key allowed origins.
Scan limit reachedThe workspace reached its monthly scan allowance.
Too many scansThe visitor reached the widget scan limit for their IP. Try again later.
Widget is too wide on mobilePass minWidth: "320px" or give the host container enough width.